From b3e2fa340bd0343a273c6cde673ed04d44bf07d4 Mon Sep 17 00:00:00 2001 From: Pawel Psztyc Date: Thu, 20 Sep 2018 16:36:39 -0700 Subject: [PATCH] Updating CLI to new API --- .gitignore | 1 + lib/api-base.js | 131 ++++++-------------------------- lib/build.js | 32 ++++---- lib/generate-json.js | 118 +++++++++++++++++++++------- lib/serve.js | 7 +- package-lock.json | 96 ++++++++++++++++++----- package.json | 4 +- test/generate-json.test.js | 60 +++++++-------- test/serve.test.js | 5 +- test/test-apis/api-oas-20.json | 68 +++++++++++++++++ test/test-apis/api-oas-20.yaml | 19 +++++ test/test-apis/api-oas-30.yaml | 24 ++++++ test/test-apis/api-raml-08.raml | 9 +++ test/test-apis/api-raml-10.raml | 73 ++++++++++++++++++ 14 files changed, 435 insertions(+), 212 deletions(-) create mode 100644 test/test-apis/api-oas-20.json create mode 100644 test/test-apis/api-oas-20.yaml create mode 100644 test/test-apis/api-oas-30.yaml create mode 100644 test/test-apis/api-raml-08.raml create mode 100644 test/test-apis/api-raml-10.raml diff --git a/.gitignore b/.gitignore index f2cdc81..60f8797 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ node_modules bower_components build output +api-console-cli.log diff --git a/lib/api-base.js b/lib/api-base.js index 5e82df9..bd3adb5 100644 --- a/lib/api-base.js +++ b/lib/api-base.js @@ -1,9 +1,6 @@ 'use strict'; - -const fs = require('fs'); const path = require('path'); -const exec = require('child_process').exec; - +const winston = require('winston'); /** * Base class for CLI commands. * @@ -11,115 +8,37 @@ const exec = require('child_process').exec; class ApiBase { /** * Sets global options. + * + * @param {Object} opts Command options. */ constructor(opts) { - opts = opts || {}; - this.verbose = opts.verbose || false; - } - // Prints arguments to the console. - log() { - if (this.verbose) { - console.log.apply(console, arguments); + if (!opts) { + opts = {}; } + this.opts = opts; + this.logger = this.__setupLogger(); } - - /** - * Ensures that given path exists in filesystem. - * - * @param {String} path A path to test - * @return {Promise} Resolced promise when the path exists. Rejects when unable to create the - * path. - */ - ensurePath(path) { - return this.pathExists(path) - .catch((exists) => { - if (!exists) { - return this._createDir(path); - } - return Promise.resolve(); - }); - } - // Creates a directory recursively. - _createDir(dirPath) { - return new Promise((resolve, reject) => { - fs.mkdir(dirPath, (error) => { - if (error && error.code === 'ENOENT') { - this._createDir(path.dirname(dirPath)) - .then(() => { - return this._createDir(dirPath); - }) - .then(resolve) - .catch(reject); - } else if (error) { - reject(error); - } else { - resolve(); - } - }); - }); - } - /** - * Checks if `path` exists in the filesystem. - * - * @param {String} path A path to check - * @return {Promise} A promise resolves itself if `path` exists and rejects if don't. + * Creates a logger object to log debug output. + * @return {Object} */ - pathExists(path) { - return new Promise((resolve, reject) => { - fs.access(path, fs.constants.F_OK, (err) => { - if (err) { - return reject(); - } - resolve(); - }); + __setupLogger() { + const level = this.opts.verbose ? 'debug' : 'warn'; + this.debugFile = path.join(process.cwd(), 'api-console-cli.log'); + const format = winston.format.combine( + winston.format.colorize(), + winston.format.simple() + ); + const logger = winston.createLogger({ + level, + format, + exitOnError: false, + transports: [ + new winston.transports.Console(), + new winston.transports.File({filename: this.debugFile, level: 'error'}) + ] }); - } - - /** - * Execute shell command - * - * @param {String} cmd Command to execute - * @param {String?} dir A directoy where to execute the command. - * @return {Promise} Promise resolves itself if the command was executed successfully and - * rejects it there was an error. - */ - exec(cmd, dir) { - dir = dir || undefined; - return new Promise((resolve, reject) => { - var opts = {}; - if (dir) { - opts.cwd = dir; - } - this.log(`Executing command: ${cmd} in dir: ${dir}`); - exec(cmd, opts, (err, stdout, stderr) => { - if (err) { - let currentDir = process.cwd(); - if (opts.cwd) { - currentDir = opts.cwd; - } - reject(new Error('Unable to execute command: ' + err.message + - '. Was in dir: ' + currentDir + '. stdout: ', stdout, '. stderr: ', stderr)); - return; - } - resolve(stdout); - }); - }); - } - - // Checks if given `url` is a local or remote file. - isRemoteFile(url) { - if (!url) { - // current dir? - return false; - } - if (url.indexOf('http') === 0) { - return true; - } - if (url.indexOf('ftp') === 0) { - return true; - } - return false; + return logger; } } exports.ApiBase = ApiBase; diff --git a/lib/build.js b/lib/build.js index a86fb9d..506931b 100644 --- a/lib/build.js +++ b/lib/build.js @@ -1,34 +1,29 @@ 'use strict'; const ApiBase = require('./api-base').ApiBase; const consoleBuilder = require('api-console-builder'); +const fs = require('fs-extra'); /** - * The ApiBuild class is a command to build the API Console for specific API spec. + * The ApiBuild class is a command to build the API Console + * for specific API spec. */ class ApiBuild extends ApiBase { - /** - * Constructs the builder. - * @param {Object} opts Options passed from the command line. - */ - constructor(opts) { - opts = opts || {}; - super(opts); - this.opts = opts; - } - /** * Runs the command. + * + * @return {Promise} */ run() { - this.log(' Building the API console. This may take a moment.'); - const startTime = Date.now(); + this.logger.info(' Building the API console. This may take a moment.'); const moduleOptions = this._prepareOptions(this.opts); return consoleBuilder(moduleOptions) - .then(() => { - const time = Date.now() - startTime; - this.log(' Console built in ' + time + ' seconds.'); - }); + // If there's no error here then there's no point of leaving the debug file. + .then(() => fs.remove(this.debugFile)); } - + /** + * Creates configuration to be passed to the builder. + * @param {Object} opts User passed options + * @return {Object} Builder options + */ _prepareOptions(opts) { const result = {}; // API source options @@ -82,6 +77,7 @@ class ApiBuild extends ApiBase { if (opts.attributes && opts.attributes.length) { result.attributes = opts.attributes; } + result.logger = this.logger; return result; } } diff --git a/lib/generate-json.js b/lib/generate-json.js index 84caf74..c17a597 100644 --- a/lib/generate-json.js +++ b/lib/generate-json.js @@ -2,7 +2,7 @@ const amf = require('amf-client-js'); const fs = require('fs-extra'); const ApiBase = require('./api-base').ApiBase; - +const path = require('path'); /** * Builds a JSON file with the API definition out from the RAML file. */ @@ -14,50 +14,112 @@ class JsonGenerator extends ApiBase { * @param {Object} opts Options passed from the command line. */ constructor(apiFile, opts) { - opts = opts || {}; super(opts); if (!apiFile) { throw new Error('The apiFile argument is not specified.'); } - if (!opts.output) { - opts.output = './api-model.json'; + if (!this.opts.output) { + this.opts.output = './api-model.json'; } this.apiFile = apiFile; - this.apiType = opts.apiType; - this.verbose = opts.verbose; - this.output = opts.output; + this.apiType = this.opts.apiType; + this.output = this.opts.output; } - /** * Runs the command. + * + * @return {Promise} */ run() { - this.log( - 'Generating API model from ', this.apiFile, 'using', this.apiType, 'parser'); + let msg = 'Generating API model from ' + this.apiFile; + msg += ' using ' + this.apiType + ' parser'; + this.logger.info(msg); + amf.plugins.document.WebApi.register(); amf.plugins.document.Vocabularies.register(); amf.plugins.features.AMFValidation.register(); return amf.Core.init() - .then(() => { - const parser = amf.Core.parser(this.apiType, 'application/yaml'); - let url; - if (this.apiFile.indexOf('http') === 0) { - url = this.apiFile; + .then(() => this._parse(this.apiFile, this.apiType)) + .then((doc) => this._validate(doc, this.apiType)) + .then((doc) => this._resolve(doc, this.apiType)) + .then((doc) => this._save(doc, this.output)); + } + /** + * Parses API file to AMF graph. + * @param {String} location API file location + * @param {String} type API type. + * @return {Promise} Promise resolved to AMF model. + */ + _parse(location, type) { + this.logger.info('AMF ready.'); + this.logger.info('Running API parser...'); + const parser = amf.Core.parser(type, 'application/yaml'); + let url; + if (location.indexOf('http') === 0) { + url = location; + } else { + url = `file://${location}`; + } + return parser.parseFileAsync(url); + } + /** + * Validates API graph + * @param {Object} doc Parsed document + * @param {String} type API type. + * @return {Promise} Promise resolved to the same document. + */ + _validate(doc, type) { + this.logger.info('API parsed.'); + this.logger.info('Validating API...'); + let validateProfile; + switch (type) { + case 'RAML 1.0': validateProfile = amf.ProfileNames.RAML; break; + case 'RAML 0.8': validateProfile = amf.ProfileNames.RAML08; break; + case 'OAS 2.0': + case 'OAS 3.0': + validateProfile = amf.ProfileNames.OAS; + break; + } + return amf.AMF.validate(doc, validateProfile) + .then((report) => { + if (!report.conforms) { + this.logger.warn(report.toString()); } else { - url = `file://${this.apiFile}`; + this.logger.info('API valid.'); } - return parser.parseFileAsync(url); - }) - .then((doc) => { - this.log('API data parsed. Resolving model using "editing" pipeline.'); - const resolver = amf.Core.resolver('RAML 1.0'); - return resolver.resolve(doc, 'editing'); - }) - .then((model) => { - this.log('Storing API data model to file.', this.output); - const generator = amf.Core.generator('AMF Graph', 'application/ld+json'); - return generator.generateString(model) - .then((data) => fs.writeFile(this.output, data, 'utf8')); + return doc; + }); + } + /** + * Validates types in the model + * @param {Object} doc Parsed document + * @param {String} type API type. + * @return {Promise} Promise resolved to the resolved document. + */ + _resolve(doc, type) { + this.logger.info('Resolving API model for API components...'); + const resolver = amf.Core.resolver(type); + return resolver.resolve(doc, 'editing'); + } + /** + * Generates json-ld model and saves it to specified location. + * @param {Object} doc Document ot use to generate the model + * @param {String} file Output file location + * @return {Promise} Resolved when file is saved. + */ + _save(doc, file) { + this.logger.info('Generating json-ld model...'); + const opts = amf.render.RenderOptions().withSourceMaps.withCompactUris; + const generator = amf.Core.generator('AMF Graph', 'application/ld+json'); + const start = Date.now(); + return generator.generateString(doc, opts) + .then((data) => { + const time = Date.now() - start; + this.logger.info(`Model ready in ${time} milliseconds`); + this.logger.info('Storing API data model to file: ' + file); + const dir = path.dirname(file); + return fs.ensureDir(dir) + .then(() => fs.writeFile(file, data, 'utf8')); }); } } diff --git a/lib/serve.js b/lib/serve.js index 3b580a7..2725b6d 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -6,7 +6,6 @@ const url = require('url'); * Based on Polyserve library lightweigth www server to display build results. */ class ApiServe extends ApiBase { - /** * Constructs the builder. * @@ -18,11 +17,11 @@ class ApiServe extends ApiBase { } _applyOpts(options) { - var root = options.root; + let root = options.root; if (!root && options.args && options.args.length) { root = options.args[0]; } - var opts = {}; + const opts = {}; // Set protocol as http by default opts.protocol = 'http'; @@ -48,7 +47,7 @@ class ApiServe extends ApiBase { opts.openPath = options.openPath; } if (options.protocol) { - var possibleProtocols = ['http', 'https']; + const possibleProtocols = ['http', 'https']; opts.protocol = ~possibleProtocols.indexOf(options.protocol) ? options.protocol : 'http'; } diff --git a/package-lock.json b/package-lock.json index 8315910..d6b5317 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3657,10 +3657,9 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "dev": true, + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", + "integrity": "sha512-EglNDLRpmaTWiD/qraZn6HREAEAHJcJOmxNEYwq6xeMKnVMAy3GUcFB+wXt2C6k4CNvB/mP1y/U3dzvKKj5OtQ==", "requires": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -3671,7 +3670,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, "requires": { "graceful-fs": "^4.1.6" } @@ -5438,6 +5436,29 @@ "version": "4.2.23", "resolved": "https://registry.npmjs.org/@types/node/-/node-4.2.23.tgz", "integrity": "sha512-U6IchCNLRyswc9p6G6lxWlbE+KwAhZp6mGo6MD2yWpmFomhYmetK+c98OpKyvphNn04CU3aXeJrXdOqbXVTS/w==" + }, + "async": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", + "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + }, + "winston": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.4.tgz", + "integrity": "sha512-NBo2Pepn4hK4V01UfcWcDlmiVTs7VTB1h7bgnB0rgP146bYhMxX0ypCz3lBOfNxCO4Zuek7yeT+y/zM1OfMw4Q==", + "requires": { + "async": "~1.0.0", + "colors": "1.0.x", + "cycle": "1.0.x", + "eyes": "0.1.x", + "isstream": "0.1.x", + "stack-trace": "0.0.x" + } } } }, @@ -7579,27 +7600,60 @@ } }, "winston": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-2.4.0.tgz", - "integrity": "sha1-gIBQuT1SZh7Z+2wms/DIJnCLCu4=", - "requires": { - "async": "~1.0.0", - "colors": "1.0.x", - "cycle": "1.0.x", - "eyes": "0.1.x", - "isstream": "0.1.x", - "stack-trace": "0.0.x" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.1.0.tgz", + "integrity": "sha512-FsQfEE+8YIEeuZEYhHDk5cILo1HOcWkGwvoidLrDgPog0r4bser1lEIOco2dN9zpDJ1M88hfDgZvxe5z4xNcwg==", + "requires": { + "async": "^2.6.0", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^1.9.1", + "one-time": "0.0.4", + "readable-stream": "^2.3.6", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.2.0" }, "dependencies": { "async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.1.tgz", + "integrity": "sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ==", + "requires": { + "lodash": "^4.17.10" + } + }, + "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz", - "integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k=" + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, - "colors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", - "integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=" + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==" + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } } } }, diff --git a/package.json b/package.json index faa902a..e55725f 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,9 @@ "commander": "^2.18.0", "polyserve": "^0.27.12", "semver": "^5.5.1", - "update-notifier": "^2.5.0" + "update-notifier": "^2.5.0", + "winston": "3.1.0", + "fs-extra": "7.0.0" }, "devDependencies": { "chai": "^3.5.0", diff --git a/test/generate-json.test.js b/test/generate-json.test.js index 0247d02..93f128c 100644 --- a/test/generate-json.test.js +++ b/test/generate-json.test.js @@ -3,46 +3,44 @@ const {JsonGenerator} = require('../lib/generate-json'); const assert = require('chai').assert; const fs = require('fs-extra'); +const path = require('path'); -const RAML_FILE = 'test/api.raml'; -const RAML_TYPE = 'RAML 1.0'; -const DEFAULT_JSON_FILE = './api-model.json'; -const OTHER_JSON_FILE = './api-other.json'; +const workingDir = path.join('test', 'parsing-test'); -describe('api-console-cli', function() { - describe('JsonGenerator', function() { - let currentApiFile; - it('Throws when API file is not defined', function() { - assert.throws(function() { - new JsonGenerator(); - }); - }); +describe('JsonGenerator', function() { + this.timeout(500000); - afterEach(() => { - if (currentApiFile) { - return fs.remove(currentApiFile); - } + it('Throws when API file is not defined', function() { + assert.throws(function() { + new JsonGenerator(); }); + }); - it('Generates the default file', function() { - const generator = new JsonGenerator(RAML_FILE, { - apiType: RAML_TYPE - }); - currentApiFile = DEFAULT_JSON_FILE; - return generator.run() - .then(() => fs.pathExists(DEFAULT_JSON_FILE)) - .then((exists) => assert.isTrue(exists)); + [ + ['RAML 0.8', 'api-raml-08.raml', 'YAML'], + ['RAML 1.0', 'api-raml-10.raml', 'YAML'], + ['OAS 2.0', 'api-oas-20.json', 'JSON'], + ['OAS 2.0', 'api-oas-20.yaml', 'YAML'], + ['OAS 3.0', 'api-oas-30.yaml', 'YAML'] + ].forEach((item) => { + after(function() { + return fs.remove(workingDir); }); - it('Generates specific file', function() { - currentApiFile = OTHER_JSON_FILE; - const generator = new JsonGenerator(RAML_FILE, { - output: OTHER_JSON_FILE, - apiType: RAML_TYPE + it('Generates from: ' + item[0] + ', format: ' + item[2], function() { + const output = path.join(workingDir, 'api-model.json'); + const generator = new JsonGenerator('test/test-apis/' + item[1], { + apiType: item[0], + output, + verbose: true }); return generator.run() - .then(() => fs.pathExists(OTHER_JSON_FILE)) - .then((exists) => assert.isTrue(exists)); + .then(() => fs.readJson(output)) + .then((model) => { + assert.typeOf(model, 'array', 'Model is saved'); + const api = model[0]; + assert.typeOf(api['@context'], 'object', 'Model is compact'); + }); }); }); }); diff --git a/test/serve.test.js b/test/serve.test.js index f059b45..15e9f3b 100644 --- a/test/serve.test.js +++ b/test/serve.test.js @@ -6,9 +6,8 @@ const assert = require('chai').assert; describe('api-console-cli', () => { describe('ApiServe', () => { describe('_applyOpts()', () => { - - const args = ['root', 'entrypoint', 'port', 'hostname', 'open', 'openPath']; - + const args = ['root', 'entrypoint', 'port', 'hostname', + 'open', 'openPath']; args.forEach((argument) => { it(`Sets ${argument} from option argument`, function() { const opts = {}; diff --git a/test/test-apis/api-oas-20.json b/test/test-apis/api-oas-20.json new file mode 100644 index 0000000..3fbf3bb --- /dev/null +++ b/test/test-apis/api-oas-20.json @@ -0,0 +1,68 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team" + }, + "license": { + "name": "MIT" + } + }, + "host": "petstore.swagger.io", + "basePath": "/api", + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + } + }, + "definitions": { + "Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } +} diff --git a/test/test-apis/api-oas-20.yaml b/test/test-apis/api-oas-20.yaml new file mode 100644 index 0000000..30125ae --- /dev/null +++ b/test/test-apis/api-oas-20.yaml @@ -0,0 +1,19 @@ +swagger: "2.0" +info: + title: Sample API + description: API description in Markdown. + version: 1.0.0 +host: api.example.com +basePath: /v1 +schemes: + - https +paths: + /users: + get: + summary: Returns a list of users. + description: Optional extended description in Markdown. + produces: + - application/json + responses: + 200: + description: OK diff --git a/test/test-apis/api-oas-30.yaml b/test/test-apis/api-oas-30.yaml new file mode 100644 index 0000000..06d50b4 --- /dev/null +++ b/test/test-apis/api-oas-30.yaml @@ -0,0 +1,24 @@ +openapi: 3.0.0 +info: + title: Sample API + description: Optional multiline or single-line description in [CommonMark](http://commonmark.org/help/) or HTML. + version: 0.1.9 +servers: + - url: http://api.example.com/v1 + description: Optional server description, e.g. Main (production) server + - url: http://staging-api.example.com + description: Optional server description, e.g. Internal staging server for testing +paths: + /users: + get: + summary: Returns a list of users. + description: Optional extended description in CommonMark or HTML. + responses: + '200': # status code + description: A JSON array of user names + content: + application/json: + schema: + type: array + items: + type: string diff --git a/test/test-apis/api-raml-08.raml b/test/test-apis/api-raml-08.raml new file mode 100644 index 0000000..2d740a5 --- /dev/null +++ b/test/test-apis/api-raml-08.raml @@ -0,0 +1,9 @@ +#%RAML 0.8 +--- +title: Jukebox API +baseUri: http://jukebox.api.com +version: v1 + +/songs: + get: + description: Get the file content diff --git a/test/test-apis/api-raml-10.raml b/test/test-apis/api-raml-10.raml new file mode 100644 index 0000000..b0f944e --- /dev/null +++ b/test/test-apis/api-raml-10.raml @@ -0,0 +1,73 @@ +#%RAML 1.0 +title: TestApi +version: v1 +baseUri: http://{environment}.api.domain.com/{version}/ +mediaType: [ application/json, application/xml ] + +baseUriParameters: + environment: + type: string + description: | + API environment. The value can be one of "development", "stage" or "production" + Development environment is avaibale for dev keys (client id). + Stage is available internally only and keys are whitelisted for this environment. Keys that are not whitelisted will always return 404 for any call. + Production is available for redular keys (klient ids). + pattern: (development|stage|production) + example: production +documentation: + - title: Read this! + content: | + # This is an example API spec + The API doesn't exists in the real world therefore calls made to any endpoint will always fail. + If you'd like to perform actual request and see the response try GitHub API (which doesn't require user authentication in some endpoints) or other APIs. + Note that you may need a Client ID or valid authorization token to perform a call to some APIs that are secured by the OAuth 2 protocol. + Thank you for testing the API console. Your feedback is welcome. Email us: arc@mulesoft.com + - title: Test docs + content: | + # A test documentation. + This text was created by ARC's RAML editor. + You probably see this because you are testing ARC's web components and this component + is responsible for displaying a documentation from the RAML definition. + Play around with the element and use it in your project. + Please, note the licensing information available in every ARC component. + If you have any question email me: arc@mulesoft.com + Or slack me (internally only): Pawel Psztyc (P3) +types: + ErrorResource: + description: A response that is errored + type: object + properties: + error: + type: boolean + required: true + example: true + default: true + description: Indicate that the response is errored. + message: + type: string + description: The error message associated with the error. + example: <> + required: true + Feature: + description: A feature to test enum values in the URI parameters. + type: string + enum: + - A + - B + - C +/test-parameters/{feature}: + uriParameters: + feature: + type: string + enum: + - A + - B + - C + get: + description: To test enum values in the URI parameters for inline type declaration. + /{typeFeature}: + uriParameters: + typeFeature: + type: Feature + get: + description: To test enum values in the URI parameters for global type declaration.