├── 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 | ![](https://example.com) 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 | [![Build status](https://travis-ci.org/Mermade/mdv.svg?branch=master)](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 | --------------------------------------------------------------------------------