├── test
├── fail
│ ├── empty.md
│ ├── hash.md
│ ├── img2.md
│ ├── missing1.md
│ ├── img1.md
│ ├── missing2.md
│ ├── nohash.md
│ ├── warn-backticks.md
│ ├── broken_json.md
│ ├── broken_yaml.md
│ └── dupe1.md
└── pass
│ ├── warn-backticks.md
│ ├── external.md
│ ├── dupe2.md
│ ├── api-blueprint.md
│ └── asyncapi.md
├── .editorconfig
├── .eslintrc.js
├── .github
├── dependabot.yml
└── workflows
│ └── pr-testing.yml
├── .gitignore
├── package.json
├── LICENSE
├── render.js
├── mdv.js
├── README.md
├── parseExamples.js
├── testRunner.js
├── index.js
└── .eslintrc.json
/test/fail/empty.md:
--------------------------------------------------------------------------------
1 | # link
2 | [](#link)
3 |
--------------------------------------------------------------------------------
/test/fail/hash.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/fail/img2.md:
--------------------------------------------------------------------------------
1 | 
2 |
--------------------------------------------------------------------------------
/test/fail/missing1.md:
--------------------------------------------------------------------------------
1 | [link](#not-there)
2 |
--------------------------------------------------------------------------------
/test/fail/img1.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/test/fail/missing2.md:
--------------------------------------------------------------------------------
1 | link
2 |
--------------------------------------------------------------------------------
/test/fail/nohash.md:
--------------------------------------------------------------------------------
1 | # heading
2 |
3 | [link](heading)
4 |
--------------------------------------------------------------------------------
/test/fail/warn-backticks.md:
--------------------------------------------------------------------------------
1 | ````json
2 | {}
3 | ```
4 |
--------------------------------------------------------------------------------
/test/pass/warn-backticks.md:
--------------------------------------------------------------------------------
1 | ```yaml
2 | ok: true
3 | ```
4 |
--------------------------------------------------------------------------------
/test/pass/external.md:
--------------------------------------------------------------------------------
1 | [link1](external.md)
2 |
3 | [link2](external.html)
4 |
--------------------------------------------------------------------------------
/test/fail/broken_json.md:
--------------------------------------------------------------------------------
1 | ```json
2 | {
3 | "test": {
4 | ]
5 | }
6 | ```
7 |
--------------------------------------------------------------------------------
/test/fail/broken_yaml.md:
--------------------------------------------------------------------------------
1 | ```yaml
2 | test:
3 | string: value
4 | nested: wrong
5 | ```
6 |
--------------------------------------------------------------------------------
/test/pass/dupe2.md:
--------------------------------------------------------------------------------
1 | # Heading
2 |
3 | # Heading
4 |
5 | [link](#heading)
6 | [link](#heading-1)
7 |
--------------------------------------------------------------------------------
/test/fail/dupe1.md:
--------------------------------------------------------------------------------
1 | ### Test
2 |
3 | Object1
4 | Object1
5 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | indent_style = space
6 | indent_size = 4
7 | charset = utf-8
8 | trim_trailing_whitespace = true
9 | insert_final_newline = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | module.exports = {
3 | "extends": './.eslintrc.json',
4 | "rules": {
5 | // override default options
6 | 'linebreak-style': ['error', process.platform === 'win32' ? 'windows' : 'unix']
7 | }
8 | };
9 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: npm
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | ignore:
9 | - dependency-name: yargs
10 | versions:
11 | - "> 14.0.0"
12 | - dependency-name: open
13 | versions:
14 | - 7.3.1
15 | - 7.4.1
16 | - 7.4.2
17 | - 8.0.1
18 | - 8.0.2
19 | - 8.0.3
20 | - 8.0.4
21 | - 8.0.5
22 | - 8.0.6
23 | - dependency-name: y18n
24 | versions:
25 | - 4.0.1
26 | - 4.0.2
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | markdown.html
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 |
13 | # Directory for instrumented libs generated by jscoverage/JSCover
14 | lib-cov
15 |
16 | # Coverage directory used by tools like istanbul
17 | coverage
18 |
19 | # nyc test coverage
20 | .nyc_output
21 |
22 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
23 | .grunt
24 |
25 | # node-waf configuration
26 | .lock-wscript
27 |
28 | # Compiled binary addons (http://nodejs.org/api/addons.html)
29 | build/Release
30 |
31 | # Dependency directories
32 | node_modules
33 | jspm_packages
34 |
35 | # Optional npm cache directory
36 | .npm
37 |
38 | # Optional REPL history
39 | .node_repl_history
40 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mdv",
3 | "version": "1.3.4",
4 | "description": "Markdown Validator",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "node testRunner -f test/fail test/pass",
8 | "lint": "npx eslint *.js",
9 | "lint:fix": "npx eslint *.js --fix"
10 | },
11 | "bin": {
12 | "mdv": "./mdv.js"
13 | },
14 | "keywords": [
15 | "markdown",
16 | "validator",
17 | "validation",
18 | "link",
19 | "links",
20 | "image",
21 | "images",
22 | "a11y",
23 | "accessibility"
24 | ],
25 | "repository": {
26 | "type": "git",
27 | "url": "git+https://github.com/Mermade/mdv.git"
28 | },
29 | "author": "Mike Ralphson ",
30 | "license": "BSD-3-Clause",
31 | "dependencies": {
32 | "abnf": "0.0.5",
33 | "cheerio": "^1.0.0-rc.12",
34 | "glob": "^7.1.6",
35 | "jgexml": "^0.4.2",
36 | "markdown-it": "^13.0.2",
37 | "open": "^8.2.1",
38 | "rich-text-diff": "^0.2.3",
39 | "yaml": "^2.3.3",
40 | "yargs": "^17.7.2"
41 | },
42 | "devDependencies": {
43 | "node-readfiles": "^0.2.0"
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/.github/workflows/pr-testing.yml:
--------------------------------------------------------------------------------
1 | name: PR testing
2 |
3 | on:
4 | pull_request:
5 | types: [opened, reopened, synchronize, ready_for_review]
6 |
7 | jobs:
8 | test:
9 | if: github.event.pull_request.draft == false #to not run the check if the PR is a draft
10 | name: Testing on ${{ matrix.os }} using Node ${{ matrix.node }}
11 | runs-on: ${{ matrix.os }}
12 | strategy:
13 | matrix:
14 | os: [ubuntu-latest, macos-latest, windows-latest]
15 | node: [ '10', '14', '15' ] #for now versions must be hardcoded as aliases like lts or latest are not yet supported by setup-node action https://github.com/actions/setup-node/issues/26
16 | steps:
17 | - name: Checkout repository
18 | uses: actions/checkout@v2
19 | - name: Setup Node.js ${{ matrix.node }}
20 | uses: actions/setup-node@v1
21 | with:
22 | node-version: ${{ matrix.node }}
23 | - name: Audit package-lock.json
24 | run: npx package-lock-audit ./package-lock.json
25 | - name: Install dependencies
26 | run: npm install
27 | - name: Run test
28 | run: npm test
29 | - name: Run linter
30 | run: npm run lint
31 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2017, Mermade Software
4 | All rights reserved.
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions are met:
8 |
9 | * Redistributions of source code must retain the above copyright notice, this
10 | list of conditions and the following disclaimer.
11 |
12 | * Redistributions in binary form must reproduce the above copyright notice,
13 | this list of conditions and the following disclaimer in the documentation
14 | and/or other materials provided with the distribution.
15 |
16 | * Neither the name of the copyright holder nor the names of its
17 | contributors may be used to endorse or promote products derived from
18 | this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 |
--------------------------------------------------------------------------------
/render.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // @ts-check
4 | 'use strict';
5 |
6 | const fs = require('fs');
7 | const url = require('url');
8 | const markdown = require('markdown-it')({linkify: true, html: true});
9 | const open = require('open');
10 | const rtd = require('rich-text-diff');
11 |
12 | const colour = process.env.NODE_DISABLE_COLORS ?
13 | { red: '', yellow: '', green: '', normal: '' } :
14 | { red: '\x1b[31m', yellow: '\x1b[33;1m', green: '\x1b[32m', normal: '\x1b[0m' };
15 |
16 | function processConflicts(md) {
17 | let lines = md.split('\r').join('').split('\n');
18 | for (let i=0;i>>>>>')) {
30 | after = after + lines[i]+'\n';
31 | i++;
32 | }
33 | let diff = rtd(before,after);
34 |
35 | diff = diff.split('').join(colour.green);
36 | diff = diff.split('').join(colour.normal);
37 | diff = diff.split('').join(colour.red);
38 | diff = diff.split('').join(colour.normal+' ');
39 |
40 | const diffLines = diff.split('\n');
41 | lines.splice(start,i-start,...diffLines);
42 | console.log(diff);
43 | i = start;
44 | }
45 | }
46 | return lines.join('\n');
47 | }
48 |
49 | if (process.argv.length > 2) {
50 | const outfile = process.argv.length > 3 ? process.argv[3] : './markdown.html';
51 | let md = fs.readFileSync(process.argv[2],'utf8');
52 | let output;
53 | if (md.indexOf('<<<<<<') >= 0) {
54 | output = processConflicts(md);
55 | }
56 | output = markdown.render(md);
57 | fs.writeFileSync(outfile,output,'utf8');
58 | open(url.pathToFileURL(outfile).toString())
59 | .catch(function(ex){
60 | console.warn(ex.message);
61 | });
62 | }
63 |
--------------------------------------------------------------------------------
/mdv.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // @ts-check
4 | 'use strict';
5 |
6 | const fs = require('fs');
7 | const util = require('util');
8 | const yaml = require('yaml');
9 | const glob = require("glob");
10 |
11 | const validator = require('./index.js');
12 |
13 | const argv = require('yargs')
14 | .boolean('debug')
15 | .alias('d','debug')
16 | .describe('debug','enable debug mode')
17 | .help('help')
18 | .alias('h','help')
19 | .string('outfile')
20 | .alias('o','outfile')
21 | .boolean('save')
22 | .alias('s','save')
23 | .describe('save','save intermediary html output')
24 | .boolean('warnings')
25 | .alias('w','warnings')
26 | .describe('warnings','enable warnings')
27 | .boolean('yaml')
28 | .alias('y','yaml')
29 | .describe('yaml','output in YAML not JSON')
30 | .require(1)
31 | .strict()
32 | .argv;
33 |
34 | let exitCode = 0;
35 | const options = argv;
36 |
37 | for (let a of argv._) {
38 | const files = glob.sync(a, { debug: !!options.debug });
39 | for (let file of files) {
40 | const s = fs.readFileSync(file,'utf8');
41 | options.source = file;
42 | const result = validator.validate(s,options);
43 |
44 | if (options.save) {
45 | fs.writeFileSync(file+'.html',options.html,'utf8');
46 | delete options.html;
47 | }
48 |
49 | let ok = true;
50 | for (let p in result) {
51 | if (typeof result[p] == 'number') {
52 | if (result[p]) ok = false
53 | else delete result[p];
54 | }
55 | else if (Array.isArray(result[p])) {
56 | if (result[p].length) ok = false
57 | else delete result[p];
58 | }
59 | else if (typeof result[p] == 'object') {
60 | if (Object.keys(result[p]).length) ok = false
61 | else delete result[p];
62 | }
63 | }
64 | if (!ok) {
65 | if (argv.yaml) {
66 | console.log(yaml.stringify(result));
67 | }
68 | else {
69 | console.log(util.inspect(result,{depth:null}));
70 | }
71 | exitCode = 1;
72 | }
73 | }
74 | }
75 |
76 |
77 | process.exit(exitCode);
78 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mdv
2 |
3 | [](https://travis-ci.org/Mermade/mdv)
4 |
5 | A tiny markdown validator. It understands [GFM auto-links](https://gist.github.com/asabaylus/3071099), and returns an exitCode of 1 if any rules are violated, making it suitable for use in CI environments. It is in use by the [OpenAPI Specification](https://github.com/OAI/OpenAPI-Specification), the [RAML Specification](https://github.com/raml-org/raml-spec) and the [AsyncAPI Specification](https://github.com/asyncapi/asyncapi), amongst others.
6 |
7 | ## Errors
8 |
9 | * Undefined internal link targets - `missingAnchors[]`
10 | * Duplicated internal link targets - `duplicatedAnchors[]`
11 | * Anchors containing the # character - `anchorsWithHash[]`
12 | * Links with empty text - `anchorsWithEmptyText[]`
13 | * Local Refs without # character - `localRefNoHash[]`
14 | * Images without an `alt` tag - `imagesWithMissingAlt[]`
15 | * `yaml`, `json`, `xml` or `abnf` examples which do not parse - `nonParsingExamples[]`
16 |
17 | ## Warnings
18 |
19 | * Internal manually-defined anchors with no links pointing to them
20 | * Code-blocks with no language specified - `codeBlocksWithNoLanguage`
21 | * Four or more leading backticks at the beginning of a line
22 |
23 | ## Usage
24 |
25 | ```
26 | Options:
27 | -d, --debug enable debug mode [boolean]
28 | -s, --save save intermediary html [boolean]
29 | -w, --warnings enable warnings [boolean]
30 | -h, --help Show help [boolean]
31 | ```
32 |
33 | ### API
34 |
35 | ```javascript
36 | const mdv = require('mdv');
37 | const options = {};
38 | const result = mdv.validate(markdownString,options);
39 | ```
40 |
41 | ### Example output
42 |
43 | ```javascript
44 | { imagesWithMissingAlt: 0,
45 | source: '../openapi-specification/versions/2.0.md',
46 | missingAnchors:
47 | [ { name: 'dataTypeType', defined: 0, seen: 1 },
48 | { name: 'stType', defined: 0, seen: 2 },
49 | { name: 'securityDefinitions', defined: 0, seen: 1 } ],
50 | duplicatedAnchors:
51 | [ { name: 'itemsMaximum', defined: 2, seen: 0, auto: false },
52 | { name: 'headerMaximum', defined: 2, seen: 0, auto: false } ],
53 | anchorsWithHash: [],
54 | anchorsWithEmptyText: [],
55 | codeBlocksWithNoLanguage: 1 }
56 | ```
57 |
58 |
--------------------------------------------------------------------------------
/parseExamples.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const fs = require('fs');
4 | const yaml = require('yaml');
5 | const xml2json = require('jgexml/xml2json').xml2json;
6 | const abnf = require('abnf');
7 |
8 | function parseExamples(s,options) {
9 |
10 | var result = [];
11 |
12 | let lines = s.split('\r').join('').split('\n');
13 | let counter = 1;
14 | let inFence = false;
15 | let extension = 'txt';
16 | let example = '';
17 | let lineStart = 1;
18 | for (let lineNo in lines) {
19 | let line = lines[lineNo];
20 | if (line.startsWith('```')) {
21 | if (inFence) {
22 | if ((extension === 'json') || (extension === 'yaml')) {
23 | if ((extension === 'json') && (example.startsWith('"'))) {
24 | example = '{'+example+'}';
25 | }
26 | if (extension === 'yaml') {
27 | example = example.split('!include').join('#include');
28 | }
29 | var obj = {};
30 | try {
31 | obj = yaml.parse(example,{prettyErrors:true});
32 | if (extension === 'yaml') {
33 | example = yaml.stringify(obj);
34 | }
35 | else {
36 | example = JSON.stringify(obj,null,2);
37 | }
38 | if (options.debug) console.log(example);
39 | }
40 | catch (ex) {
41 | let entry = {};
42 | entry.lineStart = parseInt(lineStart,10);
43 | entry.lineEnd = parseInt(lineNo,10);
44 | entry.extension = extension;
45 | entry.message = ex.message;
46 | result.push(entry);
47 | return result;
48 | }
49 | }
50 |
51 | if (extension === 'xml') {
52 | let obj = xml2json(example);
53 | if (options.debug) console.log(JSON.stringify(obj));
54 | if (Object.keys(obj).length !== 1) {
55 | let entry = {};
56 | entry.lineStart = parseInt(lineStart,10);
57 | entry.lineEnd = parseInt(lineNo,10);
58 | entry.extension = extension;
59 | entry.message = 'No root element found';
60 | result.push(entry);
61 | return result;
62 | }
63 | }
64 |
65 | if (extension === 'abnf') {
66 | abnf.Parse(example,function(err,rules){
67 | if (err) {
68 | let entry = {};
69 | entry.lineStart = parseInt(lineStart,10);
70 | entry.lineEnd = parseInt(lineNo,10);
71 | entry.extension = extension;
72 | entry.message = err;
73 | result.push(entry);
74 | return result;
75 | }
76 | });
77 | }
78 |
79 | example = '';
80 | counter++;
81 | extension = 'txt';
82 | inFence = false;
83 | }
84 | else {
85 | inFence = true;
86 | lineStart = lineNo;
87 | extension = line.split('`').pop().trim().toLowerCase();
88 | if (extension === 'yml') extension = 'yaml';
89 | if (!extension) extension = 'txt';
90 | }
91 | }
92 | else if (inFence) {
93 | example += line + '\n';
94 | }
95 | }
96 | return result;
97 | }
98 |
99 | module.exports = {
100 | parseExamples : parseExamples
101 | };
102 |
--------------------------------------------------------------------------------
/testRunner.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | // @ts-check
4 | 'use strict';
5 |
6 | var fs = require('fs');
7 | var path = require('path');
8 | var util = require('util');
9 | var readfiles = require('node-readfiles');
10 | var mdv = require('./index.js');
11 |
12 | var argv = require('yargs')
13 | .usage('testRunner [options] [{path-to-specs}...]')
14 | .demand(1)
15 | .boolean('debug')
16 | .alias('d','debug')
17 | .describe('debug','pass debug flag to mdv')
18 | .string('encoding')
19 | .alias('e','encoding')
20 | .default('encoding','utf8')
21 | .describe('encoding','encoding for input/output files')
22 | .string('fail')
23 | .describe('fail','path to specs expected to fail')
24 | .alias('f','fail')
25 | .boolean('quiet')
26 | .alias('q','quiet')
27 | .describe('quiet','do not show test passes on console, for CI')
28 | .boolean('stop')
29 | .alias('s','stop')
30 | .describe('stop','stop on first error')
31 | .count('verbose')
32 | .alias('v','verbose')
33 | .describe('verbose','increase verbosity')
34 | .help('h')
35 | .alias('h', 'help')
36 | .strict()
37 | .version()
38 | .argv;
39 |
40 | var red = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[31m';
41 | var green = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[32m';
42 | var yellow = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[33;1m';
43 | var normal = process.env.NODE_DISABLE_COLORS ? '' : '\x1b[0m';
44 |
45 | var pass = 0;
46 | var fail = 0;
47 | var failures = [];
48 | var warnings = [];
49 |
50 | var options = argv;
51 |
52 | function checkOutput(output) {
53 | var result = true;
54 | for (var k in output) {
55 | if (typeof output[k] === 'number') {
56 | if (output[k] > 0) result = false;
57 | }
58 | if (Array.isArray(output[k])) {
59 | if (output[k].length > 0) result = false;
60 | }
61 | }
62 | return result;
63 | }
64 |
65 | function check(file,force,expectFailure) {
66 | let result = false;
67 | options.expectFailure = expectFailure;
68 | options.source = file;
69 |
70 | let components = file.split(path.sep);
71 | let name = components[components.length-1];
72 |
73 | if ((name.endsWith('.md')) || force) {
74 |
75 | options.warnings = name.startsWith('warn-');
76 |
77 | var srcStr = fs.readFileSync(path.resolve(file),options.encoding);
78 | var output = {failed:true};
79 | result = false;
80 | try {
81 | output = mdv.validate(srcStr,options);
82 | result = checkOutput(output);
83 | }
84 | catch (ex) {
85 | console.log(red+'mdv threw an error: '+ex.message);
86 | warnings.push('mdv threw '+options.source);
87 | result = false;
88 | }
89 |
90 | console.log((result ? green : red)+file);
91 | if (!result) {
92 | console.log(util.inspect(output));
93 | }
94 | if (expectFailure) result = !result;
95 | if (result) {
96 | pass++;
97 | }
98 | else {
99 | fail++;
100 | }
101 |
102 | }
103 | else {
104 | result = true;
105 | }
106 | return result;
107 | }
108 |
109 | function processPathSpec(pathspec,expectFailure) {
110 | pathspec = path.resolve(pathspec);
111 | var stats = fs.statSync(pathspec);
112 | if (stats.isFile()) {
113 | check(pathspec,true,expectFailure);
114 | }
115 | else {
116 | readfiles(pathspec, {readContents: false, filenameFormat: readfiles.FULL_PATH}, function (err) {
117 | if (err) console.log(util.inspect(err));
118 | })
119 | .then(files => {
120 | files = files.sort(function(a,b){
121 | if (ab) return -1;
123 | return 0;
124 | });
125 | for (var file of files) {
126 | check(file,false,expectFailure);
127 | }
128 | })
129 | .catch(err => {
130 | console.log(util.inspect(err));
131 | });
132 | }
133 | }
134 |
135 | process.exitCode = 1;
136 | console.log('Gathering...');
137 | if ((!argv._.length) && (!argv.fail)) {
138 | argv._.push('test/pass/');
139 | }
140 | for (let pathspec of argv._) {
141 | processPathSpec(pathspec,false);
142 | }
143 | if (argv.fail) {
144 | if (!Array.isArray(argv.fail)) argv.fail = [argv.fail];
145 | for (let pathspec of argv.fail) {
146 | processPathSpec(pathspec,true);
147 | }
148 | }
149 |
150 | process.on('exit', function() {
151 | if (warnings.length) {
152 | warnings.sort();
153 | console.log(normal+'\nWarnings:'+yellow);
154 | for (var w in warnings) {
155 | console.log(warnings[w]);
156 | }
157 | }
158 | if (failures.length) {
159 | failures.sort();
160 | console.log(normal+'\nFailures:'+red);
161 | for (var f in failures) {
162 | console.log(failures[f]);
163 | }
164 | }
165 | console.log(normal);
166 | console.log('Tests: %s passing, %s failing, %s warnings', pass, fail, warnings.length);
167 | process.exitCode = ((fail === 0) && (pass > 0)) ? 0 : 1;
168 | });
169 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | var url = require('url');
3 | var util = require('util');
4 |
5 | var md = require('markdown-it')({linkify: true, html: true});
6 | //.use(require('markdown-it-lazy-headers'));
7 |
8 | var cheerio = require('cheerio');
9 |
10 | var exampleValidator = require('./parseExamples.js');
11 |
12 | function indexAnyOf(s,a) {
13 | for (let comp of a) {
14 | let tmp = s.indexOf(comp);
15 | if (tmp >= 0) return tmp;
16 | }
17 | return -1;
18 | }
19 |
20 | function gfmLink(text) {
21 | text = text.trim().toLowerCase();
22 | text = text.split("'").join('');
23 | text = text.split('"').join('');
24 | text = text.split('.').join('');
25 | text = text.split('`').join('');
26 | text = text.split(':').join('');
27 | text = text.split('/').join('');
28 | text = text.split('<').join('');
29 | text = text.split('>').join('');
30 | text = text.split('<').join('');
31 | text = text.split('>').join('');
32 | text = text.split('(').join('');
33 | text = text.split(')').join('');
34 | text = text.split(' ').join('-');
35 | return text.toLowerCase();
36 | }
37 |
38 | function define(anchors,name,auto,suffix) {
39 | var oname = name;
40 | if (suffix) name = name+'-'+suffix;
41 | var anchor = anchors.find(function(e,i,a){
42 | return e.name == name;
43 | });
44 | if (anchor) {
45 | if (auto) {
46 | define(anchors,oname,auto,suffix ? suffix+1 : 1);
47 | }
48 | else {
49 | anchor.defined++;
50 | }
51 | }
52 | else {
53 | anchor = {
54 | name: name,
55 | defined: 1,
56 | seen: 0,
57 | auto: auto
58 | };
59 | anchors.push(anchor);
60 | }
61 | }
62 |
63 | function validate(s, options) {
64 | const result = {};
65 | if (options.source) result.source = options.source;
66 | result.imagesWithMissingAlt = 0;
67 |
68 | if (options.warnings) {
69 | result.tooManyBackticks = 0;
70 | const lines = s.split('\r').join('').split('\n');
71 | for (let line of lines) {
72 | if (line.startsWith('````')) {
73 | result.tooManyBackticks++;
74 | }
75 | }
76 | }
77 | var html = md.render(s);
78 | var $ = cheerio.load(html);
79 |
80 | var anchors = [];
81 |
82 | $("a").each(function () {
83 | var name = $(this).attr('id') || $(this).attr('name');
84 | if (name) {
85 | define(anchors,name,false);
86 | }
87 | });
88 |
89 | // GFM auto-links
90 | for (var heading of ["h1","h2","h3","h4","h5","h6"]) {
91 | var elements = $(heading).each(function() {
92 | var text = gfmLink($(this).text());
93 | define(anchors,text,true);
94 | });
95 | }
96 |
97 | $("a").each(function () {
98 | var href = $(this).attr('href');
99 | if (href) {
100 | var local = true;
101 | var u = url.parse(href);
102 | if (u.protocol || (u.path && u.path.startsWith('/')) || href.startsWith('./') || href.startsWith('../')) local = false;
103 | if (indexAnyOf(href,['.md','.html','.json','.yaml','.yml'])>=0) local = false; // FIXME buggy for relative links to files like `LICENSE`
104 | if (local) {
105 | var localRefNoHash = !href.startsWith('#');
106 | var ptr = href.replace('#','');
107 | var anchor = anchors.find(function(e,i,a){
108 | // fragment names are case-sensitive: https://www.w3.org/MarkUp/html-spec/html-spec_7.html#SEC7.4
109 | return e.name == ptr;
110 | });
111 | if (anchor) {
112 | anchor.seen++;
113 | if (localRefNoHash) anchor.localRefNoHash = true;
114 | }
115 | else {
116 | anchor = {
117 | name: ptr,
118 | defined: 0,
119 | seen: 1,
120 | localRefNoHash: localRefNoHash
121 | };
122 | anchors.push(anchor);
123 | }
124 | if (!$(this).text()) {
125 | anchor.emptyText = anchor.emptyText ? anchor.emptyText++ : 1;
126 | }
127 | }
128 | }
129 | });
130 |
131 | $("img").each(function() {
132 | if (!$(this).attr('alt')) {
133 | result.imagesWithMissingAlt++;
134 | }
135 | });
136 |
137 | result.missingAnchors = anchors.filter(function(e,i,a){
138 | return (!e.defined && e.seen && indexAnyOf(e.name,['.md','.html','.json','.yaml','.yml'])<0);
139 | });
140 |
141 | result.duplicatedAnchors = anchors.filter(function(e,i,a){
142 | return (e.defined>1);
143 | });
144 |
145 | result.anchorsWithHash = anchors.filter(function(e,i,a){
146 | return (e.name.startsWith('#'));
147 | });
148 |
149 | result.anchorsWithEmptyText = anchors.filter(function(e,i,a){
150 | return e.emptyText;
151 | });
152 |
153 | result.localRefNoHash = anchors.filter(function(e,i,a){
154 | return e.localRefNoHash;
155 | });
156 |
157 | result.nonParsingExamples = exampleValidator.parseExamples(s,options);
158 |
159 | if (options.warnings) {
160 | result.codeBlocksWithNoLanguage = 0;
161 | $("pre > code").each(function(){
162 | var classes = ($(this).attr('class')||'').split(' ');
163 | var lang = classes.find(function(e,i,a){
164 | return e.startsWith('language');
165 | });
166 | if (!lang) result.codeBlocksWithNoLanguage++;
167 | });
168 |
169 | result.anchorsWithNoLinks = anchors.filter(function(e,i,a){
170 | return (e.defined && !e.seen && !e.auto);
171 | });
172 | }
173 |
174 | if (options.save) {
175 | options.html = html;
176 | }
177 |
178 | return result;
179 | }
180 |
181 | module.exports = {
182 | validate : validate
183 | };
184 |
185 |
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true
5 | },
6 | "extends": "eslint:recommended",
7 | "rules": {
8 | "accessor-pairs": "error",
9 | "array-bracket-spacing": [
10 | "off",
11 | "never"
12 | ],
13 | "array-callback-return": "error",
14 | "arrow-body-style": "off",
15 | "arrow-parens": [
16 | "off",
17 | "as-needed"
18 | ],
19 | "arrow-spacing": [
20 | "error",
21 | {
22 | "after": true,
23 | "before": true
24 | }
25 | ],
26 | "block-scoped-var": "off",
27 | "block-spacing": "off",
28 | "brace-style": [
29 | "off",
30 | "stroustrup"
31 | ],
32 | "callback-return": "off",
33 | "camelcase": "off",
34 | "class-methods-use-this": "error",
35 | "comma-dangle": "error",
36 | "comma-spacing": "off",
37 | "comma-style": [
38 | "error",
39 | "last"
40 | ],
41 | "complexity": "off",
42 | "computed-property-spacing": [
43 | "error",
44 | "never"
45 | ],
46 | "consistent-return": "off",
47 | "consistent-this": "error",
48 | "curly": "off",
49 | "default-case": "error",
50 | "dot-location": [
51 | "off",
52 | "property"
53 | ],
54 | "dot-notation": [
55 | "error",
56 | {
57 | "allowKeywords": true
58 | }
59 | ],
60 | "eol-last": "error",
61 | "eqeqeq": "off",
62 | "func-call-spacing": "error",
63 | "func-name-matching": "off",
64 | "func-names": "off",
65 | "func-style": [
66 | "error",
67 | "declaration"
68 | ],
69 | "generator-star-spacing": "off",
70 | "global-require": "off",
71 | "guard-for-in": "off",
72 | "handle-callback-err": "off",
73 | "id-blacklist": "error",
74 | "id-length": "off",
75 | "id-match": "error",
76 | "indent": "off",
77 | "init-declarations": "off",
78 | "jsx-quotes": "error",
79 | "key-spacing": "off",
80 | "keyword-spacing": "off",
81 | "line-comment-position": "off",
82 | "linebreak-style": [
83 | "error",
84 | "unix"
85 | ],
86 | "lines-around-comment": "off",
87 | "lines-around-directive": "off",
88 | "max-depth": "off",
89 | "max-len": "off",
90 | "max-lines": "off",
91 | "max-nested-callbacks": "error",
92 | "max-params": "off",
93 | "max-statements": "off",
94 | "max-statements-per-line": "off",
95 | "multiline-ternary": [
96 | "off",
97 | "never"
98 | ],
99 | "new-parens": "error",
100 | "newline-after-var": "off",
101 | "newline-before-return": "off",
102 | "newline-per-chained-call": "off",
103 | "no-alert": "error",
104 | "no-array-constructor": "error",
105 | "no-bitwise": "off",
106 | "no-caller": "error",
107 | "no-catch-shadow": "off",
108 | "no-confusing-arrow": "error",
109 | "no-console": "off",
110 | "no-continue": "off",
111 | "no-div-regex": "error",
112 | "no-duplicate-imports": "error",
113 | "no-else-return": "off",
114 | "no-empty": [
115 | "warn",
116 | {
117 | "allowEmptyCatch": true
118 | }
119 | ],
120 | "no-empty-function": "off",
121 | "no-eq-null": "error",
122 | "no-eval": "error",
123 | "no-extend-native": "off",
124 | "no-extra-bind": "error",
125 | "no-extra-label": "error",
126 | "no-extra-parens": "off",
127 | "no-floating-decimal": "error",
128 | "no-global-assign": "error",
129 | "no-implicit-globals": "error",
130 | "no-implied-eval": "error",
131 | "no-inline-comments": "off",
132 | "no-inner-declarations": [
133 | "error",
134 | "functions"
135 | ],
136 | "no-invalid-this": "off",
137 | "no-iterator": "error",
138 | "no-label-var": "error",
139 | "no-labels": "error",
140 | "no-lone-blocks": "error",
141 | "no-lonely-if": "off",
142 | "no-loop-func": "off",
143 | "no-magic-numbers": "off",
144 | "no-mixed-operators": "error",
145 | "no-mixed-requires": "error",
146 | "no-multi-spaces": "off",
147 | "no-multi-str": "error",
148 | "no-multiple-empty-lines": "error",
149 | "no-negated-condition": "error",
150 | "no-nested-ternary": "off",
151 | "no-new": "error",
152 | "no-new-func": "error",
153 | "no-new-object": "error",
154 | "no-new-require": "error",
155 | "no-new-wrappers": "error",
156 | "no-octal-escape": "error",
157 | "no-param-reassign": "off",
158 | "no-path-concat": "error",
159 | "no-plusplus": "off",
160 | "no-process-env": "off",
161 | "no-process-exit": "off",
162 | "no-proto": "error",
163 | "no-prototype-builtins": "error",
164 | "no-restricted-globals": "error",
165 | "no-restricted-imports": "error",
166 | "no-restricted-modules": "error",
167 | "no-restricted-properties": "error",
168 | "no-restricted-syntax": "error",
169 | "no-return-assign": "off",
170 | "no-script-url": "error",
171 | "no-self-compare": "error",
172 | "no-sequences": "error",
173 | "no-shadow": "off",
174 | "no-shadow-restricted-names": "error",
175 | "no-spaced-func": "error",
176 | "no-sync": "off",
177 | "no-tabs": "off",
178 | "no-template-curly-in-string": "error",
179 | "no-ternary": "off",
180 | "no-throw-literal": "error",
181 | "no-trailing-spaces": "error",
182 | "no-undef-init": "error",
183 | "no-undefined": "warn",
184 | "no-underscore-dangle": "off",
185 | "no-unmodified-loop-condition": "error",
186 | "no-unneeded-ternary": [
187 | "error",
188 | {
189 | "defaultAssignment": true
190 | }
191 | ],
192 | "no-unsafe-negation": "error",
193 | "no-unused-expressions": "error",
194 | "no-unused-vars": "off",
195 | "no-use-before-define": "off",
196 | "no-useless-call": "error",
197 | "no-useless-computed-key": "error",
198 | "no-useless-concat": "off",
199 | "no-useless-constructor": "error",
200 | "no-useless-escape": "off",
201 | "no-useless-rename": "error",
202 | "no-var": "off",
203 | "no-void": "error",
204 | "no-warning-comments": "off",
205 | "no-whitespace-before-property": "error",
206 | "no-with": "error",
207 | "object-curly-newline": "off",
208 | "object-curly-spacing": "off",
209 | "object-property-newline": [
210 | "off",
211 | {
212 | "allowMultiplePropertiesPerLine": true
213 | }
214 | ],
215 | "object-shorthand": "off",
216 | "one-var": "off",
217 | "one-var-declaration-per-line": "error",
218 | "operator-assignment": "off",
219 | "operator-linebreak": "off",
220 | "padded-blocks": "off",
221 | "prefer-arrow-callback": "off",
222 | "prefer-const": "off",
223 | "prefer-numeric-literals": "error",
224 | "prefer-reflect": "off",
225 | "prefer-rest-params": "error",
226 | "prefer-spread": "error",
227 | "prefer-template": "off",
228 | "quote-props": "off",
229 | "quotes": "off",
230 | "radix": "error",
231 | "require-jsdoc": "off",
232 | "require-yield": "off",
233 | "rest-spread-spacing": "error",
234 | "semi": "off",
235 | "semi-spacing": "off",
236 | "sort-imports": "error",
237 | "sort-keys": "off",
238 | "sort-vars": "off",
239 | "space-before-blocks": "off",
240 | "space-before-function-paren": "off",
241 | "space-in-parens": [
242 | "error",
243 | "never"
244 | ],
245 | "space-infix-ops": "off",
246 | "space-unary-ops": "error",
247 | "spaced-comment": "off",
248 | "strict": "error",
249 | "symbol-description": "error",
250 | "template-curly-spacing": "error",
251 | "unicode-bom": [
252 | "error",
253 | "never"
254 | ],
255 | "valid-jsdoc": "off",
256 | "vars-on-top": "off",
257 | "wrap-iife": "error",
258 | "wrap-regex": "off",
259 | "yield-star-spacing": "error",
260 | "yoda": [
261 | "error",
262 | "never"
263 | ]
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/test/pass/api-blueprint.md:
--------------------------------------------------------------------------------
1 | ---
2 |
3 | Author: z@apiary.io
4 | Version: 1A9
5 |
6 | ---
7 |
8 | # API Blueprint
9 | #### Format 1A revision 9
10 |
11 | ## [I. API Blueprint Language](#def-api-blueprint-language)
12 | + [Introduction](#def-introduction)
13 | + [API Blueprint](#def-api-blueprint)
14 | + [API Blueprint document](#def-api-blueprint-document)
15 | + [Blueprint section](#def-blueprint-section)
16 | + [Section types](#def-section-types)
17 | + [Section structure](#def-section-structure)
18 | + [Keywords](#def-keywords)
19 | + [Identifier](#def-identifier)
20 | + [Description](#def-description)
21 | + [Nested sections](#def-nested-sections)
22 |
23 | ## [II. Sections Reference](#def-sections-reference)
24 |
25 | ### Abstract
26 | + [Named section](#def-named-section)
27 | + [Asset section](#def-asset-section)
28 | + [Payload section](#def-payload-section)
29 |
30 | ### Section Basics
31 | + [Metadata section](#def-metadata-section)
32 | + [API name & overview section](#def-api-name-section)
33 | + [Resource group section](#def-resourcegroup-section)
34 | + [Resource section](#def-resource-section)
35 | + [Resource model section](#def-model-section)
36 | + [Schema section](#def-schema-section)
37 | + [Action section](#def-action-section)
38 | + [Request section](#def-request-section)
39 | + [Response section](#def-response-section)
40 | + [URI parameters section](#def-uriparameters-section)
41 | + [Attributes section](#def-attributes-section)
42 | + [Headers section](#def-headers-section)
43 | + [Body section](#def-body-section)
44 |
45 | ### Going Further
46 | + [Data Structures section](#def-data-structures)
47 | + [Relation section](#def-relation-section)
48 |
49 |
50 | ## [III. Appendix](#def-appendix)
51 | + [URI Templates](#def-uri-templates)
52 |
53 | ---
54 |
55 |
56 |
57 |
58 | # I. API Blueprint Language
59 |
60 |
61 | ## Introduction
62 | This documents is a full specification of the API Blueprint format. For a less
63 | formal introduction to API Blueprint visit the
64 | [API Blueprint Tutorial](Tutorial.md) or check some of the [examples][].
65 |
66 |
67 | ## API Blueprint
68 | API Blueprint is a documentation-oriented web API description language. The
69 | API Blueprint is essentially a set of semantic assumptions laid on top of the
70 | Markdown syntax used to describe a web API.
71 |
72 | In addition to the regular [Markdown syntax][], API Blueprint conforms to the
73 | [GitHub Flavored Markdown syntax][].
74 |
75 |
76 | ## API Blueprint document
77 | An API Blueprint document – a blueprint – is a plain text Markdown document
78 | describing a Web API in whole or in part. The document is structured into
79 | logical **sections**. Each section has its distinctive meaning, content and
80 | position in the document.
81 |
82 | General section definition and structure is discussed in detail later in the
83 | [Blueprint section](#def-blueprint-section) chapter.
84 |
85 | All of the blueprint sections are optional. However, when present, a section
86 | **must** follow the API Blueprint **document structure**.
87 |
88 | ### Blueprint document structure
89 |
90 | + [`0-1` **Metadata** section](#def-metadata-section)
91 | + [`0-1` **API Name & overview** section](#def-api-name-section)
92 | + [`0+` **Resource** sections](#def-resource-section)
93 | + [`0-1` **URI Parameters** section](#def-uriparameters-section)
94 | + [`0-1` **Attributes** section](#def-attributes-section)
95 | + [`0-1` **Model** section](#def-model-section)
96 | + [`0-1` **Headers** section](#def-headers-section)
97 | + [`0-1` **Attributes** section](#def-attributes-section)
98 | + [`0-1` **Body** section](#def-body-section)
99 | + [`0-1` **Schema** section](#def-schema-section)
100 | + [`1+` **Action** sections](#def-action-section)
101 | + [`0-1` **Relation** section](#def-relation-section)
102 | + [`0-1` **URI Parameters** section](#def-uriparameters-section)
103 | + [`0-1` **Attributes** section](#def-attributes-section)
104 | + [`0+` **Request** sections](#def-request-section)
105 | + [`0-1` **Headers** section](#def-headers-section)
106 | + [`0-1` **Attributes** section](#def-attributes-section)
107 | + [`0-1` **Body** section](#def-body-section)
108 | + [`0-1` **Schema** section](#def-schema-section)
109 | + [`1+` **Response** sections](#def-response-section)
110 | + [`0-1` **Headers** section](#def-headers-section)
111 | + [`0-1` **Attributes** section](#def-attributes-section)
112 | + [`0-1` **Body** section](#def-body-section)
113 | + [`0-1` **Schema** section](#def-schema-section)
114 | + [`0+` **Resource Group** sections](#def-resourcegroup-section)
115 | + [`0+` **Resource** sections](#def-resource-section) (see above)
116 | + [`0+` **Data Structures** section](#def-data-structures)
117 |
118 | > **NOTE:** The number prior to a section name denotes the allowed number of
119 | > the section occurrences.
120 |
121 | > **NOTE:** Refer to [Sections Reference](#def-sections-reference) for
122 | > description of a specific section type.
123 |
124 |
125 | ## Blueprint section
126 | A _Section_ represents a logical unit of an API Blueprint. For example: an API
127 | overview, a group of resources or a resource definition.
128 |
129 | In general a section is **defined** using a **keyword** in a Markdown entity.
130 | Depending on the type of section the keyword is written either as a Markdown
131 | header entity or in a list item entity.
132 |
133 | A section definition **may** also contain additional variable components such
134 | as its **identifier** and additional modifiers.
135 |
136 | > **NOTE**: There are two special sections that are recognized by their
137 | > position in the document instead of a keyword: The [Metadata section]() and
138 | > the [API Name & Overview section](). Refer to the respective section entry
139 | > for details on its definition.
140 |
141 | #### Example: Header-defined sections
142 |
143 | #
144 |
145 | ...
146 |
147 | #
148 |
149 | ...
150 |
151 |
152 | > **NOTE:** While this specification uses "atx" -style headers (using `#`s)
153 | > you can also use "Setext" [header syntax][] interchangeably:
154 | >
155 | >
156 | > =========
157 | >
158 | > ...
159 | >
160 | >
161 | > =========
162 | >
163 | > ...
164 |
165 | #### Example: List-defined sections
166 |
167 | +
168 |
169 | ...
170 |
171 | +
172 |
173 | ...
174 |
175 | > **NOTE:** While this specification uses pluses (`+`) as list markers you can
176 | > use any Markdown [list syntax][] using asterisks (`*`), pluses (`+`) and
177 | > hyphens (`-`) interchangeably:
178 | >
179 | > *
180 | >
181 | > ...
182 | >
183 | > -
184 | >
185 | > ...
186 |
187 |
188 | ### Section types
189 | There are several types of API Blueprint sections. You can find the complete
190 | listing of the section types in the
191 | [Section Reference](#def-sections-reference).
192 |
193 | **The Blueprint section chapter discusses the section syntax in general.**
194 | **A specific section type may conform only to some parts of this general syntax.**
195 | Always refer for respective section reference for details on its syntax.
196 |
197 |
198 | ### Section structure
199 | A general structure of an API Blueprint section defined by a **keyword**
200 | includes an **identifier** (name), section **description** and **nested
201 | sections** or a specifically formatted content.
202 |
203 | #### Example: Header-defined section structure
204 |
205 | #
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | #### Example: List-defined section structure
214 |
215 | +
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | ### Keywords
225 | Following reserved keywords are used in section definitions:
226 |
227 | #### Header keywords
228 | - `Group`
229 | - `Data Structures`
230 | - [HTTP methods][httpmethods] (e.g. `GET, POST, PUT, DELETE`...)
231 | - [URI templates][uritemplate] (e.g. `/resource/{id}`)
232 | - Combinations of an HTTP method and URI Template (e.g. `GET /resource/{id}`)
233 |
234 | #### List keywords
235 | - `Request`
236 | - `Response`
237 | - `Body`
238 | - `Schema`
239 | - `Model`
240 | - `Header` & `Headers`
241 | - `Parameter` & `Parameters`
242 | - `Values`
243 | - `Attribute` & `Attributes`
244 | - `Relation`
245 |
246 | > **NOTE: Avoid using these keywords in other Markdown headers or lists**
247 |
248 | > **NOTE:** With the exception of HTTP methods keywords the section keywords
249 | > are case insensitive.
250 |
251 |
252 | ### Identifier
253 | A section definition **may** or **must** include an identifier of the section.
254 | An **identifier is any non-empty combination of any character except `[`, `]`,
255 | `(`, `)` and newline characters**.
256 |
257 | An identifier **must not** contain any of the [keywords](#def-keywords).
258 |
259 | #### Example
260 |
261 | ```
262 | Adam's Message 42
263 | ```
264 |
265 | ```
266 | my-awesome-message_2
267 | ```
268 |
269 |
270 |
271 | ### Description
272 | A section description is any arbitrary Markdown-formatted content following the
273 | section definition.
274 |
275 | It is possible to use any Markdown header or list item in a section description
276 | as long as it does not clash with any of the
277 | [reserved keywords](#def-keywords).
278 |
279 | > **NOTE:** It is considered good practice to keep the header level nested
280 | > under the actual section.
281 |
282 |
283 | ### Nested sections
284 | A section **may** contain another nested section(s).
285 |
286 | Depending on the nested section type, to nest a section simply increase its
287 | header level or its list item indentation. Anything between the section start
288 | and the start of following section at the same level is considered to be part
289 | of the section.
290 |
291 | What sections can be nested and where depends upon the section in case, as
292 | described in the relevant section's entry.
293 |
294 | #### Example: Nested header-defined section
295 |
296 | #
297 |
298 | ...
299 |
300 | ##
301 |
302 | ...
303 |
304 | #### Example: Nested list-defined section
305 |
306 | +
307 |
308 | ...
309 |
310 | +
311 |
312 | ...
313 |
314 | > **NOTE:** While not necessary it is a good habit to increase the level of a
315 | > nested section markdown-header.
316 |
317 | > **NOTE:** A markdown-list section is always considered to be nested under the
318 | > preceding markdown-header section.
319 |
320 | ---
321 |
322 |
323 | # II. Sections Reference
324 | > **NOTE:** Sections marked as "Abstract" serve as a base for other sections
325 | > and as such they **cannot** be used directly.
326 |
327 |
328 | # Abstract
329 |
330 |
331 | ## Named section
332 | - **Abstract**
333 | - **Parent sections:** vary, see descendants
334 | - **Nested sections:** vary, see descendants
335 | - **Markdown entity:** header, list
336 | - **Inherits from**: none
337 |
338 | #### Definition
339 | Defined by a [keyword](#def-keywords) followed by an optional section name -
340 | [identifier](#def-identifier) in a Markdown header or list entity.
341 |
342 | ```
343 | #
344 | ```
345 |
346 | ```
347 | +
348 | ```
349 |
350 | #### Description
351 | Named section is the base section for most of the API Blueprint sections. It
352 | conforms to the [general section](#def-section-structure) and as such it is
353 | composed of a section name (identifier), description and nested sections or
354 | specific formatted content (see descendants descriptions).
355 |
356 | #### Example
357 |
358 | # Section Name
359 | This the `Section Name` description.
360 |
361 | - one
362 | - **two**
363 | - three
364 |
365 | |
366 |
367 | ---
368 |
369 |
370 | ## Asset section
371 | - **Abstract**
372 | - **Parent sections:** vary, see descendants
373 | - **Nested sections:** none
374 | - **Markdown entity:** list
375 | - **Inherits from**: none
376 |
377 | #### Definition
378 | Defined by a [keyword](#def-keywords) in Markdown list entity.
379 |
380 | +
381 |
382 | #### Description
383 | The asset section is the base section for atomic data in API Blueprint. The content
384 | of this section is expected to be a
385 | [pre-formatted code block](http://daringfireball.net/projects/markdown/syntax#precode).
386 |
387 | #### Example
388 |
389 | +
390 |
391 | {
392 | "message": "Hello"
393 | }
394 |
395 | #### Example: Fenced code blocks
396 |
397 | +
398 |
399 | ```
400 | {
401 | "message": "Hello"
402 | }
403 | ```
404 |
405 | ---
406 |
407 |
408 | ## Payload section
409 | - **Abstract**
410 | - **Parent sections:** vary, see descendants
411 | - **Nested sections:** [`0-1` Headers section](#def-headers-section), [`0-1` Attributes section](#def-attributes-section), [`0-1` Body section](#def-body-section), [`0-1` Schema section](#def-schema-section)
412 | - **Markdown entity:** list
413 | - **Inherits from**: [Named section](#def-named-section)
414 |
415 | #### Definition
416 | Defined by a [keyword](#def-keywords) in Markdown list entity. The keyword **may** be followed by identifier.
417 | The definition **may** include payload's media-type enclosed in braces.
418 |
419 | + ()
420 |
421 | > **NOTE:** Refer to descendant for the particular section type definition.
422 |
423 | #### Description
424 | Payload sections represent the information transferred as a payload of an HTTP
425 | request or response messages. A Payload consists of optional meta information
426 | in the form of HTTP headers and optional content in the form of an HTTP body.
427 |
428 | Furthermore, in API Blueprint context, a payload includes its description,
429 | description of its message-body attributes and a message-body validation
430 | schema.
431 |
432 | A payload **may** have its media type associated. A payload's media type
433 | represents the metadata received or sent in the form of a HTTP `Content-Type`
434 | header. When specified a payload **should** include nested
435 | [Body section](#def-body-section).
436 |
437 | This section **should** include at least one of the following nested sections:
438 |
439 | - [`0-1` Headers section](#def-headers-section)
440 | - [`0-1` Attributes section](#def-attributes-section)
441 | - [`0-1` Body section](#def-body-section)
442 | - [`0-1` Schema section](#def-schema-section)
443 |
444 | If there is no nested section the content of the payload section is considered
445 | as content of the [Body section](#def-body-section).
446 |
447 | #### Relation of Body, Schema and Attributes sections
448 | Each of body, schema and attributes sections describe a message payload's body.
449 | These descriptions **should** be consistent, not violating each other. When
450 | multiple body descriptions are provided they **should** be prioritized as
451 | follows:
452 |
453 | 1. For resolving message-body schema
454 | 1. Schema section
455 | 2. Attributes section
456 | 3. Body section
457 |
458 | 2. For resolving message-body example
459 | 1. Body section
460 | 2. Attributes section
461 | 3. Schema section
462 |
463 | #### Referencing
464 | Instead of providing a payload section content, a
465 | [model payload section](#def-model-section) can be referenced using the
466 | Markdown implicit [reference syntax][]:
467 |
468 | [][]
469 |
470 | #### Example
471 |
472 | + Payload Name (application/json)
473 |
474 | This the `Payload Name` description.
475 |
476 | + Headers
477 |
478 | ...
479 |
480 | + Body
481 |
482 | ...
483 |
484 | + Schema
485 |
486 | ...
487 |
488 | #### Example: Referencing model payload
489 |
490 | + Payload Name
491 |
492 | [Resource model identifier][]
493 |
494 | ---
495 |
496 |
497 | # Section Basics
498 |
499 |
500 |
501 | ## Metadata section
502 | - **Parent sections:** none
503 | - **Nested sections:** none
504 | - **Markdown entity:** special
505 | - **Inherits from**: none
506 |
507 | #### Definition
508 | Key-value pairs. Each key is separated from its value by a colon (`:`). One
509 | pair per line. Starts at the beginning of the document and ends with the first
510 | Markdown element that is not recognized as a key-value pair.
511 |
512 | #### Description
513 | Metadata keys and their values are tool-specific. Refer to relevant tool
514 | documentation for the list of supported keys.
515 |
516 | #### Example
517 |
518 | FORMAT: 1A
519 | HOST: http://blog.acme.com
520 |
521 | ---
522 |
523 |
524 | ## API name & overview section
525 | - **Parent sections:** none
526 | - **Nested sections:** none
527 | - **Markdown entity:** special, header
528 | - **Inherits from**: [Named section](#def-named-section)
529 |
530 | #### Definition
531 | Defined by the **first** Markdown header in the blueprint document, unless it
532 | represents another section definition.
533 |
534 | #### Description
535 | Name and description of the API
536 |
537 | #### Example
538 |
539 | # Basic ACME Blog API
540 | Welcome to the **ACME Blog** API. This API provides access to the **ACME
541 | Blog** service.
542 |
543 | ---
544 |
545 |
546 | ## Resource group section
547 | - **Parent sections:** none
548 | - **Nested sections:** [`0+` Resource section](#def-resource-section)
549 | - **Markdown entity:** header
550 | - **Inherits from**: [Named section](#def-named-section)
551 |
552 | #### Definition
553 | Defined by the `Group` keyword followed by group [name (identifier)](#def-identifier):
554 |
555 | # Group
556 |
557 | #### Description
558 | This section represents a group of resources (Resource Sections). **May**
559 | include one or more nested [Resource Sections](#def-resource-section).
560 |
561 | #### Example
562 |
563 | ```apib
564 | # Group Blog Posts
565 |
566 | ## Resource 1 [/resource1]
567 |
568 | ...
569 |
570 | # Group Authors
571 | Resources in this groups are related to **ACME Blog** authors.
572 |
573 | ## Resource 2 [/resource2]
574 |
575 | ...
576 | ```
577 |
578 | ---
579 |
580 |
581 | ## Resource section
582 | - **Parent sections:** none, [Resource group section](#def-resourcegroup-section)
583 | - **Nested sections:** [`0-1` Parameters section](#def-uriparameters-section), [`0-1` Attributes section](#def-attributes-section), [`0-1` Model section](#def-model-section), [`1+` Action section](#def-action-section)
584 | - **Markdown entity:** header
585 | - **Inherits from**: [Named section](#def-named-section)
586 |
587 | #### Definition
588 | Defined by an [URI template][uritemplate]:
589 |
590 | #
591 |
592 | **-- or --**
593 |
594 | Defined by a resource [name (identifier)](#def-identifier) followed by an
595 | [URI template][uritemplate] enclosed in square brackets `[]`.
596 |
597 | # []
598 |
599 | **-- or --**
600 |
601 | Defined by an [HTTP request method][httpmethods] followed by [URI template][uritemplate]:
602 |
603 | #
604 |
605 | **-- or --**
606 |
607 | Defined by a resource [name (identifier)](#def-identifier) followed by an
608 | [HTTP request method][httpmethods] and an [URI template][uritemplate] enclosed
609 | in square brackets `[]`:
610 |
611 | # [ ]
612 |
613 | > **NOTE:** In the latter two cases the rest of this section represents the
614 | > [Action section](#def-action-section) including its description and nested
615 | > sections and **follows the rules of the Action section instead**.
616 |
617 | #### Description
618 | An API [resource](http://www.w3.org/TR/di-gloss/#def-resource) as specified by
619 | its *URI* or a set of resources (a resource template) matching its *URI
620 | template*.
621 |
622 | This section **should** include at least one nested
623 | [Action section](#def-action-section) and **may** include following nested
624 | sections:
625 |
626 | - [`0-1` URI parameters section](#def-uriparameters-section)
627 |
628 | URI parameters defined in the scope of a Resource section apply to
629 | _any and all_ nested Action sections except when an [URI template][uritemplate] has
630 | been defined for the Action.
631 |
632 | - [`0-1` Attributes section][]
633 |
634 | Attributes defined in the scope of a Resource section represent Resource
635 | attributes. If the resource is defined with a name these attributes **may**
636 | be referenced in [Attributes sections][].
637 |
638 | - [`0-1` Model section](#def-model-section)
639 |
640 | - Additional [Action sections](#def-action-section)
641 |
642 | > **NOTE:** A blueprint document may contain multiple sections for the same
643 | > resource (or resource set), as long as their HTTP methods differ. However it
644 | > is considered good practice to group multiple HTTP methods under one resource
645 | > (resource set).
646 |
647 | #### Example
648 |
649 | ```apib
650 | # Blog Posts [/posts/{id}]
651 | Resource representing **ACME Blog** posts.
652 | ```
653 |
654 | ```apib
655 | # /posts/{id}
656 | ```
657 |
658 | ```apib
659 | # GET /posts/{id}
660 | ```
661 |
662 | ---
663 |
664 |
665 | ## Resource model section
666 | - **Parent sections:** [Resource section](#def-resource-section)
667 | - **Nested sections:** [Refer to payload section](#def-payload-section)
668 | - **Markdown entity:** list
669 | - **Inherits from**: [Payload section](#def-payload-section)
670 |
671 | #### Definition
672 | Defined by the `Model` keyword followed by an optional media type:
673 |
674 | + Model ()
675 |
676 | #### Description
677 | A [resource manifestation](http://www.w3.org/TR/di-gloss/#def-resource-manifestation) - one
678 | exemplary representation of the resource in the form of a
679 | [payload](#def-payload-section).
680 |
681 | #### Referencing
682 | The payload defined in this section **may** be referenced in any response or
683 | request section in the document using parent section's identifier. You can
684 | refer to this payload in any of the following [Request](#def-request-section)
685 | or [Response](#def-response-section) payload sections using the Markdown
686 | implicit [reference syntax][].
687 |
688 | #### Example
689 |
690 | ```apib
691 | # My Resource [/resource]
692 |
693 | + Model (text/plain)
694 |
695 | Hello World
696 |
697 | ## Retrieve My Resource [GET]
698 |
699 | + Response 200
700 |
701 | [My Resource][]
702 | ```
703 |
704 | ---
705 |
706 |
707 | ## Schema section
708 | - **Parent sections:** [Payload section](#def-payload-section)
709 | - **Nested sections:** none
710 | - **Markdown entity:** list
711 | - **Inherits from**: [Asset section](#def-asset-section)
712 |
713 | #### Definition
714 | Defined by the `Schema` keyword in Markdown list entity.
715 |
716 | + Schema
717 |
718 | #### Description
719 | Specifies a validation schema for the HTTP message-body of parent payload section.
720 |
721 | #### Example
722 |
723 | Following example uses [Body section](#def-body-section) to provide an example of an `application/json` payload, and [Schema section](#def-schema-section) to provide a [JSON Schema](http://json-schema.org/) describing all possible valid shapes of the payload.
724 |
725 | ```apib
726 | ## Retrieve a Message [GET]
727 |
728 | + Response 200 (application/json)
729 | + Body
730 |
731 | {"message": "Hello world!"}
732 |
733 | + Schema
734 |
735 | {
736 | "$schema": "http://json-schema.org/draft-04/schema#",
737 | "type": "object",
738 | "properties": {
739 | "message": {
740 | "type": "string"
741 | }
742 | }
743 | }
744 | ```
745 |
746 | ---
747 |
748 |
749 | ## Action section
750 | - **Parent sections:** [Resource section](#def-resource-section)
751 | - **Nested sections:**
752 | [`0-1` Relation section](#def-relation-section),
753 | [`0-1` URI parameters section](#def-uriparameters-section),
754 | [`0-1` Attributes section](#def-attributes-section),
755 | [`0+` Request section](#def-request-section),
756 | [`1+` Response section](#def-response-section)
757 | - **Markdown entity:** header
758 | - **Inherits from**: [Named section](#def-named-section)
759 |
760 | #### Definition
761 | Defined by an [HTTP request method][httpmethods]:
762 |
763 | ##
764 |
765 | **-- or --**
766 |
767 | Defined by an action [name (identifier)](#def-identifier) followed by an
768 | [HTTP request method][httpmethods] enclosed in square brackets `[]`.
769 |
770 | ## []
771 |
772 | **-- or --**
773 |
774 | Defined by an action [name (identifier)](#def-identifier) followed by an
775 | [HTTP request method][httpmethods] and
776 | [URI template][uritemplate] enclosed in square brackets `[]`.
777 |
778 | ## [ ]
779 |
780 | #### Description
781 | Definition of at least one complete HTTP transaction as performed with the
782 | parent resource section. An action section **may** consist of multiple HTTP
783 | transaction examples for the given HTTP request method.
784 |
785 | This section **may** include one nested
786 | [URI parameters section](#def-uriparameters-section) describing any URI
787 | parameters _specific_ to the action – URI parameters discussed in the scope of
788 | an Action section apply to the respective Action section ONLY.
789 |
790 | This section **may** include one nested [Attributes section][] defining the
791 | input (request) attributes of the section. If present, these attributes
792 | **should** be inherited in every Action's [Request section][] unless specified
793 | otherwise.
794 |
795 | Action section **should** include at least one nested
796 | [Response section](#def-response-section) and **may** include additional nested
797 | [Request](#def-request-section) and [Response](#def-response-section) sections.
798 |
799 | Nested Request and Response sections **may** be ordered into groups where each
800 | group represents one transaction example. The first transaction example group
801 | starts with the first nested Request or Response section. Subsequent groups
802 | start with the first nested Request section following a Response section.
803 |
804 | Multiple Request and Response nested sections within one transaction example
805 | **should** have different identifiers.
806 |
807 | #### Example
808 |
809 | ```apib
810 | # Blog Posts [/posts{?limit}]
811 | ...
812 |
813 | ## Retrieve Blog Posts [GET]
814 | Retrieves the list of **ACME Blog** posts.
815 |
816 | + Parameters
817 | + limit (optional, number) ... Maximum number of posts to retrieve
818 |
819 | + Response 200
820 |
821 | ...
822 |
823 | ## Create a Post [POST]
824 |
825 | + Attributes
826 |
827 | ...
828 |
829 | + Request
830 |
831 | ...
832 |
833 | + Response 201
834 |
835 | ...
836 |
837 | ## Delete a Post [DELETE /posts/{id}]
838 |
839 | + Parameters
840 | + id (string) ... Id of the post
841 |
842 | + Response 204
843 | ```
844 |
845 | #### Example Multiple Transaction Examples
846 |
847 | ```apib
848 | # Resource [/resource]
849 | ## Create Resource [POST]
850 |
851 | + request A
852 |
853 | ...
854 |
855 | + response 200
856 |
857 | ...
858 |
859 | + request B
860 |
861 | ...
862 |
863 | + response 200
864 |
865 | ...
866 |
867 | + response 500
868 |
869 | ...
870 |
871 | + request C
872 |
873 | ...
874 |
875 | + request D
876 |
877 | ...
878 |
879 | + response 200
880 |
881 | ...
882 | ```
883 |
884 | > **NOTE:** The "Multiple Transaction Examples" example demonstrates three
885 | > transaction examples for one given action:
886 | >
887 | > 1. 1st example: request `A`, response `200`
888 | > 2. 2nd example: request `B`, responses `200` and `500`
889 | > 3. 3rd example: requests `C` and `D`, response `200`
890 |
891 | ---
892 |
893 |
894 | ## Request section
895 | - **Parent sections:** [Action section](#def-action-section)
896 | - **Nested sections:** [Refer to payload section](#def-payload-section)
897 | - **Markdown entity:** list
898 | - **Inherits from**: [Payload section](#def-payload-section)
899 |
900 | #### Definition
901 | Defined by the `Request` keyword followed by an optional [identifier](#def-identifier):
902 |
903 | + Request ()
904 |
905 | #### Description
906 | One HTTP request-message example – payload.
907 |
908 | #### Example
909 |
910 | ```apib
911 | + Request Create Blog Post (application/json)
912 |
913 | { "message" : "Hello World." }
914 | ```
915 |
916 | ---
917 |
918 |
919 | ## Response section
920 | - **Parent sections:** [Action section](#def-action-section)
921 | - **Nested sections:** [Refer to payload section](#def-payload-section)
922 | - **Markdown entity:** list
923 | - **Inherits from**: [Payload section](#def-payload-section)
924 |
925 | #### Definition
926 | Defined by the `Response` keyword. The response section definition **should**
927 | include an [HTTP status code][] as its identifier.
928 |
929 | + Response ()
930 |
931 | #### Description
932 | One HTTP response-message example – payload.
933 |
934 | #### Example
935 |
936 | ```apib
937 | + Response 201 (application/json)
938 |
939 | { "message" : "created" }
940 | ```
941 |
942 | ---
943 |
944 |
945 | ## URI parameters section
946 | - **Parent Sections:** [Resource section](#def-resource-section) | [Action section](#def-action-section)
947 | - **Nested Sections:** none
948 | - **Markdown entity:** list
949 | - **Inherits from**: none, special
950 |
951 | #### Definition
952 | Defined by the `Parameters` keyword written in a Markdown list item:
953 |
954 | + Parameters
955 |
956 | #### Description
957 | Discussion of URI parameters _in the scope of the parent section_.
958 |
959 | This section **must** be composed of nested list items only. This section
960 | **must not** contain any other elements. Each list item describes a single URI
961 | parameter. The nested list items subsections inherit from the
962 | [Named section](#def-named-section) and are subject to additional formatting as
963 | follows:
964 |
965 | + : `` ( | enum[], required | optional) -
966 |
967 |
968 |
969 | + Default: ``
970 |
971 | + Members
972 | + ``
973 | + ``
974 | ...
975 | + ``
976 |
977 | Where:
978 |
979 | + `` is the parameter name as written in
980 | [Resource Section](#def-resource-section)'s URI (e.g. "id").
981 | + `` is any **optional** Markdown-formatted description of the
982 | parameter.
983 | + `` is any additional **optional** Markdown-formatted
984 | [description](#def-description) of the parameter.
985 | + `` is an **optional** default value of the parameter – a value
986 | that is used when no value is explicitly set (optional parameters only).
987 | + `` is an **optional** example value of the parameter (e.g. `1234`).
988 | + `` is the **optional** parameter type as expected by the API (e.g.
989 | "number", "string", "boolean"). "string" is the **default**.
990 | + `Members` is the **optional** enumeration of possible values.
991 | `` should be surrounded by `enum[]` if this is present.
992 | For example, if enumeration values are present for a parameter whose type is
993 | `number`, then `enum[number]` should be used instead of `number` to.
994 | + `` represents an element of enumeration type.
995 | + `required` is the **optional** specifier of a required parameter
996 | (this is the **default**)
997 | + `optional` is the **optional** specifier of an optional parameter.
998 |
999 | > **NOTE:** This section **should only** contain parameters that are specified
1000 | > in the parent's resource URI template, and does not have to list every URI
1001 | > parameter.
1002 |
1003 | #### Example
1004 |
1005 | ```apib
1006 | # GET /posts/{id}
1007 | ```
1008 |
1009 | ```apib
1010 | + Parameters
1011 | + id - Id of a post.
1012 | ```
1013 |
1014 | ```apib
1015 | + Parameters
1016 | + id (number) - Id of a post.
1017 | ```
1018 |
1019 | ```apib
1020 | + Parameters
1021 | + id: `1001` (number, required) - Id of a post.
1022 | ```
1023 |
1024 | ```apib
1025 | + Parameters
1026 | + id: `1001` (number, optional) - Id of a post.
1027 | + Default: `20`
1028 | ```
1029 |
1030 | ```apib
1031 | + Parameters
1032 | + id (enum[string])
1033 |
1034 | Id of a Post
1035 |
1036 | + Members
1037 | + `A`
1038 | + `B`
1039 | + `C`
1040 | ```
1041 | ---
1042 |
1043 |
1044 | ## Attributes Section
1045 | - **Parent sections:** [Resource section](#def-resource-section) | [Action section](#def-action-section) | [Payload section](#def-payload-section)
1046 | - **Nested sections:** See **[Markdown Syntax for Object Notation][MSON]**
1047 | - **Markdown entity:** list
1048 | - **Inherits from**: none
1049 |
1050 | #### Definition
1051 | Defined by the `Attributes` keyword followed by an optional
1052 | [MSON Type Definition][] enclosed in parentheses.
1053 |
1054 | + Attributes
1055 |
1056 | `` is the type definition of the data structure being
1057 | described. If the `` is not specified, an `object` base type
1058 | is assumed. See [MSON Type Definition][] for details.
1059 |
1060 | ##### Example
1061 |
1062 | ```apib
1063 | + Attributes (object)
1064 | ```
1065 |
1066 | #### Description
1067 | This section describes a data structure using the
1068 | **[Markdown Syntax for Object Notation][MSON] (MSON)**.
1069 | Based on the parent section, the data structure being described is one of the
1070 | following:
1071 |
1072 | 1. Resource data structure attributes ([Resource section](#def-resource-section))
1073 | 2. Action request attributes ([Action section](#def-action-section))
1074 | 3. Payload message-body attributes ([Payload section](#def-payload-section))
1075 |
1076 | Data structures defined in this section **may** refer to any arbitrary data
1077 | structures defined in the [Data Structures section](#def-data-structures) as
1078 | well as to any data structures defined by a named resource attributes
1079 | description (see below).
1080 |
1081 | #### Resource Attributes description
1082 | Description of the resource data structure.
1083 |
1084 | If defined in a named [Resource section](#def-resource-section), this data
1085 | structure **may** be referenced by other data structures using the resource
1086 | name.
1087 |
1088 | ##### Example
1089 |
1090 | ```apib
1091 | # Blog Post [/posts/{id}]
1092 | Resource representing **ACME Blog** posts.
1093 |
1094 | + Attributes
1095 | + id (number)
1096 | + message (string) - The blog post article
1097 | + author: john@appleseed.com (string) - Author of the blog post
1098 | ```
1099 |
1100 | > **NOTE:** This data structure can be later referred as:
1101 | >
1102 | > + Attributes (Blog Post)
1103 | >
1104 |
1105 | #### Action Attributes description
1106 | Description of the default request message-body data structure.
1107 |
1108 | If defined, all the [Request sections](#def-request-section) of the respective
1109 | [Action section](#def-action-section) inherits these attributes unless
1110 | specified otherwise.
1111 |
1112 | ##### Example
1113 |
1114 | ```apib
1115 | ## Create a Post [POST]
1116 |
1117 | + Attributes
1118 | + message (string) - The blog post article
1119 | + author: john@appleseed.com (string) - Author of the blog post
1120 |
1121 | + Request (application/json)
1122 |
1123 | + Request (application/yaml)
1124 |
1125 | + Response 201
1126 | ```
1127 |
1128 | #### Payload Attributes description
1129 | Description of payload (request, response, model) message-body attributes.
1130 |
1131 | Not every attribute has to be described. However, when an attribute is
1132 | described, it **should** appear in the respective
1133 | [Body section](#def-body-section) example, if a Body section is provided.
1134 |
1135 | If defined, the [Body section](#def-body-section) **may** be omitted and the
1136 | example representation **should** be generated from the attributes description.
1137 |
1138 | The description of message-body attributes **may** be used to describe
1139 | message-body validation if no [Schema section](#def-schema-section) is
1140 | provided. When a Schema section is provided, the attributes description
1141 | **should** conform to the schema.
1142 |
1143 | ##### Example
1144 |
1145 | ```apib
1146 | ## Retrieve a Post [GET]
1147 |
1148 | + Response 200 (application/json)
1149 |
1150 | + Attributes (object)
1151 | + message (string) - Message to the world
1152 |
1153 | + Body
1154 |
1155 | { "message" : "Hello World." }
1156 | ```
1157 |
1158 | ---
1159 |
1160 |
1161 | ## Headers section
1162 | - **Parent sections:** [Payload section](#def-payload-section)
1163 | - **Nested sections:** none
1164 | - **Markdown entity:** list
1165 | - **Inherits from**: none
1166 |
1167 | #### Definition
1168 | Defined by the `Headers` keyword in Markdown list entity.
1169 |
1170 | + Headers
1171 |
1172 | #### Description
1173 | Specifies the HTTP message-headers of the parent payload section. The content
1174 | this section is expected to be a [pre-formatted code block](http://daringfireball.net/projects/markdown/syntax#precode)
1175 | with the following syntax:
1176 |
1177 | :
1178 |
1179 | One HTTP header per line.
1180 |
1181 | #### Example
1182 |
1183 | ```apib
1184 | + Headers
1185 |
1186 | Accept-Charset: utf-8
1187 | Connection: keep-alive
1188 | Content-Type: multipart/form-data, boundary=AaB03x
1189 | ```
1190 |
1191 | ---
1192 |
1193 |
1194 | ## Body section
1195 | - **Parent sections:** [Payload section](#def-payload-section)
1196 | - **Nested sections:** none
1197 | - **Markdown entity:** list
1198 | - **Inherits from**: [Asset section](#def-asset-section)
1199 |
1200 | #### Definition
1201 | Defined by the `Body` keyword in Markdown list entity.
1202 |
1203 | + Body
1204 |
1205 | #### Description
1206 | Specifies the HTTP message-body of a payload section.
1207 |
1208 | #### Example
1209 |
1210 | ```apib
1211 | + Body
1212 |
1213 | {
1214 | "message": "Hello"
1215 | }
1216 | ```
1217 |
1218 | ---
1219 |
1220 |
1221 |
1222 | ## Data Structures section
1223 | - **Parent sections:** none
1224 | - **Nested sections:** _MSON Named Type definition_ (see below)
1225 | - **Markdown entity:** header
1226 | - **Inherits from**: none
1227 |
1228 | #### Definition
1229 | Defined by the `Data Structures` keyword.
1230 |
1231 | # Data Structures
1232 |
1233 | #### Description
1234 | This section holds arbitrary data structures definitions defined in the form of
1235 | [MSON Named Types][].
1236 |
1237 | Data structures defined in this section **may** be used in any [Attributes section][].
1238 | Similarly, any data structures defined in a [Attributes section][] of a named
1239 | [Resource Section][] **may** be used in a data structure definition.
1240 |
1241 | Refer to the [MSON][] specification for full details on how to define an MSON Named type.
1242 |
1243 | #### Example
1244 |
1245 | ```apib
1246 | # Data Structures
1247 |
1248 | ## Message (object)
1249 |
1250 | + text (string) - text of the message
1251 | + author (Author) - author of the message
1252 |
1253 | ## Author (object)
1254 |
1255 | + name: John
1256 | + email: john@appleseed.com
1257 | ```
1258 |
1259 | #### Example reusing Data Structure in Resource
1260 |
1261 | ```apib
1262 | # User [/user]
1263 |
1264 | + Attributes (Author)
1265 |
1266 | # Data Structures
1267 |
1268 | ## Author (object)
1269 |
1270 | + name: John
1271 | + email: john@appleseed.com
1272 | ```
1273 |
1274 | #### Example reusing Resource-defined Data Structure
1275 |
1276 | ```apib
1277 | # User [/user]
1278 |
1279 | + Attributes
1280 | + name: John
1281 | + email: john@appleseed.com
1282 |
1283 | # Data Structures
1284 |
1285 | ## Author (User)
1286 | ```
1287 |
1288 | ---
1289 |
1290 |
1291 | ## Relation section
1292 | - **Parent sections:** [Action section](#def-action-section)
1293 | - **Nested Sections:** none
1294 | - **Markdown entity:** list
1295 | - **Inherits from**: none
1296 |
1297 | #### Definition
1298 | Defined by the `Relation` keyword written in a Markdown list item followed by a
1299 | colon (`:`) and a link relation identifier.
1300 |
1301 | + Relation:
1302 |
1303 | #### Description
1304 | This section specifies a [link relation type](https://tools.ietf.org/html/rfc5988#section-4)
1305 | for the given action as specified by [RFC 5988](https://tools.ietf.org/html/rfc5988).
1306 |
1307 | > **NOTE:** The link relation identifiers should be unique per resource in the blueprint document.
1308 |
1309 | #### Example
1310 |
1311 | ```apib
1312 | # Task [/tasks/{id}]
1313 |
1314 | + Parameters
1315 | + id
1316 |
1317 | ## Retrieve Task [GET]
1318 |
1319 | + Relation: task
1320 | + Response 200
1321 |
1322 | { ... }
1323 |
1324 | ## Delete Task [DELETE]
1325 |
1326 | + Relation: delete
1327 | + Response 204
1328 | ```
1329 |
1330 | ---
1331 |
1332 |
1333 |
1334 |
1335 |
1336 | # III. Appendix
1337 |
1338 |
1339 | ## URI Templates
1340 |
1341 | The API Blueprint uses a subset of [RFC6570][rfc6570] to define a resource URI Template.
1342 |
1343 | ### URI Path Segment
1344 |
1345 | At its simplest form – without any variables – a path segment of an
1346 | URI Template is identical to an URI path segment:
1347 |
1348 | ```
1349 | /path/to/resources/42
1350 | ```
1351 |
1352 | ### URI Template Variable
1353 |
1354 | Variable names are case-sensitive. The variable name may consists of following
1355 | characters **only**:
1356 |
1357 | - ASCII alpha numeric characters (`a-z`, `A-Z`)
1358 | - Decimal digits (`0-9`)
1359 | - `_`
1360 | - [Percent-encoded][pct-encoded] characters
1361 | - `.`
1362 |
1363 | Multiple variables are separated by the comma **without** any leading or
1364 | trailing spaces. A variable(s) **must** be enclosed in braces – `{}`
1365 | **without** any additional leading or trailing whitespace.
1366 |
1367 | #### Operators
1368 |
1369 | The first variable in the braces **might** be preceded by an operator.
1370 | API Blueprint currently supports the following operators:
1371 |
1372 | - `#` – _fragment identifier_ operator
1373 | - `+` – _reserved value_ operator
1374 | - `?` – _form-style query_ operator
1375 | - `&` – _form-style query continuation_ operator
1376 |
1377 | #### Examples
1378 |
1379 | ```
1380 | {var}
1381 | {var1,var2,var3}
1382 | {#var}
1383 | {+var}
1384 | {?var}
1385 | {?var1,var2}
1386 | {?%24var}
1387 | {&var}
1388 | ```
1389 |
1390 | > **NOTE:** The [explode variable modifier][uri-explode] is also supported.
1391 | > Refer to RFC6570 for its description.
1392 |
1393 | #### Variable Reserved Values
1394 |
1395 | Following characters are **reserved** in variable _values_:
1396 |
1397 | `:` / `/` / `?` / `#` / `[` / `]` / `@` / `!` / `$` / `&` / `'` / `(` / `)` / `*` / `+` / `,` / `;` / `=`
1398 |
1399 | ### Path Segment Variable
1400 |
1401 | Simple path segment component variable is defined without any operator:
1402 |
1403 | ```
1404 | /path/to/resources/{var}
1405 | ```
1406 |
1407 | With `var := 42` the expansion is `/path/to/resources/42`.
1408 |
1409 | > **NOTE:** RFC6570 – Level 1
1410 |
1411 | ### Fragment Identifier Variable
1412 |
1413 | URI Template variables for fragment identifiers are defined using the
1414 | crosshatch (`#`) operator:
1415 |
1416 | ```
1417 | /path/to/resources/42{#var}
1418 | ```
1419 |
1420 | With `var := my_id` the expansion is `/path/to/resources/42#my_id`.
1421 |
1422 | > **NOTE:** RFC6570 – Level 2
1423 |
1424 | ### Variable with Reserved Characters Values
1425 |
1426 | To define URI Template variables with reserved URI characters,
1427 | use the plus (`+`) operator:
1428 |
1429 | ```
1430 | /path/{+var}/42
1431 | ```
1432 |
1433 | With `var := to/resources` the expansion is `/path/to/resources/42`.
1434 |
1435 | > **NOTE:** RFC6570 – Level 2
1436 |
1437 | ### Form-style Query Variable
1438 |
1439 | To define variables for a form-style query use the question mark (`?`) operator
1440 |
1441 | ```
1442 | /path/to/resources/{varone}{?vartwo}
1443 | ```
1444 |
1445 | With `varone := 42` and `vartwo = hello` the expansion is `/path/to/resources/42?vartwo=hello`.
1446 |
1447 | To continue a form-style query use the ampersand (`&`) operator:
1448 |
1449 | ```
1450 | /path/to/resources/{varone}?path=test{&vartwo,varthree}
1451 | ```
1452 |
1453 | With `varone := 42`, `vartwo = hello`, `varthree = 1024` the expansion is `/path/to/resources/42?path=test&vartwo=hello&varthree=1024`.
1454 |
1455 | > **NOTE:** RFC6570 – Part of Level 3
1456 |
1457 | ---
1458 |
1459 | [apiblueprint.org]: http://apiblueprint.org
1460 | [markdown syntax]: http://daringfireball.net/projects/markdown
1461 | [reference syntax]: http://daringfireball.net/projects/markdown/syntax#link
1462 | [gitHub flavored markdown syntax]: https://help.github.com/articles/github-flavored-markdown
1463 | [httpmethods]: https://github.com/for-GET/know-your-http-well/blob/master/methods.md#know-your-http-methods-well
1464 | [uritemplate]: #def-uri-templates
1465 | [rfc6570]: http://tools.ietf.org/html/rfc6570
1466 | [HTTP status code]: https://github.com/for-GET/know-your-http-well/blob/master/status-codes.md
1467 | [header syntax]: https://daringfireball.net/projects/markdown/syntax#header
1468 | [list syntax]: https://daringfireball.net/projects/markdown/syntax#list
1469 | [pct-encoded]: http://en.wikipedia.org/wiki/Percent-encoding
1470 | [uri-explode]: http://tools.ietf.org/html/rfc6570#section-2.4.2
1471 | [examples]: https://github.com/apiaryio/api-blueprint/tree/master/examples
1472 |
1473 | [MSON]: https://github.com/apiaryio/mson
1474 | [MSON Named Types]: https://github.com/apiaryio/mson/blob/master/MSON%20Specification.md#22-named-types
1475 | [MSON Type Definition]: https://github.com/apiaryio/mson/blob/master/MSON%20Specification.md#35-type-definition
1476 |
1477 | [`0-1` Attributes section]: #def-attributes-section
1478 | [Attributes section]: #def-attributes-section
1479 | [Attributes sections]: #def-attributes-section
1480 |
1481 | [Resource Section]: #def-resource-section
1482 |
1483 | [Request section]: #def-request-section
1484 |
--------------------------------------------------------------------------------
/test/pass/asyncapi.md:
--------------------------------------------------------------------------------
1 | # AsyncAPI Specification
2 |
3 | #### Disclaimer
4 |
5 | Part of this content has been taken from the great work done by the folks at the [Open API Initiative](https://openapis.org). Mainly because **it's a great work** and we want to keep as much compatibility as possible with the [Open API Specification](https://github.com/OAI/OpenAPI-Specification).
6 |
7 | #### Version 1.0.0-rc1
8 |
9 | The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC 2119](http://www.ietf.org/rfc/rfc2119.txt).
10 |
11 | The AsyncAPI Specification is licensed under [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
12 |
13 | ## Introduction
14 |
15 | The AsyncAPI Specification is a project used to describe and document Asynchronous APIs.
16 |
17 | The AsyncAPI Specification defines a set of files required to describe such an API.
18 | These files can then be used to create utilities, such as documentation, integration and/or testing tools.
19 |
20 | ## Table of Contents
21 |
22 |
23 | - [Definitions](#definitions)
24 | - [Message Broker](#definitionsMessageBroker)
25 | - [Message](#definitionsMessage)
26 | - [Topic](#definitionstTopic)
27 | - [Process](#definitionsProcess)
28 | - [Producer](#definitionsProducer)
29 | - [Consumer](#definitionsConsumer)
30 | - [Topic Templating](#definitionsTopicTemplating)
31 | - [Specification](#specification)
32 | - [Format](#format)
33 | - [File Structure](#file-structure)
34 | - [Schema](#schema)
35 | - [AsyncAPI Object](#A2SObject)
36 | - [AsyncAPI Version String](#A2SVersionString)
37 | - [Info Object](#infoObject)
38 | - [Contact Object](#contactObject)
39 | - [License Object](#licenseObject)
40 | - [Host String](#hostString)
41 | - [Base Topic String](#baseTopicString)
42 | - [Schemes List](#schemesList)
43 | - [Topics Object](#topicsObject)
44 | - [Topic Item Object](#topicItemObject)
45 | - [Message Object](#messageObject)
46 | - [Tag Object](#tagObject)
47 | - [External Documentation Object](#externalDocumentationObject)
48 | - [Components Object](#componentsObject)
49 | - [Reference Object](#referenceObject)
50 | - [Schema Object](#schemaObject)
51 | - [XML Object](#xmlObject)
52 | - [Specification Extensions](#specificationExtensions)
53 |
54 |
55 |
56 | ## Definitions
57 |
58 | #### Message Broker
59 | A message broker is a system in charge of message exchange. It MAY provide additional features, such as message queueing, storage or processing.
60 |
61 | #### Message
62 | A message is a piece of information a process will send to a message broker. It MUST contain headers and payload.
63 |
64 | #### Topic
65 | A topic is a routing key used by the message broker to deliver messages to the subscribed processes. Depending on the protocol used, a message MAY include the topic in its headers.
66 |
67 | #### Process
68 | A process is any kind of computer program connected to a message broker. It MUST be a producer, a consumer or both.
69 |
70 | #### Producer
71 | A producer is a process publishing messages to a message broker.
72 |
73 | #### Consumer
74 | A consumer is a process subscribed to a message broker and consumes messages from it.
75 |
76 | #### Topic Templating
77 | Topic templating refers to the usage of curly braces ({}) to mark a section of a topic as replaceable.
78 |
79 | ## Specification
80 |
81 | ### Format
82 |
83 | The files describing the Asynchronous API in accordance with the AsyncAPI Specification are represented as JSON objects and conform to the JSON standards.
84 | YAML, being a superset of JSON, can be used as well to represent a A2S (AsyncAPI Specification) file.
85 |
86 | For example, if a field is said to have an array value, the JSON array representation will be used:
87 |
88 | ```json
89 | {
90 | "field" : [...]
91 | }
92 | ```
93 |
94 | While the API is described using JSON it does not impose a JSON input/output to the API itself.
95 |
96 | All field names in the specification are **case sensitive**.
97 |
98 | The schema exposes two types of fields.
99 | Fixed fields, which have a declared name, and Patterned fields, which declare a regex pattern for the field name.
100 | Patterned fields can have multiple occurrences as long as each has a unique name.
101 |
102 | In order to preserve the ability to round-trip between YAML and JSON formats, YAML version [1.2](http://www.yaml.org/spec/1.2/spec.html) is recommended along with some additional constraints:
103 |
104 | - Tags MUST be limited to those allowed by the [JSON Schema ruleset](http://www.yaml.org/spec/1.2/spec.html#id2803231)
105 | - Keys used in YAML maps MUST be limited to a scalar string, as defined by the YAML Failsafe schema ruleset
106 |
107 | ### File Structure
108 |
109 | The A2S representation of the API is made of a single file.
110 | However, parts of the definitions can be split into separate files, at the discretion of the user.
111 | This is applicable for `$ref` fields in the specification as follows from the [JSON Schema](http://json-schema.org) definitions.
112 |
113 | By convention, the AsyncAPI Specification (A2S) file is named `asyncapi.json` or `asyncapi.yaml`.
114 |
115 | ### Schema
116 |
117 | #### AsyncAPI Object
118 |
119 | This is the root document object for the API specification.
120 | It combines resource listing and API declaration together into one document.
121 |
122 | ##### Fixed Fields
123 |
124 | Field Name | Type | Description
125 | ---|:---:|---
126 | asyncapi | [AsyncAPI Version String](#A2SVersionString) | **Required.** Specifies the AsyncAPI Specification version being used. It can be used by tooling Specifications and clients to interpret the version. The structure shall be `major`.`minor`.`patch`, where `patch` versions _must_ be compatible with the existing `major`.`minor` tooling. Typically patch versions will be introduced to address errors in the documentation, and tooling should typically be compatible with the corresponding `major`.`minor` (1.0.*). Patch versions will correspond to patches of this document.
127 | info | [Info Object](#infoObject) | **Required.** Provides metadata about the API. The metadata can be used by the clients if needed.
128 | baseTopic | [BaseTopic String](#baseTopicString) | The base topic to the API.
129 | topics | [Topics Object](#topicsObject) | **Required.** The available topics and messages for the API.
130 | components | [Components Object](#componentsObject) | An element to hold various schemas for the specification.
131 | tags | [[Tag Object](#tagObject)] | A list of tags used by the specification with additional metadata. Each tag name in the list MUST be unique.
132 | externalDocs | [External Documentation Object](#externalDocumentationObject) | Additional external documentation.
133 |
134 |
135 | This object can be extended with [Specification Extensions](#specificationExtensions).
136 |
137 | #### AsyncAPI Version String
138 |
139 | The version string signifies the version of the AsyncAPI Specification that the document complies to.
140 | The format for this string _must_ be `major`.`minor`.`patch`. The `patch` _may_ be suffixed by a hyphen and extra alphanumeric characters.
141 |
142 | A `major`.`minor` shall be used to designate the AsyncAPI Specification version, and will be considered compatible with the AsyncAPI Specification specified by that `major`.`minor` version.
143 | The patch version will not be considered by tooling, making no distinction between `1.0.0` and `1.0.1`.
144 |
145 | In subsequent versions of the AsyncAPI Specification, care will be given such that increments of the `minor` version should not interfere with operations of tooling developed to a lower minor version. Thus a hypothetical `1.1.0` specification should be usable with tooling designed for `1.0.0`.
146 |
147 | #### Info Object
148 |
149 | The object provides metadata about the API.
150 | The metadata can be used by the clients if needed.
151 |
152 | ##### Fixed Fields
153 |
154 | Field Name | Type | Description
155 | ---|:---:|---
156 | title | `string` | **Required.** The title of the application.
157 | version | `string` | **Required** Provides the version of the application API (not to be confused with the specification version).
158 | description | `string` | A short description of the application. [CommonMark syntax](http://spec.commonmark.org/) can be used for rich text representation.
159 | termsOfService | `string` | A URL to the Terms of Service for the API.
160 | contact | [Contact Object](#contactObject) | The contact information for the exposed API.
161 | license | [License Object](#licenseObject) | The license information for the exposed API.
162 |
163 | This object can be extended with [Specification Extensions](#specificationExtensions).
164 |
165 | ##### Info Object Example:
166 |
167 | ```json
168 | {
169 | "title": "AsyncAPI Sample App",
170 | "description": "This is a sample server.",
171 | "termsOfService": "http://asyncapi.org/terms/",
172 | "contact": {
173 | "name": "API Support",
174 | "url": "http://www.asyncapi.org/support",
175 | "email": "support@asyncapi.org"
176 | },
177 | "license": {
178 | "name": "Apache 2.0",
179 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
180 | },
181 | "version": "1.0.1"
182 | }
183 | ```
184 |
185 | ```yaml
186 | title: AsyncAPI Sample App
187 | description: This is a sample server.
188 | termsOfService: http://asyncapi.org/terms/
189 | contact:
190 | name: API Support
191 | url: http://www.asyncapi.org/support
192 | email: support@asyncapi.org
193 | license:
194 | name: Apache 2.0
195 | url: http://www.apache.org/licenses/LICENSE-2.0.html
196 | version: 1.0.1
197 | ```
198 |
199 | #### Contact Object
200 |
201 | Contact information for the exposed API.
202 |
203 | ##### Fixed Fields
204 |
205 | Field Name | Type | Description
206 | ---|:---:|---
207 | name | `string` | The identifying name of the contact person/organization.
208 | url | `string` | The URL pointing to the contact information. MUST be in the format of a URL.
209 | email | `string` | The email address of the contact person/organization. MUST be in the format of an email address.
210 |
211 | This object can be extended with [Specification Extensions](#specificationExtensions).
212 |
213 | ##### Contact Object Example:
214 |
215 | ```json
216 | {
217 | "name": "API Support",
218 | "url": "http://www.example.com/support",
219 | "email": "support@example.com"
220 | }
221 | ```
222 |
223 | ```yaml
224 | name: API Support
225 | url: http://www.example.com/support
226 | email: support@example.com
227 | ```
228 |
229 | #### License Object
230 |
231 | License information for the exposed API.
232 |
233 | ##### Fixed Fields
234 |
235 | Field Name | Type | Description
236 | ---|:---:|---
237 | name | `string` | **Required.** The license name used for the API.
238 | url | `string` | A URL to the license used for the API. MUST be in the format of a URL.
239 |
240 | This object can be extended with [Specification Extensions](#specificationExtensions).
241 |
242 | ##### License Object Example:
243 |
244 | ```json
245 | {
246 | "name": "Apache 2.0",
247 | "url": "http://www.apache.org/licenses/LICENSE-2.0.html"
248 | }
249 | ```
250 |
251 | ```yaml
252 | name: Apache 2.0
253 | url: http://www.apache.org/licenses/LICENSE-2.0.html
254 | ```
255 |
256 |
257 | #### Host String
258 |
259 | The host (name or ip) of the target server. This MAY point to a message broker.
260 |
261 | ##### Host String Example:
262 |
263 | ```json
264 | {
265 | "host": "myapi.example.com"
266 | }
267 | ```
268 |
269 | ```yaml
270 | host: myapi.example.com
271 | ```
272 |
273 | #### Base Topic String
274 |
275 | The base topic to the API. You MAY use this field to avoid repeating the beginning of the topics.
276 |
277 |
278 | ##### Base Topic String Example:
279 |
280 | ```json
281 | {
282 | "baseTopic": "hitch.accounts"
283 | }
284 | ```
285 |
286 | ```yaml
287 | host: hitch.accounts
288 | ```
289 |
290 |
291 |
292 | #### Schemes List
293 |
294 | An array of the transfer protocol(s) the API supports. Values MUST be one (or more) of the following values:
295 |
296 | Value | Type | Description
297 | ---|:---:|---
298 | amqp | `string` | AMQP protocol.
299 | amqps | `string` | AMQP protocol over SSL/TLS.
300 | mqtt | `string` | MQTT protocol
301 | mqtts | `string` | MQTT protocol over SSL/TLS.
302 | ws | `string` | WebSockets protocol
303 | wss | `string` | WebSockets protocol over SSL/TLS.
304 | stomp | `string` | STOMP protocol.
305 | stomps | `string` | STOMP protocol over SSL/TLS.
306 |
307 |
308 | ##### Schemes List Example:
309 |
310 | ```json
311 | {
312 | "schemes": ["amqps", "mqtts"]
313 | }
314 | ```
315 |
316 | ```yaml
317 | host:
318 | - amqps
319 | - mqtts
320 | ```
321 |
322 |
323 | #### Topics Object
324 |
325 | Holds the relative paths to the individual topic and their operations.
326 | The topic is appended to the [`Base Topic`](#baseTopicString) in order to construct the full one.
327 |
328 | ##### Patterned Fields
329 |
330 | Field Pattern | Type | Description
331 | ---|:---:|---
332 | ^[^.]{topic} | [Topic Item Object](#topicItemObject) | A relative path to an individual topic. The field name MUST NOT begin with a dot. [Topic templating](#definitionsTopicTemplating) is allowed.
333 |
334 | This object can be extended with [Specification Extensions](#specificationExtensions).
335 |
336 | ##### Topics Object Example
337 |
338 | ```json
339 | {
340 | "accounts.1.0.event.user.signup": {
341 | "subscribe": {
342 | "$ref": "#/components/messages/userSignedUp"
343 | }
344 | }
345 | }
346 | ```
347 |
348 | ```yaml
349 | accounts.1.0.event.user.signup:
350 | subscribe:
351 | $ref: "#/components/messages/userSignedUp"
352 | ```
353 |
354 |
355 |
356 |
357 | #### Topic Item Object
358 |
359 | Describes the operations available on a single topic.
360 |
361 | ##### Fixed Fields
362 |
363 | Field Name | Type | Description
364 | ---|:---:|---
365 | $ref | `string` | Allows for an external definition of this topic item. The referenced structure MUST be in the format of a [Topic Item Object](#topicItemObject). If there are conflicts between the referenced definition and this Topic Item's definition, the behavior is *undefined*.
366 | subscribe | [Message Object](#messageObject) | A definition of the message a SUBSCRIBE operation will receive on this topic.
367 | publish | [Message Object](#messageObject) | A definition of the message a PUBLISH operation will receive on this topic.
368 |
369 | This object can be extended with [Specification Extensions](#specificationExtensions).
370 |
371 | ##### Topic Item Object Example
372 |
373 | ```json
374 | {
375 | "subscribe": {
376 | "summary": "A user signed up.",
377 | "description": "A longer description of the message",
378 | "payload": {
379 | "type": "object",
380 | "properties": {
381 | "user": {
382 | "$ref": "#/components/schemas/user"
383 | },
384 | "signup": {
385 | "$ref": "#/components/schemas/signup"
386 | }
387 | }
388 | }
389 | }
390 | }
391 | ```
392 |
393 | ```yaml
394 | subscribe:
395 | summary: A user signed up.
396 | description: A longer description of the message
397 | payload:
398 | type: object
399 | properties:
400 | user:
401 | $ref: "#/components/schemas/user"
402 | signup:
403 | $ref: "#/components/schemas/signup"
404 | ```
405 |
406 |
407 |
408 | #### Message Object
409 |
410 | Describes a message received on a given topic and operation.
411 |
412 | ##### Fixed Fields
413 |
414 | Field Name | Type | Description
415 | ---|:---:|---
416 | headers | [Schema Object](#schemaObject) | Definition of the message headers. It MAY or MAY NOT define the protocol headers.
417 | payload | [Schema Object](#schemaObject) | Definition of the message payload.
418 | summary | `string` | A short summary of what the message is about.
419 | description | `string` | A verbose explanation of the message. [CommonMark syntax](http://spec.commonmark.org/) can be used for rich text representation.
420 | tags | [`string`] | A list of tags for API documentation control. Tags can be used for logical grouping of messages.
421 | externalDocs | [External Documentation Object](#externalDocumentationObject) | Additional external documentation for this message.
422 |
423 | This object can be extended with [Specification Extensions](#specificationExtensions).
424 |
425 | ##### Message Object Example
426 |
427 | ```json
428 | {
429 | "summary": "Action to sign a user up.",
430 | "description": "A longer description",
431 | "tags": [
432 | { "name": "user" },
433 | { "name": "signup" },
434 | { "name": "register" }
435 | ],
436 | "headers": {
437 | "type": "object",
438 | "properties": {
439 | "qos": {
440 | "$ref": "#/components/schemas/MQTTQoSHeader"
441 | },
442 | "retainFlag": {
443 | "$ref": "#/components/schemas/MQTTRetainHeader"
444 | }
445 | }
446 | },
447 | "payload": {
448 | "type": "object",
449 | "properties": {
450 | "user": {
451 | "$ref": "#/components/schemas/userCreate"
452 | },
453 | "signup": {
454 | "$ref": "#/components/schemas/signup"
455 | }
456 | }
457 | }
458 | }
459 | ```
460 |
461 | ```yaml
462 | summary: Action to sign a user up.
463 | description: A longer description
464 | tags:
465 | - name: user
466 | - name: signup
467 | - name: register
468 | headers:
469 | type: object
470 | properties:
471 | qos:
472 | $ref: "#/components/schemas/MQTTQoSHeader"
473 | retainFlag:
474 | $ref: "#/components/schemas/MQTTRetainHeader"
475 | payload:
476 | type: object
477 | properties:
478 | user:
479 | $ref: "#/components/schemas/userCreate"
480 | signup:
481 | $ref: "#/components/schemas/signup"
482 | ```
483 |
484 |
485 |
486 |
487 |
488 |
489 | #### Tag Object
490 |
491 | Allows adding meta data to a single tag.
492 |
493 | ##### Fixed Fields
494 | Field Name | Type | Description
495 | ---|:---:|---
496 | name | `string` | **Required.** The name of the tag.
497 | description | `string` | A short description for the tag. [CommonMark syntax](http://spec.commonmark.org/) can be used for rich text representation.
498 | externalDocs | [External Documentation Object](#externalDocumentationObject) | Additional external documentation for this tag.
499 |
500 | This object can be extended with [Specification Extensions](#specificationExtensions).
501 |
502 | ##### Tag Object Example
503 |
504 | ```json
505 | {
506 | "name": "user",
507 | "description": "User-related messages"
508 | }
509 | ```
510 |
511 | ```yaml
512 | name: user
513 | description: User-related messages
514 | ```
515 |
516 |
517 |
518 |
519 |
520 | #### External Documentation Object
521 |
522 | Allows referencing an external resource for extended documentation.
523 |
524 | ##### Fixed Fields
525 |
526 | Field Name | Type | Description
527 | ---|:---:|---
528 | description | `string` | A short description of the target documentation. [CommonMark syntax](http://spec.commonmark.org/) can be used for rich text representation.
529 | url | `string` | **Required.** The URL for the target documentation. Value MUST be in the format of a URL.
530 |
531 | This object can be extended with [Specification Extensions](#specificationExtensions).
532 |
533 | ##### External Documentation Object Example
534 |
535 | ```json
536 | {
537 | "description": "Find more info here",
538 | "url": "https://example.com"
539 | }
540 | ```
541 |
542 | ```yaml
543 | description: Find more info here
544 | url: https://example.com
545 | ```
546 |
547 | #### Reference Object
548 |
549 | A simple object to allow referencing other components in the specification, internally and externally.
550 |
551 |
552 | The Reference Object is defined by [JSON Reference](https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03) and follows the same structure, behavior and rules.
553 |
554 | For this specification, reference resolution is done as defined by the JSON Reference specification
555 | and not by the JSON Schema specification.
556 |
557 | ##### Fixed Fields
558 | Field Name | Type | Description
559 | ---|:---:|---
560 | $ref | `string` | **Required.** The reference string.
561 |
562 | This object cannot be extended with additional properties and any properties added SHALL be ignored.
563 |
564 | ##### Reference Object Example
565 |
566 | ```json
567 | {
568 | "$ref": "#/components/schemas/Pet"
569 | }
570 | ```
571 |
572 | ```yaml
573 | $ref: '#/components/schemas/Pet'
574 | ```
575 |
576 | #### Components Object
577 |
578 | Holds a set of reusable objects for different aspects of the AsyncAPI specification.
579 | All objects defined within the components object will have no effect on the API unless they are explicitly referenced from properties outside the components object.
580 |
581 | ##### Fixed Fields
582 |
583 | Field Name | Type | Description
584 | ---|:---|---
585 | schemas | Map[`string`, [Schema Object](#schemaObject) \| [Reference Object](#referenceObject)] | An object to hold reusable [Schema Objects](#schemaObject).
586 | messages | Map[`string`, [Message Object](#messageObject) \| [Reference Object](#referenceObject)] | An object to hold reusable [Message Objects](#messageObject).
587 |
588 | This object can be extended with [Specification Extensions](#specificationExtensions).
589 |
590 | All the fixed fields declared above are objects that MUST use keys that match the regular expression: `^[a-zA-Z0-9\.\-_]+$`.
591 |
592 | Field Name Examples:
593 |
594 | ```
595 | User
596 | User_1
597 | User_Name
598 | user-name
599 | my.org.User
600 | ```
601 |
602 | ##### Components Object Example
603 |
604 | ```json
605 | "components": {
606 | "schemas": {
607 | "Category": {
608 | "type": "object",
609 | "properties": {
610 | "id": {
611 | "type": "integer",
612 | "format": "int64"
613 | },
614 | "name": {
615 | "type": "string"
616 | }
617 | }
618 | },
619 | "Tag": {
620 | "type": "object",
621 | "properties": {
622 | "id": {
623 | "type": "integer",
624 | "format": "int64"
625 | },
626 | "name": {
627 | "type": "string"
628 | }
629 | }
630 | }
631 | },
632 | "messages": {
633 | "userSignUp": {
634 | "summary": "Action to sign a user up.",
635 | "description": "Multiline description of what this action does.\nHere you have another line.\n",
636 | "tags": [
637 | {
638 | "name": "user"
639 | },
640 | {
641 | "name": "signup"
642 | }
643 | ],
644 | "headers": {
645 | "type": "object",
646 | "properties": {
647 | "qos": {
648 | "$ref": "#/components/schemas/MQTTQoSHeader"
649 | },
650 | "retainFlag": {
651 | "$ref": "#/components/schemas/MQTTRetainHeader"
652 | }
653 | }
654 | },
655 | "payload": {
656 | "type": "object",
657 | "properties": {
658 | "user": {
659 | "$ref": "#/components/schemas/userCreate"
660 | },
661 | "signup": {
662 | "$ref": "#/components/schemas/signup"
663 | }
664 | }
665 | }
666 | }
667 | }
668 | }
669 | ```
670 |
671 | ```yaml
672 | components:
673 | schemas:
674 | Category:
675 | type: object
676 | properties:
677 | id:
678 | type: integer
679 | format: int64
680 | name:
681 | type: string
682 | Tag:
683 | type: object
684 | properties:
685 | id:
686 | type: integer
687 | format: int64
688 | name:
689 | type: string
690 | messages:
691 | userSignUp:
692 | summary: Action to sign a user up.
693 | description: |
694 | Multiline description of what this action does.
695 | Here you have another line.
696 | tags:
697 | - name: user
698 | - name: signup
699 | headers:
700 | type: object
701 | properties:
702 | qos:
703 | $ref: "#/components/schemas/MQTTQoSHeader"
704 | retainFlag:
705 | $ref: "#/components/schemas/MQTTRetainHeader"
706 | payload:
707 | type: object
708 | properties:
709 | user:
710 | $ref: "#/components/schemas/userCreate"
711 | signup:
712 | $ref: "#/components/schemas/signup"
713 | ```
714 |
715 | #### Schema Object
716 |
717 | The Schema Object allows the definition of input and output data types.
718 | These types can be objects, but also primitives and arrays.
719 | This object is an extended subset of the [JSON Schema Specification Wright Draft 00](http://json-schema.org/).
720 |
721 | Further information about the properties can be found in [JSON Schema Core](https://tools.ietf.org/html/draft-wright-json-schema-00) and [JSON Schema Validation](https://tools.ietf.org/html/draft-wright-json-schema-validation-00).
722 | Unless stated otherwise, the property definitions follow the JSON Schema specification as referenced here.
723 |
724 | ##### Properties
725 |
726 | The following properties are taken directly from the JSON Schema definition and follow the same specifications:
727 |
728 | - title
729 | - multipleOf
730 | - maximum
731 | - exclusiveMaximum
732 | - minimum
733 | - exclusiveMinimum
734 | - maxLength
735 | - minLength
736 | - pattern (This string SHOULD be a valid regular expression, according to the [ECMA 262 regular expression](https://www.ecma-international.org/ecma-262/5.1/#sec-7.8.5) dialect)
737 | - maxItems
738 | - minItems
739 | - uniqueItems
740 | - maxProperties
741 | - minProperties
742 | - required
743 | - enum
744 |
745 | The following properties are taken from the JSON Schema definition but their definitions were adjusted to the AsyncAPI Specification.
746 | - type - Value MUST be a string. Multiple types via an array are not supported.
747 | - allOf - Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.
748 | - oneOf - Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.
749 | - anyOf - Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.
750 | - not - Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.
751 | - items - Value MUST be an object and not an array. Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema. `items` MUST be present if the `type` is `array`.
752 | - properties - Property definitions MUST be a [Schema Object](#schemaObject) and not a standard JSON Schema (inline or referenced).
753 | - additionalProperties - Value can be boolean or object. Inline or referenced schema MUST be of a [Schema Object](#schemaObject) and not a standard JSON Schema.
754 | - description - [CommonMark syntax](http://spec.commonmark.org/) can be used for rich text representation.
755 | - format - See [Data Type Formats](#dataTypeFormat) for further details. While relying on JSON Schema's defined formats, the AsyncAPI Specification offers a few additional predefined formats.
756 | - default - The default value represents what would be assumed by the consumer of the input as the value of the schema if one is not provided. Unlike JSON Schema, the value MUST conform to the defined type for the Schema Object defined at the same level. For example, of `type` is `string`, then `default` can be `"foo"` but cannot be `1`.
757 |
758 | Alternatively, any time a Schema Object can be used, a [Reference Object](#referenceObject) can be used in its place. This allows referencing definitions in place of defining them inline.
759 |
760 | Additional properties defined by the JSON Schema specification that are not mentioned here are strictly unsupported.
761 |
762 | Other than the JSON Schema subset fields, the following fields MAY be used for further schema documentation:
763 |
764 | ##### Fixed Fields
765 | Field Name | Type | Description
766 | ---|:---:|---
767 | nullable | `boolean` | Allows sending a `null` value for the defined schema. Default value is `false`.
768 | discriminator | `string` | Adds support for polymorphism. The discriminator is the schema property name that is used to differentiate between other schema that inherit this schema. The property name used MUST be defined at this schema and it MUST be in the `required` property list. When used, the value MUST be the name of this schema or any schema that inherits it. See [Composition and Inheritance](#schemaComposition) for more details.
769 | readOnly | `boolean` | Relevant only for Schema `"properties"` definitions. Declares the property as "read only". This means that it MAY be sent as part of a response but SHOULD NOT be sent as part of the request. If property is marked as `readOnly` being `true` and is in the `required` list, the `required` will take effect on the response only. A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`. Default value is `false`.
770 | writeOnly | `boolean` | Relevant only for Schema `"properties"` definitions. Declares the property as "write only". This means that it MAY be sent as part of a request but SHOULD NOT be sent as part of the response. If property is marked as `writeOnly` being `true` and is in the `required` list, the `required` will take effect on the request only. A property MUST NOT be marked as both `readOnly` and `writeOnly` being `true`. Default value is `false`.
771 | xml | [XML Object](#xmlObject) | This MAY be used only on properties schemas. It has no effect on root schemas. Adds Additional metadata to describe the XML representation format of this property.
772 | externalDocs | [External Documentation Object](#externalDocumentationObject) | Additional external documentation for this schema.
773 | example | Any | A free-form property to include an example of an instance for this schema. To represent examples that cannot naturally represented in JSON or YAML, a string value can be used to contain the example with escaping where necessary.
774 | deprecated | `boolean` | Specifies that a schema is deprecated and SHOULD be transitioned out of usage. Default value is `false`.
775 |
776 | This object can be extended with [Specification Extensions](#specificationExtensions).
777 |
778 | ###### Composition and Inheritance (Polymorphism)
779 |
780 | The AsyncAPI Specification allows combining and extending model definitions using the `allOf` property of JSON Schema, in effect offering model composition.
781 | `allOf` takes in an array of object definitions that are validated *independently* but together compose a single object.
782 |
783 | While composition offers model extensibility, it does not imply a hierarchy between the models.
784 | To support polymorphism, AsyncAPI Specification adds the support of the `discriminator` field.
785 | When used, the `discriminator` will be the name of the property used to decide which schema definition is used to validate the structure of the model.
786 | As such, the `discriminator` field MUST be a required field.
787 | There are are two ways to define the value of a discriminator for an inheriting instance.
788 | - Use the schema's name.
789 | - Override the schema's name by overriding the property with a new value. If exists, this takes precedence over the schema's name.
790 | As such, inline schema definitions, which do not have a given id, *cannot* be used in polymorphism.
791 |
792 | ###### XML Modeling
793 |
794 | The [xml](#schemaObjectXml) property allows extra definitions when translating the JSON definition to XML.
795 | The [XML Object](#xmlObject) contains additional information about the available options.
796 |
797 | ##### Schema Object Examples
798 |
799 | ###### Primitive Sample
800 |
801 | ```json
802 | {
803 | "type": "string",
804 | "format": "email"
805 | }
806 | ```
807 |
808 | ```yaml
809 | type: string
810 | format: email
811 | ```
812 |
813 | ###### Simple Model
814 |
815 | ```json
816 | {
817 | "type": "object",
818 | "required": [
819 | "name"
820 | ],
821 | "properties": {
822 | "name": {
823 | "type": "string"
824 | },
825 | "address": {
826 | "$ref": "#/components/schemas/Address"
827 | },
828 | "age": {
829 | "type": "integer",
830 | "format": "int32",
831 | "minimum": 0
832 | }
833 | }
834 | }
835 | ```
836 |
837 | ```yaml
838 | type: object
839 | required:
840 | - name
841 | properties:
842 | name:
843 | type: string
844 | address:
845 | $ref: '#/components/schemas/Address'
846 | age:
847 | type: integer
848 | format: int32
849 | minimum: 0
850 | ```
851 |
852 | ###### Model with Map/Dictionary Properties
853 |
854 | For a simple string to string mapping:
855 |
856 | ```json
857 | {
858 | "type": "object",
859 | "additionalProperties": {
860 | "type": "string"
861 | }
862 | }
863 | ```
864 |
865 | ```yaml
866 | type: object
867 | additionalProperties:
868 | type: string
869 | ```
870 |
871 | For a string to model mapping:
872 |
873 | ```json
874 | {
875 | "type": "object",
876 | "additionalProperties": {
877 | "$ref": "#/components/schemas/ComplexModel"
878 | }
879 | }
880 | ```
881 |
882 | ```yaml
883 | type: object
884 | additionalProperties:
885 | $ref: '#/components/schemas/ComplexModel'
886 | ```
887 |
888 | ###### Model with Example
889 |
890 | ```json
891 | {
892 | "type": "object",
893 | "properties": {
894 | "id": {
895 | "type": "integer",
896 | "format": "int64"
897 | },
898 | "name": {
899 | "type": "string"
900 | }
901 | },
902 | "required": [
903 | "name"
904 | ],
905 | "example": {
906 | "name": "Puma",
907 | "id": 1
908 | }
909 | }
910 | ```
911 |
912 | ```yaml
913 | type: object
914 | properties:
915 | id:
916 | type: integer
917 | format: int64
918 | name:
919 | type: string
920 | required:
921 | - name
922 | example:
923 | name: Puma
924 | id: 1
925 | ```
926 |
927 | ###### Models with Composition
928 |
929 | ```json
930 | {
931 | "schemas": {
932 | "ErrorModel": {
933 | "type": "object",
934 | "required": [
935 | "message",
936 | "code"
937 | ],
938 | "properties": {
939 | "message": {
940 | "type": "string"
941 | },
942 | "code": {
943 | "type": "integer",
944 | "minimum": 100,
945 | "maximum": 600
946 | }
947 | }
948 | },
949 | "ExtendedErrorModel": {
950 | "allOf": [
951 | {
952 | "$ref": "#/components/schemas/ErrorModel"
953 | },
954 | {
955 | "type": "object",
956 | "required": [
957 | "rootCause"
958 | ],
959 | "properties": {
960 | "rootCause": {
961 | "type": "string"
962 | }
963 | }
964 | }
965 | ]
966 | }
967 | }
968 | }
969 | ```
970 |
971 | ```yaml
972 | schemas:
973 | ErrorModel:
974 | type: object
975 | required:
976 | - message
977 | - code
978 | properties:
979 | message:
980 | type: string
981 | code:
982 | type: integer
983 | minimum: 100
984 | maximum: 600
985 | ExtendedErrorModel:
986 | allOf:
987 | - $ref: '#/components/schemas/ErrorModel'
988 | - type: object
989 | required:
990 | - rootCause
991 | properties:
992 | rootCause:
993 | type: string
994 | ```
995 |
996 | ###### Models with Polymorphism Support
997 |
998 | ```json
999 | {
1000 | "schemas": {
1001 | "Pet": {
1002 | "type": "object",
1003 | "discriminator": "petType",
1004 | "properties": {
1005 | "name": {
1006 | "type": "string"
1007 | },
1008 | "petType": {
1009 | "type": "string"
1010 | }
1011 | },
1012 | "required": [
1013 | "name",
1014 | "petType"
1015 | ]
1016 | },
1017 | "Cat": {
1018 | "description": "A representation of a cat. Note that `Cat` will be used as the discriminator value.",
1019 | "allOf": [
1020 | {
1021 | "$ref": "#/components/schemas/Pet"
1022 | },
1023 | {
1024 | "type": "object",
1025 | "properties": {
1026 | "huntingSkill": {
1027 | "type": "string",
1028 | "description": "The measured skill for hunting",
1029 | "enum": [
1030 | "clueless",
1031 | "lazy",
1032 | "adventurous",
1033 | "aggressive"
1034 | ]
1035 | }
1036 | },
1037 | "required": [
1038 | "huntingSkill"
1039 | ]
1040 | }
1041 | ]
1042 | },
1043 | "Dog": {
1044 | "description": "A representation of a dog. Note that `Dog` will be used as the discriminator value.",
1045 | "allOf": [
1046 | {
1047 | "$ref": "#/components/schemas/Pet"
1048 | },
1049 | {
1050 | "type": "object",
1051 | "properties": {
1052 | "packSize": {
1053 | "type": "integer",
1054 | "format": "int32",
1055 | "description": "the size of the pack the dog is from",
1056 | "minimum": 0
1057 | }
1058 | },
1059 | "required": [
1060 | "packSize"
1061 | ]
1062 | }
1063 | ]
1064 | }
1065 | }
1066 | }
1067 | ```
1068 |
1069 | ```yaml
1070 | schemas:
1071 | Pet:
1072 | type: object
1073 | discriminator: petType
1074 | properties:
1075 | name:
1076 | type: string
1077 | petType:
1078 | type: string
1079 | required:
1080 | - name
1081 | - petType
1082 | Cat: ## "Cat" will be used as the discriminator value
1083 | description: A representation of a cat
1084 | allOf:
1085 | - $ref: '#/components/schemas/Pet'
1086 | - type: object
1087 | properties:
1088 | huntingSkill:
1089 | type: string
1090 | description: The measured skill for hunting
1091 | enum:
1092 | - clueless
1093 | - lazy
1094 | - adventurous
1095 | - aggressive
1096 | required:
1097 | - huntingSkill
1098 | Dog: ## "Dog" will be used as the discriminator value
1099 | description: A representation of a dog
1100 | allOf:
1101 | - $ref: '#/components/schemas/Pet'
1102 | - type: object
1103 | properties:
1104 | packSize:
1105 | type: integer
1106 | format: int32
1107 | description: the size of the pack the dog is from
1108 | minimum: 0
1109 | required:
1110 | - packSize
1111 | ```
1112 |
1113 | #### XML Object
1114 |
1115 | A metadata object that allows for more fine-tuned XML model definitions.
1116 |
1117 | When using arrays, XML element names are *not* inferred (for singular/plural forms) and the `name` property SHOULD be used to add that information.
1118 | See examples for expected behavior.
1119 |
1120 | ##### Fixed Fields
1121 | Field Name | Type | Description
1122 | ---|:---:|---
1123 | name | `string` | Replaces the name of the element/attribute used for the described schema property. When defined within `items`, it will affect the name of the individual XML elements within the list. When defined alongside `type` being `array` (outside the `items`), it will affect the wrapping element and only if `wrapped` is `true`. If `wrapped` is `false`, it will be ignored.
1124 | namespace | `string` | The URL of the namespace definition. Value SHOULD be in the form of a URL.
1125 | prefix | `string` | The prefix to be used for the [name](#xmlObjectName).
1126 | attribute | `boolean` | Declares whether the property definition translates to an attribute instead of an element. Default value is `false`.
1127 | wrapped | `boolean` | MAY be used only for an array definition. Signifies whether the array is wrapped (for example, ``) or unwrapped (``). Default value is `false`. The definition takes effect only when defined alongside `type` being `array` (outside the `items`).
1128 |
1129 | This object can be extended with [Specification Extensions](#specificationExtensions).
1130 |
1131 | ##### XML Object Examples
1132 |
1133 | The examples of the XML object definitions are included inside a property definition of a [Schema Object](#schemaObject) with a sample of the XML representation of it.
1134 |
1135 | ###### No XML Element
1136 |
1137 | Basic string property:
1138 |
1139 | ```json
1140 | {
1141 | "animals": {
1142 | "type": "string"
1143 | }
1144 | }
1145 | ```
1146 |
1147 | ```yaml
1148 | animals:
1149 | type: string
1150 | ```
1151 |
1152 | ```xml
1153 | ...
1154 | ```
1155 |
1156 | Basic string array property ([`wrapped`](#xmlObjectWrapped) is `false` by default):
1157 |
1158 | ```json
1159 | {
1160 | "animals": {
1161 | "type": "array",
1162 | "items": {
1163 | "type": "string"
1164 | }
1165 | }
1166 | }
1167 | ```
1168 |
1169 | ```yaml
1170 | animals:
1171 | type: array
1172 | items:
1173 | type: string
1174 | ```
1175 |
1176 | ```xml
1177 | ...
1178 | ...
1179 | ...
1180 | ```
1181 |
1182 | ###### XML Name Replacement
1183 |
1184 | ```json
1185 | {
1186 | "animals": {
1187 | "type": "string",
1188 | "xml": {
1189 | "name": "animal"
1190 | }
1191 | }
1192 | }
1193 | ```
1194 |
1195 | ```yaml
1196 | animals:
1197 | type: string
1198 | xml:
1199 | name: animal
1200 | ```
1201 |
1202 | ```xml
1203 | ...
1204 | ```
1205 |
1206 |
1207 | ###### XML Attribute, Prefix and Namespace
1208 |
1209 | In this example, a full model definition is shown.
1210 |
1211 | ```json
1212 | {
1213 | "Person": {
1214 | "type": "object",
1215 | "properties": {
1216 | "id": {
1217 | "type": "integer",
1218 | "format": "int32",
1219 | "xml": {
1220 | "attribute": true
1221 | }
1222 | },
1223 | "name": {
1224 | "type": "string",
1225 | "xml": {
1226 | "namespace": "http://example.com/schema/sample",
1227 | "prefix": "sample"
1228 | }
1229 | }
1230 | }
1231 | }
1232 | }
1233 | ```
1234 |
1235 | ```yaml
1236 | Person:
1237 | type: object
1238 | properties:
1239 | id:
1240 | type: integer
1241 | format: int32
1242 | xml:
1243 | attribute: true
1244 | name:
1245 | type: string
1246 | xml:
1247 | namespace: http://example.com/schema/sample
1248 | prefix: sample
1249 | ```
1250 |
1251 | ```xml
1252 |
1253 | example
1254 |
1255 | ```
1256 |
1257 | ###### XML Arrays
1258 |
1259 | Changing the element names:
1260 |
1261 | ```json
1262 | {
1263 | "animals": {
1264 | "type": "array",
1265 | "items": {
1266 | "type": "string",
1267 | "xml": {
1268 | "name": "animal"
1269 | }
1270 | }
1271 | }
1272 | }
1273 | ```
1274 |
1275 | ```yaml
1276 | animals:
1277 | type: array
1278 | items:
1279 | type: string
1280 | xml:
1281 | name: animal
1282 | ```
1283 |
1284 | ```xml
1285 | value
1286 | value
1287 | ```
1288 |
1289 | The external `name` property has no effect on the XML:
1290 |
1291 | ```json
1292 | {
1293 | "animals": {
1294 | "type": "array",
1295 | "items": {
1296 | "type": "string",
1297 | "xml": {
1298 | "name": "animal"
1299 | }
1300 | },
1301 | "xml": {
1302 | "name": "aliens"
1303 | }
1304 | }
1305 | }
1306 | ```
1307 |
1308 | ```yaml
1309 | animals:
1310 | type: array
1311 | items:
1312 | type: string
1313 | xml:
1314 | name: animal
1315 | xml:
1316 | name: aliens
1317 | ```
1318 |
1319 | ```xml
1320 | value
1321 | value
1322 | ```
1323 |
1324 | Even when the array is wrapped, if no name is explicitly defined, the same name will be used both internally and externally:
1325 |
1326 | ```json
1327 | {
1328 | "animals": {
1329 | "type": "array",
1330 | "items": {
1331 | "type": "string"
1332 | },
1333 | "xml": {
1334 | "wrapped": true
1335 | }
1336 | }
1337 | }
1338 | ```
1339 |
1340 | ```yaml
1341 | animals:
1342 | type: array
1343 | items:
1344 | type: string
1345 | xml:
1346 | wrapped: true
1347 | ```
1348 |
1349 | ```xml
1350 |
1351 | value
1352 | value
1353 |
1354 | ```
1355 |
1356 | To overcome the above example, the following definition can be used:
1357 |
1358 | ```json
1359 | {
1360 | "animals": {
1361 | "type": "array",
1362 | "items": {
1363 | "type": "string",
1364 | "xml": {
1365 | "name": "animal"
1366 | }
1367 | },
1368 | "xml": {
1369 | "wrapped": true
1370 | }
1371 | }
1372 | }
1373 | ```
1374 |
1375 | ```yaml
1376 | animals:
1377 | type: array
1378 | items:
1379 | type: string
1380 | xml:
1381 | name: animal
1382 | xml:
1383 | wrapped: true
1384 | ```
1385 |
1386 | ```xml
1387 |
1388 | value
1389 | value
1390 |
1391 | ```
1392 |
1393 | Affecting both internal and external names:
1394 |
1395 | ```json
1396 | {
1397 | "animals": {
1398 | "type": "array",
1399 | "items": {
1400 | "type": "string",
1401 | "xml": {
1402 | "name": "animal"
1403 | }
1404 | },
1405 | "xml": {
1406 | "name": "aliens",
1407 | "wrapped": true
1408 | }
1409 | }
1410 | }
1411 | ```
1412 |
1413 | ```yaml
1414 | animals:
1415 | type: array
1416 | items:
1417 | type: string
1418 | xml:
1419 | name: animal
1420 | xml:
1421 | name: aliens
1422 | wrapped: true
1423 | ```
1424 |
1425 | ```xml
1426 |
1427 | value
1428 | value
1429 |
1430 | ```
1431 |
1432 | If we change the external element but not the internal ones:
1433 |
1434 | ```json
1435 | {
1436 | "animals": {
1437 | "type": "array",
1438 | "items": {
1439 | "type": "string"
1440 | },
1441 | "xml": {
1442 | "name": "aliens",
1443 | "wrapped": true
1444 | }
1445 | }
1446 | }
1447 | ```
1448 |
1449 | ```yaml
1450 | animals:
1451 | type: array
1452 | items:
1453 | type: string
1454 | xml:
1455 | name: aliens
1456 | wrapped: true
1457 | ```
1458 |
1459 | ```xml
1460 |
1461 | value
1462 | value
1463 |
1464 | ```
1465 |
1466 |
1467 |
1468 |
1469 |
1470 |
1471 |
1472 |
1473 |
1474 | ### Specification Extensions
1475 |
1476 | While the AsyncAPI Specification tries to accommodate most use cases, additional data can be added to extend the specification at certain points.
1477 |
1478 | The extensions properties are implemented as patterned fields that are always prefixed by `"x-"`.
1479 |
1480 | Field Pattern | Type | Description
1481 | ---|:---:|---
1482 | ^x- | Any | Allows extensions to the AsyncAPI Schema. The field name MUST begin with `x-`, for example, `x-internal-id`. The value can be `null`, a primitive, an array or an object. Can have any valid JSON format value.
1483 |
1484 | The extensions may or may not be supported by the available tooling, but those may be extended as well to add requested support (if tools are internal or open-sourced).
1485 |
1486 | ### Data Type Formats
1487 |
1488 | Primitives have an optional modifier property: `format`.
1489 | The AsyncAPI specification uses several known formats to more finely define the data type being used.
1490 | However, the `format` property is an open `string`-valued property, and can have any value to support documentation needs.
1491 | Formats such as `"email"`, `"uuid"`, etc., can be used even though they are not defined by this specification.
1492 | Types that are not accompanied by a `format` property follow their definition from the JSON Schema.
1493 | Tools that do not recognize a specific `format` MAY default back to the `type` alone, as if the `format` was not specified.
1494 |
1495 | The formats defined by the AsyncAPI Specification are:
1496 |
1497 |
1498 | Common Name | `type` | [`format`](#dataTypeFormat) | Comments
1499 | ----------- | ------ | -------- | --------
1500 | integer | `integer` | `int32` | signed 32 bits
1501 | long | `integer` | `int64` | signed 64 bits
1502 | float | `number` | `float` | |
1503 | double | `number` | `double` | |
1504 | string | `string` | | |
1505 | byte | `string` | `byte` | base64 encoded characters
1506 | binary | `string` | `binary` | any sequence of octets
1507 | boolean | `boolean` | | |
1508 | date | `string` | `date` | As defined by `full-date` - [RFC3339](http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14)
1509 | dateTime | `string` | `date-time` | As defined by `date-time` - [RFC3339](http://xml2rfc.ietf.org/public/rfc/html/rfc3339.html#anchor14)
1510 | password | `string` | `password` | Used to hint UIs the input needs to be obscured.
1511 |
--------------------------------------------------------------------------------