├── .nvmrc ├── .gitattributes ├── test ├── indexof │ └── object1.raml ├── world-music-api │ ├── schemas │ │ ├── idea.raml │ │ └── songs.xsd │ ├── examples │ │ └── songs.xml │ ├── secured │ │ └── accessToken.raml │ └── libraries │ │ ├── songs-library.raml │ │ └── api-library.raml ├── indexof.raml ├── oom.js ├── consistent-headers.raml ├── typeexample.raml ├── typeexample.flatObject.raml ├── examples-not-polluted.raml ├── zeropointeight.spec.js ├── array-of-strings.raml ├── array-of-foo.raml ├── output-json.js ├── outofmemory.raml ├── resourceexample.raml ├── typeexample.spec.js ├── consistent-headers.spec.js ├── inheritance.raml ├── array-of-strings.spec.js ├── indexof.spec.js ├── array-of-foo.spec.js ├── examples-not-polluted.spec.js ├── zeropointeight.raml ├── helloworld.raml ├── resourceexample.spec.js ├── secured-by.raml ├── consistent-headers.json ├── typeexample.flatObject.spec.js ├── indexof.json ├── inheritance.spec.js ├── parameters.raml ├── consistent-examples.raml ├── secured-by.spec.js ├── consistent-examples.spec.js ├── helloworld.spec.js ├── helloworld-as-buffer.spec.js ├── helloworld-as-string.spec.js ├── helloworld.json ├── array-of-strings.json ├── worldmusic.raml ├── typeexample.json ├── secured-by.json ├── parameters.spec.js ├── typeexample.flatObject.json ├── worldmusic.spec.js ├── array-of-foo.json ├── resourceexample.json ├── parameters.json └── inheritance.json ├── .gitignore ├── .editorconfig ├── .eslintrc.js ├── .github └── workflows │ └── tests.yml ├── CONTRIBUTING.md ├── LICENSE ├── package.json ├── README.md ├── arrays-objects-helpers.js ├── consistency-helpers.js ├── changelog.md └── index.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 4.7.0 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js text eol=lf 2 | *.raml text eol=lf -------------------------------------------------------------------------------- /test/indexof/object1.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 DataType 2 | 3 | type: object 4 | properties: 5 | o1a: string 6 | -------------------------------------------------------------------------------- /test/world-music-api/schemas/idea.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 DataType 2 | 3 | properties: 4 | comment: string 5 | -------------------------------------------------------------------------------- /test/world-music-api/examples/songs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | str1234 4 | str1234 5 | 6 | -------------------------------------------------------------------------------- /test/indexof.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Test 3 | version: v1 4 | mediaType: application/json 5 | /test: 6 | get: 7 | body: 8 | type: !include indexof/object1.raml 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules 16 | .vscode 17 | -------------------------------------------------------------------------------- /test/oom.js: -------------------------------------------------------------------------------- 1 | const raml2obj = require('..'); 2 | 3 | raml2obj.parse('outofmemory.raml').then( 4 | () => { 5 | console.log('DONE'); 6 | }, 7 | error => { 8 | console.log('error', error); 9 | } 10 | ); 11 | -------------------------------------------------------------------------------- /test/world-music-api/secured/accessToken.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 Trait 2 | 3 | usage: | 4 | This trait can be used to apply an access token query parameter 5 | to any resources or HTTP methods. 6 | queryParameters: 7 | access_token: string 8 | -------------------------------------------------------------------------------- /test/consistent-headers.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Consistent Headers 3 | 4 | 5 | /A: 6 | get: 7 | headers: 8 | X-Some-Header: string 9 | responses: 10 | 200: 11 | headers: 12 | X-Some-Other-Header: string -------------------------------------------------------------------------------- /test/typeexample.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Test 3 | types: 4 | objectid: 5 | description: A valid MongoDB object id. 6 | example: 7 | value: 576bca8b70fddb149c4a9e92 8 | /company: 9 | uriParameters: 10 | id: 11 | type: objectid 12 | get: 13 | description: bla 14 | -------------------------------------------------------------------------------- /test/typeexample.flatObject.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Test 3 | types: 4 | objectid: 5 | description: A valid MongoDB object id. 6 | example: 7 | value: 576bca8b70fddb149c4a9e92 8 | objectType: 9 | description: a type with properties. 10 | properties: 11 | first: 12 | type: string 13 | second: 14 | type: number 15 | /company: 16 | uriParameters: 17 | id: 18 | type: objectid 19 | get: 20 | description: bla 21 | -------------------------------------------------------------------------------- /test/examples-not-polluted.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Test examples are not polluted with previous usages 3 | types: 4 | TypeId: 5 | description: The request identifier. 6 | type: integer 7 | example: 123 8 | 9 | /a/{aId}: 10 | uriParameters: 11 | aId: 12 | type: TypeId 13 | example: 456 14 | get: 15 | description: bla 16 | 17 | /b/{bId}: 18 | uriParameters: 19 | bId: 20 | type: TypeId 21 | example: 789 22 | get: 23 | description: bla -------------------------------------------------------------------------------- /test/world-music-api/schemas/songs.xsd: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /test/zeropointeight.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | it('should not support 0.8 files at all', done => { 10 | raml2obj.parse('test/zeropointeight.raml').then(null, error => { 11 | assert.strictEqual( 12 | error.message, 13 | '_sourceToRamlObj: only RAML 1.0 is supported!' 14 | ); 15 | done(); 16 | }); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'eslint:recommended', 4 | ], 5 | plugins: [ 6 | 'prettier', 7 | ], 8 | env: { 9 | es6: true, 10 | browser: false, 11 | node: true, 12 | commonjs: true, 13 | }, 14 | parserOptions: { 15 | ecmaVersion: 7, 16 | }, 17 | rules: { 18 | 'prettier/prettier': ['error', {trailingComma: 'es5', singleQuote: true}], 19 | 'no-console': 'off', 20 | 'prefer-const': 'error', 21 | 'eqeqeq': 'error', 22 | 'no-useless-return': 'error', 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /test/array-of-strings.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: API used to test broken features 3 | version: v1 4 | baseUri: http://api.samplehost.com/broken/{version} 5 | mediaType: application/json 6 | protocols: [HTTP, HTTPS] 7 | 8 | /: 9 | get: 10 | responses: 11 | 200: 12 | body: 13 | type: string[] 14 | example: ["somevalue","anothervalue"] 15 | post: 16 | responses: 17 | 200: 18 | body: 19 | type: array 20 | items: string 21 | example: ["somevalue","anothervalue"] 22 | -------------------------------------------------------------------------------- /test/world-music-api/libraries/songs-library.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 Library 2 | 3 | types: 4 | Song: 5 | properties: 6 | title: string 7 | length: number 8 | examples: 9 | song1: 10 | strict: false 11 | value: 12 | title: "My Song" 13 | length: 12 14 | song2: 15 | title: "Last" 16 | length: 3 17 | Album: 18 | properties: 19 | title: string 20 | songs: Song[] 21 | Musician: 22 | properties: 23 | name: string 24 | discography: (Song | Album)[] 25 | -------------------------------------------------------------------------------- /test/array-of-foo.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Test 3 | description: Test of array support 4 | version: v1 5 | baseUri: 6 | value: https://exmaple.com/{version} 7 | protocols: HTTPS 8 | mediaType: application/json 9 | 10 | types: 11 | Foo: 12 | properties: 13 | id: string 14 | Foos: Foo[] 15 | 16 | /foos: 17 | get: 18 | responses: 19 | 200: 20 | body: Foos 21 | post: 22 | responses: 23 | 200: 24 | body: 25 | type: array 26 | items: Foo 27 | minItems: 1 28 | maxItems: 200 29 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | push: 5 | branches: [ develop ] 6 | pull_request: 7 | branches: [ '*' ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | 13 | strategy: 14 | matrix: 15 | node-version: [10.x, 12.x, 14.x, 15.x] 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Use Node.js ${{ matrix.node-version }} 20 | uses: actions/setup-node@v1 21 | with: 22 | node-version: ${{ matrix.node-version }} 23 | 24 | - run: npm ci 25 | - run: npm test 26 | -------------------------------------------------------------------------------- /test/output-json.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const fs = require('fs'); 4 | const raml2obj = require('..'); 5 | const glob = require('glob'); 6 | 7 | process.chdir(__dirname); 8 | 9 | const ramlFiles = glob 10 | .sync('*.raml') 11 | .filter( 12 | ramlFile => 13 | ramlFile !== 'zeropointeight.raml' && ramlFile !== 'outofmemory.raml' 14 | ); 15 | 16 | ramlFiles.forEach(ramlFile => { 17 | console.log(ramlFile); 18 | raml2obj.parse(ramlFile).then( 19 | result => { 20 | const jsonString = JSON.stringify(result, null, 4); 21 | const filename = ramlFile.replace('.raml', '.json'); 22 | fs.writeFileSync(filename, jsonString); 23 | }, 24 | error => { 25 | console.log(ramlFile, error); 26 | } 27 | ); 28 | }); 29 | -------------------------------------------------------------------------------- /test/outofmemory.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: some API 3 | baseUri: https://someserver.com/api 4 | mediaType: application/json 5 | 6 | types: 7 | SomeProfile: 8 | type: object 9 | properties: 10 | attr1: string? 11 | attr2: string? 12 | attr3: string? 13 | attr4: string? 14 | attr5: string? 15 | attr6: string? 16 | attr7: string? 17 | attr8: string? 18 | attr9: string? 19 | attr10: string? 20 | attr11: string? 21 | attr12: string? 22 | attr13: string? 23 | attr14: string? 24 | attr15: string? 25 | 26 | ManyProfiles: 27 | type: object 28 | properties: 29 | profiles?: SomeProfile 30 | 31 | /api: 32 | get: 33 | responses: 34 | 200: 35 | body: 36 | type: ManyProfiles 37 | -------------------------------------------------------------------------------- /test/resourceexample.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: resource example 3 | 4 | types: 5 | ResourceExampleType: 6 | type: object 7 | properties: 8 | content: string 9 | additionalProperties: false 10 | examples: 11 | typeExample1: { "content": "typeExample1" } 12 | typeExample2: { "content": "typeExample2" } 13 | 14 | 15 | 16 | /example: 17 | get: 18 | responses: 19 | 200: 20 | body: 21 | application/json: 22 | type: ResourceExampleType 23 | 202: 24 | body: 25 | application/json: 26 | type: ResourceExampleType 27 | examples: 28 | resourceExample1: { "content": "resourceExample1" } 29 | resourceExample2: { "content": "resourceExample2" } 30 | 31 | -------------------------------------------------------------------------------- /test/typeexample.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('typeexample.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/typeexample.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should test the array items', () => { 25 | const uriParameter = obj.resources[0].uriParameters[0]; 26 | assert.strictEqual( 27 | uriParameter.description, 28 | 'A valid MongoDB object id.' 29 | ); 30 | assert.strictEqual( 31 | uriParameter.examples[0].value, 32 | '576bca8b70fddb149c4a9e92' 33 | ); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/consistent-headers.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('consistent-headers.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/consistent-headers.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should have keys on both request and response headers', () => { 25 | assert.strictEqual( 26 | obj.resources[0].methods[0].headers[0].key, 27 | 'X-Some-Header' 28 | ); 29 | assert.strictEqual( 30 | obj.resources[0].methods[0].responses[0].headers[0].key, 31 | 'X-Some-Other-Header' 32 | ); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /test/inheritance.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Inheritance Test 3 | 4 | types: 5 | Email: 6 | type: string 7 | pattern: ^.+@.+\..+$ 8 | Account: 9 | description: A basic account in the inheritance test 10 | properties: 11 | name: string 12 | email: Email 13 | gender: 14 | type: string 15 | enum: [ "male", "female" ] 16 | required: false 17 | PasswordProtectedAccount: 18 | description: An account which is password protected. 19 | type: Account 20 | properties: 21 | password: string 22 | BannableAccount: 23 | type: PasswordProtectedAccount 24 | properties: 25 | banned: boolean 26 | 27 | /account: 28 | displayName: ACCOUNT 29 | 30 | post: 31 | description: | 32 | Creates a new account 33 | body: 34 | application/json: 35 | type: PasswordProtectedAccount 36 | put: 37 | body: 38 | application/json: 39 | type: BannableAccount 40 | -------------------------------------------------------------------------------- /test/array-of-strings.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('array-of-strings.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/array-of-strings.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should test the array items', () => { 25 | let body; 26 | 27 | body = obj.resources[0].methods[0].responses[0].body[0]; 28 | assert.strictEqual(body.type, 'array'); 29 | assert.strictEqual(body.items, 'string'); 30 | 31 | body = obj.resources[0].methods[1].responses[0].body[0]; 32 | assert.strictEqual(body.type, 'array'); 33 | assert.strictEqual(body.items, 'string'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/indexof.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('indexof.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/indexof.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should test the basic properties of the raml object', () => { 25 | assert.strictEqual(obj.title, 'Test'); 26 | assert.strictEqual(obj.resources.length, 1); 27 | }); 28 | 29 | it('should test the GET /test method', () => { 30 | const GET = obj.resources[0].methods[0]; 31 | const body = GET.body[0]; 32 | assert.strictEqual(body.type.displayName, 'type'); 33 | assert.strictEqual(body.type.properties[0].name, 'o1a'); 34 | }); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /test/array-of-foo.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('array-of-foo.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/array-of-foo.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should test the array items', () => { 25 | let body; 26 | 27 | body = obj.resources[0].methods[0].responses[0].body[0]; 28 | assert.strictEqual(body.type, 'array'); 29 | assert.strictEqual(body.items.displayName, 'Foo'); 30 | 31 | body = obj.resources[0].methods[1].responses[0].body[0]; 32 | assert.strictEqual(body.type, 'array'); 33 | assert.strictEqual(body.items.displayName, 'Foo'); 34 | assert.strictEqual(body.minItems, 1); 35 | assert.strictEqual(body.maxItems, 200); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | raml2obj is an open source project and your contribution is very much appreciated. Since raml2obj is 3 | maintained by a very limited number of people, we ask you to please keep these rules in mind. 4 | 5 | ## Questions 6 | If you have a question, please use [StackOverflow](http://stackoverflow.com/). 7 | 8 | ## Bugs 9 | If you found a bug, instead of creating a new issue, please create a failing unit test and send in a pull request. 10 | It would be even better if you could include the fix as well :) 11 | 12 | ## Workflow 13 | 1. Fork the repository on Github and make your changes on the **develop** branch (or branch off of it). 14 | 3. Add unit tests, run them with `npm run test` 15 | 3. Run `npm run lint` before committing to check for common problems and auto format all code. 16 | 4. Send a pull request (with the develop branch as the target). 17 | 18 | ## Thanks 19 | A big thank you goes out to everyone who helped with the project, the [contributors](https://github.com/raml2html/raml2obj/graphs/contributors) 20 | and everyone who took the time to report issues and give feedback. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kevin Renskers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /test/examples-not-polluted.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('examples-not-polluted.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/examples-not-polluted.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('examples should not be polluted with previous usages', () => { 25 | // Take the first value example to verify is not in the type examples 26 | const [ramlExample] = obj.resources.map( 27 | rsc => rsc.methods[0].allUriParameters[0].examples[0].value 28 | ); 29 | // Take all type examples 30 | const typeExamples = obj.types.TypeId.examples.map( 31 | example => example.value 32 | ); 33 | typeExamples.forEach(typeExample => { 34 | assert.notStrictEqual(ramlExample, typeExample); 35 | }); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /test/zeropointeight.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | title: Hello world # required title 3 | version: 1 4 | documentation: 5 | - title: Welcome 6 | content: | 7 | Welcome to the Example Documentation. The Example API allows you 8 | to do stuff. See also [example.com](https://www.example.com). 9 | - title: Chapter two 10 | content: | 11 | More content here. Including **bold** text! 12 | 13 | /helloworld: # optional resource 14 | get: # HTTP method declaration 15 | responses: # declare a response 16 | 200: # HTTP status code 17 | body: # declare content of response 18 | application/json: # media type 19 | schema: | # structural definition of a response (schema or type) 20 | { 21 | "title": "Hello world Response", 22 | "type": "object", 23 | "properties": { 24 | "message": { 25 | "type": "string" 26 | } 27 | } 28 | } 29 | example: | # example how a response looks like 30 | { 31 | "message": "Hello world" 32 | } 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "raml2obj", 3 | "description": "RAML to object", 4 | "version": "6.8.1", 5 | "author": { 6 | "name": "Kevin Renskers", 7 | "email": "kevin@loopwerk.io" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/raml2html/raml2obj/issues" 11 | }, 12 | "dependencies": { 13 | "datatype-expansion": "^0.4.1", 14 | "raml-1-parser": "^1.1.67" 15 | }, 16 | "devDependencies": { 17 | "eslint": "^6.8.0", 18 | "eslint-plugin-prettier": "^3.1.4", 19 | "glob": "^7.1.6", 20 | "mocha": "^6.2.3", 21 | "prettier": "^1.19.1" 22 | }, 23 | "homepage": "https://github.com/raml2html/raml2obj", 24 | "keywords": [ 25 | "RAML" 26 | ], 27 | "license": "MIT", 28 | "main": "index.js", 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/raml2html/raml2obj.git" 32 | }, 33 | "preferGlobal": false, 34 | "scripts": { 35 | "json": "node test/output-json.js", 36 | "lint": "eslint . --fix", 37 | "test": "mocha 'test/*.spec.js' --reporter progress" 38 | }, 39 | "files": [ 40 | "index.js", 41 | "arrays-objects-helpers.js", 42 | "consistency-helpers.js" 43 | ], 44 | "engines": { 45 | "node": ">=4" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /test/helloworld.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Hello world # required title 3 | version: 1 4 | baseUri: http://example.com/{version} 5 | documentation: 6 | - title: Welcome 7 | content: | 8 | Welcome to the Example Documentation. The Example API allows you 9 | to do stuff. See also [example.com](https://www.example.com). 10 | - title: Chapter two 11 | content: | 12 | More content here. Including **bold** text! 13 | 14 | /helloworld: # optional resource 15 | description: This is the top level description for /helloworld. 16 | 17 | get: # HTTP method declaration 18 | responses: # declare a response 19 | 200: # HTTP status code 20 | body: # declare content of response 21 | application/json: # media type 22 | type: | # structural definition of a response (schema or type) 23 | { 24 | "title": "Hello world Response", 25 | "type": "object", 26 | "properties": { 27 | "message": { 28 | "type": "string" 29 | } 30 | } 31 | } 32 | example: # example how a response looks like 33 | { 34 | "message": "Hello world" 35 | } 36 | /test: 37 | displayName: TEST 38 | get: 39 | description: a sub resource 40 | -------------------------------------------------------------------------------- /test/resourceexample.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('resourceexample.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/resourceexample.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should have examples from types when no examples in resource', () => { 25 | const method = obj.resources[0].methods[0]; 26 | const response = method.responses[0]; 27 | const examples = response.body[0].examples; 28 | assert.strictEqual(response.code, '200'); 29 | assert.strictEqual(examples.length, 2); 30 | assert.deepEqual(examples[0].structuredValue, { 31 | content: 'typeExample1', 32 | }); 33 | assert.deepEqual(examples[1].structuredValue, { 34 | content: 'typeExample2', 35 | }); 36 | }); 37 | 38 | it('should have examples from resources when given', () => { 39 | const method = obj.resources[0].methods[0]; 40 | const response = method.responses[1]; 41 | const examples = response.body[0].examples; 42 | assert.strictEqual(response.code, '202'); 43 | assert.strictEqual(examples.length, 2); 44 | assert.deepEqual(examples[0].structuredValue, { 45 | content: 'resourceExample1', 46 | }); 47 | assert.deepEqual(examples[1].structuredValue, { 48 | content: 'resourceExample2', 49 | }); 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /test/secured-by.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Secured By Null 3 | 4 | securitySchemes: 5 | oauth_2_0: 6 | type: OAuth 2.0 7 | describedBy: 8 | headers: 9 | Authorization: string 10 | responses: 11 | 401: 12 | description: Invalid or expired token. 13 | settings: 14 | accessTokenUri: /token 15 | authorizationGrants: [ client_credentials ] 16 | oauth_2_0_withscopes: 17 | type: OAuth 2.0 18 | describedBy: 19 | headers: 20 | Authorization: string 21 | responses: 22 | 401: 23 | description: Invalid or expired token. 24 | settings: 25 | accessTokenUri: /token 26 | authorizationGrants: [ client_credentials ] 27 | scopes: [ add-a, remove-a, add-b, remove-b, read-c ] 28 | custom_scheme: 29 | description: | 30 | A custom security scheme for authenticating requests. 31 | type: x-custom 32 | describedBy: 33 | headers: 34 | SpecialToken: 35 | description: | 36 | Used to send a custom token. 37 | type: string 38 | responses: 39 | 401: 40 | description: | 41 | Bad token. 42 | 403: 43 | 44 | /A: 45 | get: 46 | post: 47 | securedBy: [ ] 48 | put: 49 | securedBy: [ null ] 50 | delete: 51 | securedBy: [ null, null, null ] 52 | 53 | /B: 54 | get: 55 | securedBy: [ oauth_2_0 ] 56 | post: 57 | securedBy: [ oauth_2_0, null ] 58 | delete: 59 | securedBy: [ oauth_2_0_withscopes: { scopes: [ remove-b ] }, null ] 60 | 61 | /C: 62 | get: 63 | securedBy: [ oauth_2_0_withscopes: { scopes: [ read-c ] }, custom_scheme ] 64 | -------------------------------------------------------------------------------- /test/world-music-api/libraries/api-library.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 Library 2 | 3 | types: 4 | RamlDataType: 5 | type: object 6 | properties: 7 | propString: string 8 | propStringArray1: string[] 9 | ideas: 10 | type: array 11 | items: !include ../schemas/idea.raml 12 | extIdeas: 13 | type: !include ../schemas/idea.raml 14 | properties: 15 | createdBy: string 16 | feedback: 17 | type: string 18 | default: "very nice" 19 | example: "very well made" 20 | minLength: 1 21 | maxLength: 255 22 | pattern: "[a-zA-Z\\s]*" 23 | propNumber: 24 | type: number 25 | minimum: 0 26 | maximum: 32 27 | format: int32 28 | multipleOf: 2 29 | propInteger: 30 | type: integer 31 | minimum: 3 32 | maximum: 5 33 | format: int8 34 | multipleOf: 1 35 | propBoolean: boolean 36 | propDate: 37 | type: date-only 38 | example: 2015-05-23 39 | userPicture: 40 | type: file 41 | fileTypes: ['image/jpeg', 'image/png'] 42 | maxLength: 307200 43 | NilValue: 44 | type: object 45 | properties: 46 | name: 47 | comment: string? 48 | CatOrDog: Cat | Dog 49 | CatAndDog: [ Cat, Dog ] 50 | PossibleMeetingDate: 51 | type: CustomDate 52 | noHolidays: true 53 | Cat: 54 | type: object 55 | properties: 56 | name: string 57 | color: string 58 | Dog: 59 | type: object 60 | properties: 61 | name: string 62 | fangs: string 63 | CustomDate: 64 | type: date-only 65 | facets: 66 | onlyFutureDates?: boolean # optional in `PossibleMeetingDate` 67 | noHolidays: boolean # required in `PossibleMeetingDate` 68 | -------------------------------------------------------------------------------- /test/consistent-headers.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Consistent Headers", 3 | "resources": [ 4 | { 5 | "methods": [ 6 | { 7 | "headers": [ 8 | { 9 | "name": "X-Some-Header", 10 | "displayName": "X-Some-Header", 11 | "typePropertyKind": "TYPE_EXPRESSION", 12 | "type": "string", 13 | "required": true, 14 | "key": "X-Some-Header" 15 | } 16 | ], 17 | "responses": [ 18 | { 19 | "code": "200", 20 | "headers": [ 21 | { 22 | "name": "X-Some-Other-Header", 23 | "displayName": "X-Some-Other-Header", 24 | "typePropertyKind": "TYPE_EXPRESSION", 25 | "type": "string", 26 | "required": true, 27 | "key": "X-Some-Other-Header" 28 | } 29 | ], 30 | "key": "200" 31 | } 32 | ], 33 | "method": "get", 34 | "allUriParameters": [] 35 | } 36 | ], 37 | "relativeUri": "/A", 38 | "displayName": "/A", 39 | "relativeUriPathSegments": [ 40 | "A" 41 | ], 42 | "absoluteUri": "/A", 43 | "parentUrl": "", 44 | "uniqueId": "a", 45 | "allUriParameters": [] 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /test/typeexample.flatObject.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('typeexample.flatObject.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj 14 | .parse('test/typeexample.flatObject.raml', { 15 | collectionFormat: 'arrays', 16 | }) 17 | .then( 18 | result => { 19 | obj = result; 20 | done(); 21 | }, 22 | error => { 23 | console.log('error', error); 24 | } 25 | ); 26 | }); 27 | 28 | it('should test the structure if the option "arraysToObjects" is set to "flatObjects"', () => { 29 | assert.strictEqual( 30 | // check that it's really an array without keys 31 | Object.prototype.toString.call(obj.types), 32 | '[object Array]' 33 | ); 34 | assert.strictEqual( 35 | // check that it's really an Object 36 | Object(obj), 37 | obj 38 | ); 39 | assert.strictEqual( 40 | // check that we can directly access a property inside the object (this is the actual difference the flag makes) 41 | obj.types[0].name, 42 | 'objectid' 43 | ); 44 | assert.strictEqual( 45 | // check that the type key is in a property inside the object 46 | obj.types[0].key, 47 | 'objectid' 48 | ); 49 | assert.strictEqual( 50 | // check that the second type is still the second 51 | obj.types[1].key, 52 | 'objectType' 53 | ); 54 | assert.strictEqual( 55 | // check that the second type's second property is still the second 56 | obj.types[1].properties[1].key, 57 | 'second' 58 | ); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/indexof.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Test", 3 | "version": "v1", 4 | "mediaType": "application/json", 5 | "resources": [ 6 | { 7 | "methods": [ 8 | { 9 | "body": [ 10 | { 11 | "name": "application/json", 12 | "displayName": "application/json", 13 | "typePropertyKind": "INPLACE", 14 | "type": { 15 | "name": "type", 16 | "displayName": "type", 17 | "typePropertyKind": "TYPE_EXPRESSION", 18 | "type": "object", 19 | "properties": [ 20 | { 21 | "name": "o1a", 22 | "displayName": "o1a", 23 | "typePropertyKind": "TYPE_EXPRESSION", 24 | "type": "string", 25 | "required": true, 26 | "key": "o1a" 27 | } 28 | ] 29 | }, 30 | "key": "application/json" 31 | } 32 | ], 33 | "method": "get", 34 | "allUriParameters": [] 35 | } 36 | ], 37 | "relativeUri": "/test", 38 | "displayName": "/test", 39 | "relativeUriPathSegments": [ 40 | "test" 41 | ], 42 | "absoluteUri": "/test", 43 | "parentUrl": "", 44 | "uniqueId": "test", 45 | "allUriParameters": [] 46 | } 47 | ] 48 | } -------------------------------------------------------------------------------- /test/inheritance.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('inheritance.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/inheritance.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should test the basic properties of the raml object', () => { 25 | assert.strictEqual(obj.title, 'Inheritance Test'); 26 | assert.strictEqual(obj.resources.length, 1); 27 | }); 28 | 29 | it('should test type inheritance', () => { 30 | const properties = obj.resources[0].methods[0].body[0].properties; 31 | 32 | assert.strictEqual(properties.length, 4); 33 | assert.strictEqual(properties[0].displayName, 'name'); 34 | assert.strictEqual(properties[0].type, 'string'); 35 | 36 | assert.strictEqual(properties[1].displayName, 'email'); 37 | assert.strictEqual(properties[1].type, 'string'); 38 | assert.strictEqual(properties[1].pattern, '^.+@.+\\..+$'); 39 | 40 | assert.strictEqual(properties[2].displayName, 'gender'); 41 | assert.strictEqual(properties[2].type, 'string'); 42 | assert.strictEqual(properties[2].enum.length, 2); 43 | 44 | assert.strictEqual(properties[3].displayName, 'password'); 45 | assert.strictEqual(properties[3].type, 'string'); 46 | }); 47 | 48 | it('should test description of descendants', () => { 49 | const methods = obj.resources[0].methods; 50 | 51 | assert.strictEqual( 52 | methods[0].body[0].description, 53 | 'An account which is password protected.' 54 | ); 55 | assert.strictEqual( 56 | methods[1].body[0].description, 57 | 'An account which is password protected.' 58 | ); 59 | }); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /test/parameters.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Lots of parameters 3 | 4 | /account: 5 | post: 6 | body: 7 | application/json: 8 | examples: 9 | example1: 10 | value: 11 | { 12 | "email": "john@example.com", 13 | "password": "super_secret", 14 | "name": "John Doe" 15 | } 16 | responses: 17 | 200: 18 | description: Account was created and user is now logged in 19 | 20 | /find: 21 | get: 22 | queryParameters: 23 | name: 24 | description: name on account 25 | required: true 26 | example: Naruto Uzumaki 27 | gender: 28 | enum: ["male", "female"] 29 | required: true 30 | number: 31 | type: integer 32 | default: 42 33 | 34 | /{id}: 35 | uriParameters: 36 | id: 37 | type: string 38 | description: account identifier 39 | minLength: 1 40 | maxLength: 10 41 | 42 | get: 43 | headers: 44 | Authorization: 45 | type: string 46 | description: Basic authentication header 47 | example: | 48 | Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== 49 | 50 | put: 51 | body: 52 | application/x-www-form-urlencoded: 53 | properties: 54 | name: 55 | description: name on account 56 | type: string 57 | examples: 58 | example1: Naruto Uzumaki 59 | example2: 60 | value: Kevin Renskers 61 | gender: 62 | enum: ["male", "female"] 63 | 64 | delete: 65 | description: Delete the account 66 | 67 | /{id}: 68 | uriParameters: 69 | id: 70 | type: string 71 | description: sub account identifier 72 | minLength: 1 73 | maxLength: 10 74 | get: 75 | responses: 76 | 401: 77 | description: Not authorized 78 | headers: 79 | WWW-Authenticate: 80 | type: string 81 | description: user was not authorized 82 | example: | 83 | WWW-Authenticate: Basic realm="raml2html" 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RAML to object 2 | 3 | [![NPM version](http://img.shields.io/npm/v/raml2obj.svg)](https://www.npmjs.org/package/raml2obj) 4 | [![Prettier](https://img.shields.io/badge/code%20style-prettier-blue.svg?style=flat)](https://github.com/prettier/prettier) 5 | 6 | A thin wrapper around [raml-js-parser-2](https://github.com/raml-org/raml-js-parser-2), adding extra properties to the resulting 7 | object for use in [raml2html](https://www.npmjs.org/package/raml2html) and [raml2md](https://www.npmjs.org/package/raml2md). 8 | 9 | Versions 4.0.0 and up only support RAML 1.x files. If you still have RAML 0.8 source files, please stick with raml2obj 3. 10 | 11 | ## Install 12 | ``` 13 | npm i raml2obj --save 14 | ``` 15 | 16 | ## Usage 17 | ```js 18 | var raml2obj = require('raml2obj'); 19 | 20 | // source can either be a filename, url, or parsed RAML object. 21 | // Returns a promise. 22 | raml2obj.parse(source).then(function(ramlObj) { 23 | // Do something with the resulting ramlObj :) 24 | }); 25 | ``` 26 | 27 | ## Options 28 | The `parse()` function can be called with options to customize the result. 29 | Defaults are compatible with `raml2html`. 30 | 31 | ```js 32 | raml2obj.parse(source, { 33 | validate: true, 34 | extensionsAndOverlays : [], 35 | collectionFormat: 'arrays', 36 | }).then(function(ramlObj) { 37 | // Do something with the resulting ramlObj :) 38 | }); 39 | ``` 40 | * `validate`: triggers the `rejectOnErrors` flag of the underlying parser. defaults to `false` 41 | * `extensionsAndOverlays`: Defaults to `[]`. See parser documentation. 42 | * `collectionFormat`: choose what data structure the double-nested `[{name1: {..}}, {name2: {..}}]` patterns of the `raml-1-parser` are transformed to in the output object: 43 | 44 | | `collectionFormat` value | output | 45 | | --- | --- | 46 | |`objects` (*default*)|`{name1: { orderHint: 0, ..}, name2: { orderHint: 1, ..}}` (eases e.g. property access). *Applies to top-level collections only, nested are arrays except type properties.*| 47 | |`arrays`|`[ {key: "name1", ..}, {key: "name2", ..}]` (eases e.g. representation in a database). *Applies recursively everywhere.* | 48 | 49 | 50 | ## Questions & Support 51 | Do you have a question? Have you found a bug or would you like to request a feature? Please check out [`CONTRIBUTING.md`](CONTRIBUTING.md). 52 | 53 | 54 | ## License 55 | raml2obj is available under the MIT license. See the LICENSE file for more info. 56 | -------------------------------------------------------------------------------- /arrays-objects-helpers.js: -------------------------------------------------------------------------------- 1 | function _isObject(obj) { 2 | return obj === Object(obj); 3 | } 4 | 5 | // EXAMPLE INPUT: 6 | // { 7 | // foo: { 8 | // name: "foo!" 9 | // }, 10 | // bar: { 11 | // name: "bar" 12 | // } 13 | // } 14 | // 15 | // EXAMPLE OUTPUT: 16 | // [ { name: "foo!", key: "foo" }, { name: "bar", key: "bar" } ] 17 | function _objectToArray(obj) { 18 | if (Array.isArray(obj)) { 19 | return obj; 20 | } 21 | 22 | return Object.keys(obj).map(key => { 23 | if (_isObject(obj[key])) { 24 | obj[key].key = key; 25 | } 26 | return obj[key]; 27 | }); 28 | } 29 | 30 | // EXAMPLE INPUT: 31 | // [ 32 | // { foo: { ... } }, 33 | // { bar: { ... } }, 34 | // ] 35 | // 36 | // EXAMPLE OUTPUT: 37 | // { foo: { orderHint: 0, ... }, bar: { orderHint: 1, ... } } 38 | function _arrayToObject(arr) { 39 | return arr.reduce((acc, cur, idx) => { 40 | Object.keys(cur).forEach(key => { 41 | acc[key] = cur[key]; 42 | acc[key].orderHint = idx; 43 | }); 44 | return acc; 45 | }, {}); 46 | } 47 | 48 | // PUBLIC 49 | 50 | function recursiveObjectToArray(obj) { 51 | if (_isObject(obj)) { 52 | Object.keys(obj).forEach(key => { 53 | const value = obj[key]; 54 | if ( 55 | _isObject(obj) && 56 | [ 57 | 'responses', 58 | 'body', 59 | 'queryParameters', 60 | 'headers', 61 | 'properties', 62 | 'baseUriParameters', 63 | 'annotations', 64 | 'uriParameters', 65 | ].indexOf(key) !== -1 66 | ) { 67 | obj[key] = _objectToArray(value); 68 | } 69 | 70 | recursiveObjectToArray(value); 71 | }); 72 | } else if (Array.isArray(obj)) { 73 | obj.forEach(value => { 74 | recursiveObjectToArray(value); 75 | }); 76 | } 77 | 78 | return obj; 79 | } 80 | 81 | // Transform some TOP LEVEL properties from objects to simple arrays 82 | function objectsToArrays(ramlObj) { 83 | [ 84 | 'types', 85 | 'traits', 86 | 'resourceTypes', 87 | 'annotationTypes', 88 | 'securitySchemes', 89 | ].forEach(key => { 90 | if (ramlObj[key]) { 91 | ramlObj[key] = _objectToArray(ramlObj[key]); 92 | ramlObj[key].sort((first, second) => { 93 | first.orderHint - second.orderHint; 94 | }); 95 | } 96 | }); 97 | 98 | return ramlObj; 99 | } 100 | 101 | // Transform some TOP LEVEL properties from arrays to simple objects 102 | function arraysToObjects(ramlObj) { 103 | [ 104 | 'types', 105 | 'traits', 106 | 'resourceTypes', 107 | 'annotationTypes', 108 | 'securitySchemes', 109 | ].forEach(key => { 110 | if (ramlObj[key]) { 111 | ramlObj[key] = _arrayToObject(ramlObj[key]); 112 | } 113 | }); 114 | 115 | return ramlObj; 116 | } 117 | 118 | module.exports = { 119 | arraysToObjects, 120 | recursiveObjectToArray, 121 | objectsToArrays, 122 | }; 123 | -------------------------------------------------------------------------------- /test/consistent-examples.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: Consistent Examples 3 | 4 | types: 5 | HasFullExamples: 6 | properties: 7 | SomeField: string 8 | examples: 9 | firstExample: 10 | displayName: First Example! 11 | description: It's the first example 12 | value: 13 | SomeField: Uno 14 | secondExample: 15 | displayName: Second Example! 16 | description: It's the second example 17 | value: 18 | SomeField: Duo 19 | 20 | HasJsonExample: 21 | properties: 22 | TestField: string 23 | example: | 24 | { 25 | "TestField": "This example is defined in JSON." 26 | } 27 | 28 | NestedExample: 29 | properties: 30 | ChildObject: 31 | type: Nestee 32 | Message: 33 | type: string 34 | example: I'm layered, bro 35 | 36 | Nestee: 37 | properties: 38 | HereIsANumber: 39 | type: number 40 | HereIsAString: 41 | type: string 42 | example: 43 | displayName: Sneaky nested example 44 | description: It's sneaky! 45 | value: 46 | HereIsANumber: 42 47 | HereIsAString: Strange strong string 48 | 49 | 50 | /FullExamples: 51 | get: 52 | responses: 53 | 200: 54 | body: 55 | application/json: 56 | type: HasFullExamples 57 | 58 | /JsonExample: 59 | get: 60 | responses: 61 | 200: 62 | body: 63 | application/json: 64 | type: HasJsonExample 65 | 66 | /HeaderExample: 67 | get: 68 | headers: 69 | X-MyImportantHeader: 70 | type: string 71 | example: Very Important 72 | X-MyDescriptiveHeader: 73 | type: string 74 | example: 75 | displayName: Baloon Assertion 76 | description: This descriptive header example 77 | value: Red baloons are known to be the colour red. 78 | 79 | /UriExample/{Id}/{Mood}: 80 | uriParameters: 81 | Id: 82 | type: string 83 | example: ABC123 84 | Mood: 85 | type: string 86 | example: 87 | displayName: How are ya 88 | description: ARE YOU ANGRY 89 | value: Serene. 90 | get: 91 | 92 | /QueryParamsExample: 93 | get: 94 | queryParameters: 95 | Temperature: 96 | type: number 97 | description: In celsius you savages 98 | example: 99 | displayName: Comfortable 100 | description: Best combined with a sunny day 101 | value: 24 102 | 103 | /NestedExample: 104 | get: 105 | responses: 106 | 200: 107 | body: 108 | application/json: 109 | type: NestedExample -------------------------------------------------------------------------------- /test/secured-by.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('secured-by.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/secured-by.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should remove securedBy', () => { 25 | const A = obj.resources[0]; 26 | const get = A.methods[0]; 27 | const post = A.methods[1]; 28 | const put = A.methods[2]; 29 | const del = A.methods[3]; 30 | 31 | assert.strictEqual(get.securedBy, undefined); 32 | assert.strictEqual(post.securedBy, undefined); 33 | assert.strictEqual(put.securedBy, undefined); 34 | assert.strictEqual(del.securedBy, undefined); 35 | }); 36 | 37 | it('should make securedBy consistent', () => { 38 | const B = obj.resources[1]; 39 | 40 | assert.strictEqual(B.methods[0].securedBy.constructor, Array); 41 | assert.strictEqual(B.methods[0].securedBy.length, 1); 42 | assert.strictEqual( 43 | B.methods[0].securedBy[0], 44 | Object(B.methods[0].securedBy[0]) 45 | ); 46 | assert.strictEqual(B.methods[0].securedBy[0].schemeName, 'oauth_2_0'); 47 | 48 | assert.strictEqual(B.methods[1].securedBy.constructor, Array); 49 | assert.strictEqual(B.methods[1].securedBy.length, 2); 50 | assert.strictEqual( 51 | B.methods[1].securedBy[0], 52 | Object(B.methods[1].securedBy[0]) 53 | ); 54 | assert.strictEqual(B.methods[1].securedBy[0].schemeName, 'oauth_2_0'); 55 | assert.strictEqual(B.methods[1].securedBy[1], null); 56 | 57 | assert.strictEqual(B.methods[2].securedBy.constructor, Array); 58 | assert.strictEqual(B.methods[2].securedBy.length, 2); 59 | assert.strictEqual( 60 | B.methods[2].securedBy[0], 61 | Object(B.methods[2].securedBy[0]) 62 | ); 63 | assert.strictEqual( 64 | B.methods[2].securedBy[0].schemeName, 65 | 'oauth_2_0_withscopes' 66 | ); 67 | assert.strictEqual(B.methods[2].securedBy[0].scopes.constructor, Array); 68 | assert.strictEqual(B.methods[2].securedBy[0].scopes.length, 1); 69 | assert.strictEqual(B.methods[2].securedBy[0].scopes[0], 'remove-b'); 70 | assert.strictEqual(B.methods[2].securedBy[1], null); 71 | 72 | const C = obj.resources[2]; 73 | 74 | assert.strictEqual(C.methods[0].securedBy.constructor, Array); 75 | assert.strictEqual(C.methods[0].securedBy.length, 2); 76 | assert.strictEqual( 77 | C.methods[0].securedBy[0], 78 | Object(C.methods[0].securedBy[0]) 79 | ); 80 | assert.strictEqual( 81 | C.methods[0].securedBy[0].schemeName, 82 | 'oauth_2_0_withscopes' 83 | ); 84 | assert.strictEqual(C.methods[0].securedBy[0].scopes.constructor, Array); 85 | assert.strictEqual(C.methods[0].securedBy[0].scopes.length, 1); 86 | assert.strictEqual(C.methods[0].securedBy[0].scopes[0], 'read-c'); 87 | assert.strictEqual( 88 | C.methods[0].securedBy[1], 89 | Object(C.methods[0].securedBy[1]) 90 | ); 91 | assert.strictEqual(C.methods[0].securedBy[1].schemeName, 'custom_scheme'); 92 | }); 93 | }); 94 | }); 95 | -------------------------------------------------------------------------------- /test/consistent-examples.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('consistent-examples.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/consistent-examples.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('all examples should have consistent representations', () => { 25 | let parent; 26 | let example; 27 | let value; 28 | 29 | assert.strictEqual(obj.resources[0].relativeUri, '/FullExamples'); 30 | parent = obj.resources[0].methods[0].responses[0].body[0]; 31 | example = parent.examples[0]; 32 | 33 | assert.strictEqual(example.displayName, 'First Example!'); 34 | assert.strictEqual(example.description, "It's the first example"); 35 | assert.strictEqual(JSON.parse(example.value).SomeField, 'Uno'); 36 | 37 | example = parent.examples[1]; 38 | 39 | assert.strictEqual(example.displayName, 'Second Example!'); 40 | assert.strictEqual(example.description, "It's the second example"); 41 | assert.strictEqual(JSON.parse(example.value).SomeField, 'Duo'); 42 | 43 | assert.strictEqual(obj.resources[1].relativeUri, '/JsonExample'); 44 | parent = obj.resources[1].methods[0].responses[0].body[0]; 45 | example = parent.examples[0]; 46 | value = JSON.parse(example.value); 47 | assert.strictEqual(value.TestField, 'This example is defined in JSON.'); 48 | 49 | assert.strictEqual(obj.resources[2].relativeUri, '/HeaderExample'); 50 | parent = obj.resources[2].methods[0]; 51 | example = parent.headers[0].examples[0]; 52 | assert.strictEqual(example.value, 'Very Important'); 53 | 54 | example = parent.headers[1].examples[0]; 55 | assert.strictEqual(example.displayName, 'Baloon Assertion'); 56 | assert.strictEqual( 57 | example.description, 58 | 'This descriptive header example' 59 | ); 60 | assert.strictEqual( 61 | example.value, 62 | 'Red baloons are known to be the colour red.' 63 | ); 64 | 65 | assert.strictEqual( 66 | obj.resources[3].relativeUri, 67 | '/UriExample/{Id}/{Mood}' 68 | ); 69 | parent = obj.resources[3]; 70 | example = parent.uriParameters[0].examples[0]; 71 | assert.strictEqual(example.value, 'ABC123'); 72 | 73 | example = parent.uriParameters[1].examples[0]; 74 | assert.strictEqual(example.displayName, 'How are ya'); 75 | assert.strictEqual(example.description, 'ARE YOU ANGRY'); 76 | assert.strictEqual(example.value, 'Serene.'); 77 | 78 | assert.strictEqual(obj.resources[4].relativeUri, '/QueryParamsExample'); 79 | parent = obj.resources[4].methods[0]; 80 | example = parent.queryParameters[0].examples[0]; 81 | assert.strictEqual(example.displayName, 'Comfortable'); 82 | assert.strictEqual(example.description, 'Best combined with a sunny day'); 83 | assert.strictEqual(example.value, '24'); 84 | 85 | assert.strictEqual(obj.resources[5].relativeUri, '/NestedExample'); 86 | parent = obj.resources[5].methods[0].responses[0].body[0]; 87 | example = parent.properties[0].examples[0]; 88 | value = JSON.parse(example.value); 89 | assert.strictEqual(example.displayName, 'Sneaky nested example'); 90 | assert.strictEqual(example.description, "It's sneaky!"); 91 | assert.strictEqual(value.HereIsANumber, 42); 92 | assert.strictEqual(value.HereIsAString, 'Strange strong string'); 93 | 94 | example = parent.properties[1].examples[0]; 95 | assert.strictEqual(example.value, "I'm layered, bro"); 96 | }); 97 | }); 98 | }); 99 | -------------------------------------------------------------------------------- /test/helloworld.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('helloworld.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/helloworld.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should test the basic properties of the raml object', () => { 25 | assert.strictEqual(obj.title, 'Hello world'); 26 | assert.strictEqual(obj.version, '1'); 27 | assert.strictEqual(obj.baseUri, 'http://example.com/{version}'); 28 | assert.strictEqual(obj.resources.length, 1); 29 | }); 30 | 31 | it('should test the documentation', () => { 32 | assert.strictEqual(obj.documentation.length, 2); 33 | 34 | const first = obj.documentation[0]; 35 | const second = obj.documentation[1]; 36 | 37 | assert.strictEqual(first.title, 'Welcome'); 38 | assert.strictEqual( 39 | first.content, 40 | 'Welcome to the Example Documentation. The Example API allows you\nto do stuff. See also [example.com](https://www.example.com).\n' 41 | ); 42 | assert.strictEqual(first.uniqueId, 'welcome'); 43 | 44 | assert.strictEqual(second.title, 'Chapter two'); 45 | assert.strictEqual( 46 | second.content, 47 | 'More content here. Including **bold** text!\n' 48 | ); 49 | assert.strictEqual(second.uniqueId, 'chapter_two'); 50 | }); 51 | 52 | it('should test the top level /helloworld resource', () => { 53 | const resource = obj.resources[0]; 54 | 55 | assert.strictEqual(resource.relativeUri, '/helloworld'); 56 | assert.strictEqual(resource.displayName, '/helloworld'); 57 | assert.strictEqual( 58 | resource.description, 59 | 'This is the top level description for /helloworld.' 60 | ); 61 | assert.strictEqual(resource.parentUrl, ''); 62 | assert.strictEqual(resource.uniqueId, 'helloworld'); 63 | assert.deepEqual(resource.allUriParameters, []); 64 | }); 65 | 66 | it('should test the /helloworld methods', () => { 67 | const methods = obj.resources[0].methods; 68 | 69 | assert.strictEqual(methods.length, 1); 70 | 71 | const method = methods[0]; 72 | 73 | assert.strictEqual(method.method, 'get'); 74 | assert.deepEqual(method.allUriParameters, []); 75 | assert.deepEqual(method.responses.length, 1); 76 | 77 | const response = method.responses[0]; 78 | 79 | assert.strictEqual(response.code, '200'); 80 | assert.strictEqual(response.body.length, 1); 81 | assert.strictEqual(response.body[0].name, 'application/json'); 82 | assert.strictEqual(response.body[0].displayName, 'application/json'); 83 | assert.strictEqual( 84 | response.body[0].type, 85 | '{\n "title": "Hello world Response",\n "type": "object",\n "properties": {\n "message": {\n "type": "string"\n }\n }\n}\n' 86 | ); 87 | assert.strictEqual( 88 | response.body[0].examples[0].value, 89 | '{\n "message": "Hello world"\n}' 90 | ); 91 | }); 92 | 93 | it('should test the sub-resource', () => { 94 | const topTesource = obj.resources[0]; 95 | assert.strictEqual(topTesource.resources.length, 1); 96 | 97 | const resource = obj.resources[0].resources[0]; 98 | assert.strictEqual(resource.relativeUri, '/test'); 99 | assert.strictEqual(resource.displayName, 'TEST'); 100 | assert.strictEqual(resource.parentUrl, '/helloworld'); 101 | assert.strictEqual(resource.uniqueId, 'helloworld_test'); 102 | assert.deepEqual(resource.allUriParameters, []); 103 | }); 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /test/helloworld-as-buffer.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | const fs = require('fs'); 8 | 9 | describe('raml2obj', () => { 10 | describe('helloworld.raml as Buffer', () => { 11 | let obj; 12 | 13 | before(done => { 14 | const strContent = fs.readFileSync('test/helloworld.raml'); 15 | raml2obj.parse(strContent).then( 16 | result => { 17 | obj = result; 18 | done(); 19 | }, 20 | error => { 21 | console.log('error', error); 22 | } 23 | ); 24 | }); 25 | 26 | it('should test the basic properties of the raml object', () => { 27 | assert.strictEqual(obj.title, 'Hello world'); 28 | assert.strictEqual(obj.version, '1'); 29 | assert.strictEqual(obj.baseUri, 'http://example.com/{version}'); 30 | assert.strictEqual(obj.resources.length, 1); 31 | }); 32 | 33 | it('should test the documentation', () => { 34 | assert.strictEqual(obj.documentation.length, 2); 35 | 36 | const first = obj.documentation[0]; 37 | const second = obj.documentation[1]; 38 | 39 | assert.strictEqual(first.title, 'Welcome'); 40 | assert.strictEqual( 41 | first.content, 42 | 'Welcome to the Example Documentation. The Example API allows you\nto do stuff. See also [example.com](https://www.example.com).\n' 43 | ); 44 | assert.strictEqual(first.uniqueId, 'welcome'); 45 | 46 | assert.strictEqual(second.title, 'Chapter two'); 47 | assert.strictEqual( 48 | second.content, 49 | 'More content here. Including **bold** text!\n' 50 | ); 51 | assert.strictEqual(second.uniqueId, 'chapter_two'); 52 | }); 53 | 54 | it('should test the top level /helloworld resource', () => { 55 | const resource = obj.resources[0]; 56 | 57 | assert.strictEqual(resource.relativeUri, '/helloworld'); 58 | assert.strictEqual(resource.displayName, '/helloworld'); 59 | assert.strictEqual( 60 | resource.description, 61 | 'This is the top level description for /helloworld.' 62 | ); 63 | assert.strictEqual(resource.parentUrl, ''); 64 | assert.strictEqual(resource.uniqueId, 'helloworld'); 65 | assert.deepEqual(resource.allUriParameters, []); 66 | }); 67 | 68 | it('should test the /helloworld methods', () => { 69 | const methods = obj.resources[0].methods; 70 | 71 | assert.strictEqual(methods.length, 1); 72 | 73 | const method = methods[0]; 74 | 75 | assert.strictEqual(method.method, 'get'); 76 | assert.deepEqual(method.allUriParameters, []); 77 | assert.deepEqual(method.responses.length, 1); 78 | 79 | const response = method.responses[0]; 80 | 81 | assert.strictEqual(response.code, '200'); 82 | assert.strictEqual(response.body.length, 1); 83 | assert.strictEqual(response.body[0].name, 'application/json'); 84 | assert.strictEqual(response.body[0].displayName, 'application/json'); 85 | assert.strictEqual( 86 | response.body[0].type, 87 | '{\n "title": "Hello world Response",\n "type": "object",\n "properties": {\n "message": {\n "type": "string"\n }\n }\n}\n' 88 | ); 89 | assert.strictEqual( 90 | response.body[0].examples[0].value, 91 | '{\n "message": "Hello world"\n}' 92 | ); 93 | }); 94 | 95 | it('should test the sub-resource', () => { 96 | const topTesource = obj.resources[0]; 97 | assert.strictEqual(topTesource.resources.length, 1); 98 | 99 | const resource = obj.resources[0].resources[0]; 100 | assert.strictEqual(resource.relativeUri, '/test'); 101 | assert.strictEqual(resource.displayName, 'TEST'); 102 | assert.strictEqual(resource.parentUrl, '/helloworld'); 103 | assert.strictEqual(resource.uniqueId, 'helloworld_test'); 104 | assert.deepEqual(resource.allUriParameters, []); 105 | }); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /test/helloworld-as-string.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | const fs = require('fs'); 8 | 9 | describe('raml2obj', () => { 10 | describe('helloworld.raml as string', () => { 11 | let obj; 12 | 13 | before(done => { 14 | const strContent = fs.readFileSync('test/helloworld.raml').toString(); 15 | raml2obj.parse(strContent).then( 16 | result => { 17 | obj = result; 18 | done(); 19 | }, 20 | error => { 21 | console.log('error', error); 22 | } 23 | ); 24 | }); 25 | 26 | it('should test the basic properties of the raml object', () => { 27 | assert.strictEqual(obj.title, 'Hello world'); 28 | assert.strictEqual(obj.version, '1'); 29 | assert.strictEqual(obj.baseUri, 'http://example.com/{version}'); 30 | assert.strictEqual(obj.resources.length, 1); 31 | }); 32 | 33 | it('should test the documentation', () => { 34 | assert.strictEqual(obj.documentation.length, 2); 35 | 36 | const first = obj.documentation[0]; 37 | const second = obj.documentation[1]; 38 | 39 | assert.strictEqual(first.title, 'Welcome'); 40 | assert.strictEqual( 41 | first.content, 42 | 'Welcome to the Example Documentation. The Example API allows you\nto do stuff. See also [example.com](https://www.example.com).\n' 43 | ); 44 | assert.strictEqual(first.uniqueId, 'welcome'); 45 | 46 | assert.strictEqual(second.title, 'Chapter two'); 47 | assert.strictEqual( 48 | second.content, 49 | 'More content here. Including **bold** text!\n' 50 | ); 51 | assert.strictEqual(second.uniqueId, 'chapter_two'); 52 | }); 53 | 54 | it('should test the top level /helloworld resource', () => { 55 | const resource = obj.resources[0]; 56 | 57 | assert.strictEqual(resource.relativeUri, '/helloworld'); 58 | assert.strictEqual(resource.displayName, '/helloworld'); 59 | assert.strictEqual( 60 | resource.description, 61 | 'This is the top level description for /helloworld.' 62 | ); 63 | assert.strictEqual(resource.parentUrl, ''); 64 | assert.strictEqual(resource.uniqueId, 'helloworld'); 65 | assert.deepEqual(resource.allUriParameters, []); 66 | }); 67 | 68 | it('should test the /helloworld methods', () => { 69 | const methods = obj.resources[0].methods; 70 | 71 | assert.strictEqual(methods.length, 1); 72 | 73 | const method = methods[0]; 74 | 75 | assert.strictEqual(method.method, 'get'); 76 | assert.deepEqual(method.allUriParameters, []); 77 | assert.deepEqual(method.responses.length, 1); 78 | 79 | const response = method.responses[0]; 80 | 81 | assert.strictEqual(response.code, '200'); 82 | assert.strictEqual(response.body.length, 1); 83 | assert.strictEqual(response.body[0].name, 'application/json'); 84 | assert.strictEqual(response.body[0].displayName, 'application/json'); 85 | assert.strictEqual( 86 | response.body[0].type, 87 | '{\n "title": "Hello world Response",\n "type": "object",\n "properties": {\n "message": {\n "type": "string"\n }\n }\n}\n' 88 | ); 89 | assert.strictEqual( 90 | response.body[0].examples[0].value, 91 | '{\n "message": "Hello world"\n}' 92 | ); 93 | }); 94 | 95 | it('should test the sub-resource', () => { 96 | const topTesource = obj.resources[0]; 97 | assert.strictEqual(topTesource.resources.length, 1); 98 | 99 | const resource = obj.resources[0].resources[0]; 100 | assert.strictEqual(resource.relativeUri, '/test'); 101 | assert.strictEqual(resource.displayName, 'TEST'); 102 | assert.strictEqual(resource.parentUrl, '/helloworld'); 103 | assert.strictEqual(resource.uniqueId, 'helloworld_test'); 104 | assert.deepEqual(resource.allUriParameters, []); 105 | }); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /test/helloworld.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Hello world", 3 | "version": "1", 4 | "baseUri": "http://example.com/{version}", 5 | "baseUriParameters": [ 6 | { 7 | "name": "version", 8 | "displayName": "version", 9 | "typePropertyKind": "TYPE_EXPRESSION", 10 | "type": "string", 11 | "required": true, 12 | "enum": [ 13 | "1" 14 | ], 15 | "key": "version" 16 | } 17 | ], 18 | "protocols": [ 19 | "HTTP" 20 | ], 21 | "resources": [ 22 | { 23 | "methods": [ 24 | { 25 | "responses": [ 26 | { 27 | "code": "200", 28 | "body": [ 29 | { 30 | "name": "application/json", 31 | "displayName": "application/json", 32 | "typePropertyKind": "JSON", 33 | "type": "{\n \"title\": \"Hello world Response\",\n \"type\": \"object\",\n \"properties\": {\n \"message\": {\n \"type\": \"string\"\n }\n }\n}\n", 34 | "examples": [ 35 | { 36 | "value": "{\n \"message\": \"Hello world\"\n}", 37 | "strict": true, 38 | "name": null, 39 | "structuredValue": { 40 | "message": "Hello world" 41 | } 42 | } 43 | ], 44 | "key": "application/json" 45 | } 46 | ], 47 | "key": "200" 48 | } 49 | ], 50 | "protocols": [ 51 | "HTTP" 52 | ], 53 | "method": "get", 54 | "allUriParameters": [] 55 | } 56 | ], 57 | "description": "This is the top level description for /helloworld.", 58 | "relativeUri": "/helloworld", 59 | "displayName": "/helloworld", 60 | "resources": [ 61 | { 62 | "methods": [ 63 | { 64 | "protocols": [ 65 | "HTTP" 66 | ], 67 | "description": "a sub resource", 68 | "method": "get", 69 | "allUriParameters": [] 70 | } 71 | ], 72 | "relativeUri": "/test", 73 | "displayName": "TEST", 74 | "relativeUriPathSegments": [ 75 | "test" 76 | ], 77 | "absoluteUri": "http://example.com/{version}/helloworld/test", 78 | "parentUrl": "/helloworld", 79 | "uniqueId": "helloworld_test", 80 | "allUriParameters": [] 81 | } 82 | ], 83 | "relativeUriPathSegments": [ 84 | "helloworld" 85 | ], 86 | "absoluteUri": "http://example.com/{version}/helloworld", 87 | "parentUrl": "", 88 | "uniqueId": "helloworld", 89 | "allUriParameters": [] 90 | } 91 | ], 92 | "documentation": [ 93 | { 94 | "title": "Welcome", 95 | "content": "Welcome to the Example Documentation. The Example API allows you\nto do stuff. See also [example.com](https://www.example.com).\n", 96 | "uniqueId": "welcome" 97 | }, 98 | { 99 | "title": "Chapter two", 100 | "content": "More content here. Including **bold** text!\n", 101 | "uniqueId": "chapter_two" 102 | } 103 | ] 104 | } -------------------------------------------------------------------------------- /test/array-of-strings.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "API used to test broken features", 3 | "version": "v1", 4 | "baseUri": "http://api.samplehost.com/broken/{version}", 5 | "baseUriParameters": [ 6 | { 7 | "name": "version", 8 | "displayName": "version", 9 | "typePropertyKind": "TYPE_EXPRESSION", 10 | "type": "string", 11 | "required": true, 12 | "enum": [ 13 | "v1" 14 | ], 15 | "key": "version" 16 | } 17 | ], 18 | "protocols": [ 19 | "HTTP", 20 | "HTTPS" 21 | ], 22 | "mediaType": "application/json", 23 | "resources": [ 24 | { 25 | "methods": [ 26 | { 27 | "responses": [ 28 | { 29 | "code": "200", 30 | "body": [ 31 | { 32 | "name": "application/json", 33 | "displayName": "application/json", 34 | "typePropertyKind": "TYPE_EXPRESSION", 35 | "type": "array", 36 | "items": "string", 37 | "examples": [ 38 | { 39 | "value": "[\n \"somevalue\",\n \"anothervalue\"\n]", 40 | "strict": true, 41 | "name": null, 42 | "structuredValue": [ 43 | "somevalue", 44 | "anothervalue" 45 | ] 46 | } 47 | ], 48 | "key": "application/json" 49 | } 50 | ], 51 | "key": "200" 52 | } 53 | ], 54 | "protocols": [ 55 | "HTTP" 56 | ], 57 | "method": "get", 58 | "allUriParameters": [] 59 | }, 60 | { 61 | "responses": [ 62 | { 63 | "code": "200", 64 | "body": [ 65 | { 66 | "name": "application/json", 67 | "displayName": "application/json", 68 | "typePropertyKind": "TYPE_EXPRESSION", 69 | "type": "array", 70 | "items": "string", 71 | "examples": [ 72 | { 73 | "value": "[\n \"somevalue\",\n \"anothervalue\"\n]", 74 | "strict": true, 75 | "name": null, 76 | "structuredValue": [ 77 | "somevalue", 78 | "anothervalue" 79 | ] 80 | } 81 | ], 82 | "key": "application/json" 83 | } 84 | ], 85 | "key": "200" 86 | } 87 | ], 88 | "protocols": [ 89 | "HTTP" 90 | ], 91 | "method": "post", 92 | "allUriParameters": [] 93 | } 94 | ], 95 | "relativeUri": "/", 96 | "displayName": "/", 97 | "relativeUriPathSegments": [], 98 | "absoluteUri": "http://api.samplehost.com/broken/{version}/", 99 | "parentUrl": "", 100 | "uniqueId": "", 101 | "allUriParameters": [] 102 | } 103 | ] 104 | } -------------------------------------------------------------------------------- /consistency-helpers.js: -------------------------------------------------------------------------------- 1 | function _isObject(obj) { 2 | return obj === Object(obj); 3 | } 4 | 5 | function _objectToArray(obj) { 6 | if (Array.isArray(obj)) { 7 | return obj; 8 | } 9 | 10 | return Object.keys(obj).map(key => { 11 | if (_isObject(obj[key])) { 12 | obj[key].key = key; 13 | } 14 | return obj[key]; 15 | }); 16 | } 17 | 18 | function makeConsistent(obj, types) { 19 | if (_isObject(obj)) { 20 | const ignoredKeys = { rawType: true }; 21 | if (obj.type) { 22 | if (Array.isArray(obj.type)) { 23 | obj.type = obj.type[0]; 24 | } 25 | 26 | if ( 27 | obj.type === 'array' && 28 | Array.isArray(obj.items) && 29 | obj.items.length === 1 30 | ) { 31 | obj.items = obj.items[0]; 32 | } 33 | 34 | if (types && types[obj.type]) { 35 | const mergedObj = Object.assign({}, obj, types[obj.type]); 36 | 37 | // Every exception of inheritance should be deleted from mergedObj 38 | if (obj.description && types[obj.type].description) { 39 | delete mergedObj.description; 40 | } 41 | 42 | if (obj.examples) { 43 | mergedObj.examples = _objectToArray(obj.examples); 44 | } else if (obj.example) { 45 | mergedObj.examples = []; 46 | } else if (Array.isArray(types[obj.type].examples)) { 47 | mergedObj.examples = [...types[obj.type].examples]; 48 | } else if (_isObject(types[obj.type].examples)) { 49 | mergedObj.examples = _objectToArray(types[obj.type].examples); 50 | } 51 | 52 | Object.assign(obj, mergedObj); 53 | ignoredKeys.type = true; 54 | } 55 | } 56 | 57 | if (obj.items && types && types[obj.items]) { 58 | obj.items = types[obj.items]; 59 | ignoredKeys.items = true; 60 | } 61 | 62 | if (obj.structuredExample) { 63 | if (typeof obj.examples === 'undefined') { 64 | obj.examples = []; 65 | } 66 | 67 | obj.examples.push(obj.structuredExample); 68 | delete obj.example; 69 | delete obj.structuredExample; 70 | } 71 | 72 | if (obj.examples && obj.examples.length) { 73 | obj.examples = obj.examples.map(example => { 74 | if (!example.value) { 75 | return { value: example }; 76 | } 77 | if (!example.displayName && example.name) { 78 | example.displayName = example.name; 79 | } 80 | return example; 81 | }); 82 | } 83 | 84 | // Give each security scheme a displayName if it isn't already set 85 | if (obj.securitySchemes) { 86 | Object.keys(obj.securitySchemes).forEach(schemeName => { 87 | const scheme = obj.securitySchemes[schemeName]; 88 | scheme.displayName = scheme.displayName || scheme.name; 89 | }); 90 | } 91 | 92 | if (Array.isArray(obj.securedBy)) { 93 | if ( 94 | obj.securedBy.length === 0 || 95 | obj.securedBy.every(scheme => scheme === null) 96 | ) { 97 | // The RAML 1.0 spec allows that: 98 | // "A securedBy node containing null as the array component indicates 99 | // the method can be called without applying any security scheme." 100 | delete obj.securedBy; 101 | } else { 102 | // Guarantee that all elements of the securedBy array are either null or 103 | // objects containing a schemeName property (and an optional scopes property) 104 | obj.securedBy = obj.securedBy.map(scheme => { 105 | if (scheme === null) { 106 | return null; 107 | } 108 | if (typeof scheme === 'string') { 109 | return { schemeName: scheme }; 110 | } 111 | const schemeName = Object.keys(scheme)[0]; 112 | return Object.assign({ schemeName }, scheme[schemeName] || {}); 113 | }); 114 | } 115 | } 116 | 117 | // Fix inconsistency between request headers and response headers from raml-1-parser. 118 | // https://github.com/raml-org/raml-js-parser-2/issues/582 119 | // TODO this issue is fixed since 26 Sep 2017, i.e. v1.1.32, could be removed 120 | if (Array.isArray(obj.headers)) { 121 | obj.headers.forEach(hdr => { 122 | if (typeof hdr.key === 'undefined' && hdr.name) { 123 | hdr.key = hdr.name; 124 | } 125 | }); 126 | } 127 | 128 | Object.keys(obj).forEach(key => { 129 | // Don't recurse into types, which have already been canonicalized. 130 | if (!(key in ignoredKeys)) { 131 | makeConsistent(obj[key], types); 132 | } 133 | }); 134 | } else if (Array.isArray(obj)) { 135 | obj.forEach(value => { 136 | makeConsistent(value, types); 137 | }); 138 | } 139 | 140 | return obj; 141 | } 142 | 143 | module.exports = makeConsistent; 144 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | 6.8.1 - Jun 28, 2021 2 | - Removed `#!/usr/bin/env node` from index.js 3 | 4 | 6.7.0 - Jul 16, 2020 5 | - Updated dependencies 6 | 7 | 6.6.0 - Oct 30, 2019 8 | - Updated dependencies 9 | 10 | 6.5.0 - June 13, 2019 11 | - Allow for 'parse' to accept raw input (e.g. string or Buffer) 12 | 13 | 6.4.0 - May 29, 2019 14 | - Array output format and orderHints in object output (via options flag) 15 | 16 | 6.3.0 - May 29, 2019 17 | - Add support for RAML parser resolvers options 18 | 19 | 6.2.0 - April 2, 2019 20 | - Updated dependencies 21 | 22 | 6.1.0 - September 7, 2018 23 | - Added options.extensionsAndOverlays 24 | - Updated dependencies 25 | 26 | 6.0.0 - February 19, 2018 27 | - Updated datatype-expansion to 0.3.x, which fixes invalid hoisting of unions outside of array items, as well as several other issues 28 | - Enabled tracking of original type in datatype-expansion, so that themes can reference base types 29 | - Tracking rawType of canonicalized types, so that themes can distinguish between declared annotations and inherited annotations 30 | 31 | 5.9.0 - January 9, 2018 32 | - Updated raml-1-parser to 1.1.36, plus devDependencies like eslint, prettier, and mocha 33 | 34 | 5.8.0 - December 6, 2017 35 | - Updated datatype-expansion dependency to 0.2.6 36 | 37 | 5.7.0 - November 3, 2017 38 | - Updated datatype-expansion dependency to 0.2.4 39 | - Updated raml-1-parser to 1.1.36 40 | 41 | 5.6.0 - September 27, 2017 42 | - Updated raml-1-parser 43 | 44 | 5.5.0 - June 5, 2017 45 | - Prefer taking examples from higher level items instead of types (#41) 46 | - Update raml-1-parser dependency 47 | 48 | 5.4.0 - May 3, 2017 49 | - Updated third party dependencies 50 | 51 | 5.3.0 - April 25, 2017 52 | - Updated third party dependencies 53 | - By default we're no longer using raml-1-parser's rejectOnErrors option since it's very slow (raml2html/raml2html#345) 54 | 55 | 5.2.1 - April 14, 2017 56 | - Updated third party dependencies 57 | 58 | 5.2.0 - March 24, 2017 59 | - Updated raml-1-parser dependency 60 | 61 | 5.1.0 - March 1, 2017 62 | - Updated raml-1-parser dependency 63 | 64 | 5.0.0 - February 15, 2017 65 | - Breaking change: updated mapping of securitySchemes and securedBy 66 | - Fixed an issue with some array type declarations having inconsistent item type definitions (raml2html/raml2html#323) (#29) 67 | 68 | 4.1.0 - January 27, 2017 69 | - Added extra properties to examples (#22) 70 | - Updated raml-js-parser-2 dependancy (#25) 71 | - Fixed types' parent overriding behaviour (#26) 72 | 73 | 4.0.4 - January 10, 2017 74 | - Fix response headers inconsistency (#23) 75 | 76 | 4.0.3 - December 29, 2016 77 | - Made securedBy: [ null] consistent with no security 78 | 79 | 4.0.2 - December 8, 2016 80 | - Fixed more Node 4 problems, now actually tested via NVM 81 | 82 | 4.0.1 - December 7, 2016 83 | - Fixed Node 4 support 84 | 85 | 4.0.0 - December 1, 2016 86 | - After almost 4 months of development, it's done: 4.0.0 with RAML 1 support, and a much more consistent output. And a whole lot of unit tests! 87 | - Breaking change: removed support for RAML 0.8 files 88 | - Breaking change: the output of raml2obj has changed 89 | 90 | 4.0.0-beta16 - November 30, 2016 91 | - Updated raml-1-parser to 1.1.9 (#18) 92 | 93 | 4.0.0-beta15 - November 21, 2016 94 | - Downgraded datatype-expansion library 95 | - Making all the types consistent ourselves now, always an array 96 | 97 | 4.0.0-beta14 - November 21, 2016 98 | - Updated datatype-expansion library 99 | 100 | 4.0.0-beta13 - November 2, 2016 101 | - Correctly expanding types within uriParameters 102 | - Updated datatype-expansion and raml-1-parser 103 | 104 | 4.0.0-beta12 - November 1, 2016 105 | - Handling `array` types with `items` by expanding the items into a type object 106 | 107 | 4.0.0-beta11 - November 1, 2016 108 | - Updated datatype-expansion to 0.0.14 109 | 110 | 4.0.0-beta10 - October 31, 2016 111 | - Updated raml-1-parser to 1.1.6 112 | 113 | 4.0.0-beta9 - October 14, 2016 114 | - Types are just a string instead of an array 115 | 116 | 4.0.0-beta8 - October 14, 2016 117 | - Updated raml-1-parser to 1.1.5 118 | - Fixed handling of type inheritance (#15) 119 | 120 | 4.0.0-beta7 - October 4, 2016 121 | - Fixed JS error when `body` is used as a property (#14) 122 | - Removed all the empty examples arrays 123 | - Fixed `key` properties that were sometimes integers 124 | 125 | 4.0.0-beta6 - September 29, 2016 126 | - Fixed examples array 127 | 128 | 4.0.0-beta5 - September 29, 2016 129 | - Always return an `examples` array containing simple strings 130 | 131 | 4.0.0-beta4 - September 29, 2016 132 | - Expanding types where possible 133 | - Added more unit tests 134 | 135 | 4.0.0-beta3 - September 23, 2016 136 | - Limit the `files` that are sent to NPM 137 | 138 | 4.0.0-beta2 - September 22, 2016 139 | - Updated raml-1-parser to 1.1.3 140 | - Added a bunch of unit tests 141 | - Breaking change: removed support for RAML 0.8 files 142 | - Breaking change: the output of raml2obj has changed 143 | 144 | 4.0.0-beta1 - August 10, 2016 145 | - Using the new raml-1-parser which support RAML 1.0 as well as 0.8 146 | - Breaking change: raml-1-parser doesn't support string or buffer sources anymore 147 | 148 | 3.0.0 - August 10, 2016 149 | - Released without further changes 150 | 151 | 3.0.0-beta2 - August 8, 2016 152 | - Fix JS error 153 | 154 | 3.0.0-beta1 - August 7, 2016 155 | - Updated code to use ES6 syntax 156 | - The securitySchemeWithName helper function is now part of raml2obj 157 | - Breaking change: Node 4 or higher is now required! 158 | 159 | 2.2.0 - July 16, 2015 160 | - Update third party dependencies 161 | 162 | 2.1.0 - May 22, 2015 163 | - Renamed raml2obj.js to index.js 164 | - Trim the left underscore from the uniqueId's 165 | 166 | 2.0.0 - March 13, 2015 167 | - Using a promise based API, please see README for updated usage example 168 | 169 | 1.0.0 - January 26, 2015 170 | - Finalized API, the parse method is all we need 171 | - Removed FileReader export 172 | 173 | 0.5.0 - January 21, 2015 174 | - Copy resource allUriParameters to its methods 175 | 176 | 0.4.0 - January 14, 2015 177 | - Export FileReader object 178 | 179 | 0.3.0 - September 25, 2014 180 | - Allow remote urls to be loaded 181 | 182 | 0.2.1 - July 8, 2014 183 | - Critical bugfix 184 | 185 | 0.2.0 - July 8, 2014 186 | - Adding the unique id's to top level documentation chapters has been moved from raml2html to raml2obj 187 | 188 | 0.1.0 - June 12, 2014 189 | - Initial release of standalone raml2obj 190 | -------------------------------------------------------------------------------- /test/worldmusic.raml: -------------------------------------------------------------------------------- 1 | #%RAML 1.0 2 | title: World Music API 3 | description: This is an example of a music API. 4 | version: v1 5 | baseUri: 6 | value: http://{environment}.musicapi.com/{version} 7 | (rediractable): true 8 | baseUriParameters: 9 | environment: 10 | type: string 11 | enum: [ "stg", "dev", "test", "prod" ] 12 | protocols: [ HTTP, HTTPS ] 13 | mediaType: [ application/json ] 14 | documentation: 15 | - title: Getting Started 16 | content: | 17 | This is a getting started guide for the World Music API. 18 | - title: Legal 19 | content: See http://legal.musicapi.com 20 | 21 | types: 22 | Comment: 23 | type: object 24 | properties: 25 | author: 26 | type: string 27 | required: true 28 | body: 29 | type: string 30 | required: true 31 | date: 32 | type: integer 33 | required: true 34 | editAuthor: 35 | type: string 36 | required: false 37 | edited: 38 | type: integer 39 | required: false 40 | Entry: | 41 | { 42 | "type": "array", 43 | "items": { 44 | "$ref": "#/definitions/song" 45 | }, 46 | "definitions": { 47 | "song": { 48 | "type": "object", 49 | "properties": { 50 | "title": { 51 | "type": "string" 52 | }, 53 | "artist": { 54 | "type": "string" 55 | } 56 | } 57 | } 58 | } 59 | } 60 | AnotherEntry: 61 | type: Entry 62 | description: | 63 | This is just another entry to simulate that you can add facets also on JSON 64 | schema defined types. Although you can only add documentation-based facets. 65 | User: 66 | properties: 67 | firstname: string 68 | lastname: 69 | type: string 70 | example: Doe 71 | required: false 72 | example: 73 | firstname: John 74 | 75 | traits: 76 | secured: !include world-music-api/secured/accessToken.raml 77 | 78 | resourceTypes: 79 | collection: 80 | get: 81 | description: returns a list of <> 82 | responses: 83 | 200: 84 | body: 85 | application/json: 86 | schema: <> 87 | 88 | 89 | annotationTypes: 90 | deprecated: 91 | properties: 92 | date: datetime 93 | deprecatedBy: User 94 | comment: nil | string 95 | monitoringInterval: 96 | type: integer 97 | description: interval in seconds 98 | example: 2 99 | ready: 100 | type: nil 101 | description: markes a resource as ready 102 | allowedTargets: Resource 103 | info: 104 | properties: 105 | license: 106 | type: string 107 | enum: [ "MIT", "Apache 2.0" ] 108 | allowedTargets: API 109 | rediractable: boolean 110 | 111 | (info): 112 | license: MIT 113 | 114 | securitySchemes: 115 | oauth_1_0: 116 | description: | 117 | OAuth 1.0 continues to be supported for all API requests, but OAuth 2.0 is now preferred. 118 | type: OAuth 1.0 119 | settings: 120 | requestTokenUri: https://api.mysampleapi.com/1/oauth/request_token 121 | authorizationUri: https://api.mysampleapi.com/1/oauth/authorize 122 | tokenCredentialsUri: https://api.mysampleapi.com/1/oauth/access_token 123 | signatures: [ 'HMAC-SHA1', 'PLAINTEXT' ] 124 | oauth_2_0: 125 | description: | 126 | Dropbox supports OAuth 2.0 for authenticating all API requests. 127 | type: OAuth 2.0 128 | describedBy: 129 | headers: 130 | Authorization: 131 | description: | 132 | Used to send a valid OAuth 2 access token. Do not use 133 | with the "access_token" query string parameter. 134 | type: string 135 | queryParameters: 136 | access_token: 137 | description: | 138 | Used to send a valid OAuth 2 access token. Do not use with 139 | the "Authorization" header. 140 | type: string 141 | responses: 142 | 401: 143 | description: | 144 | Bad or expired token. This can happen if the user or Dropbox 145 | revoked or expired an access token. To fix, re-authenticate 146 | the user. 147 | 403: 148 | description: | 149 | Bad OAuth request (wrong consumer key, bad nonce, expired 150 | timestamp...). Unfortunately, re-authenticating the user won't help here. 151 | settings: 152 | authorizationUri: https://www.dropbox.com/1/oauth2/authorize 153 | accessTokenUri: https://api.dropbox.com/1/oauth2/token 154 | authorizationGrants: [ authorization_code, implicit, 'urn:ietf:params:oauth:grant-type:saml2-bearer' ] 155 | custom_scheme: 156 | description: | 157 | A custom security scheme for authenticating requests. 158 | type: x-custom 159 | describedBy: 160 | headers: 161 | SpecialToken: 162 | description: | 163 | Used to send a custom token. 164 | type: string 165 | responses: 166 | 401: 167 | description: | 168 | Bad token. 169 | 403: 170 | 171 | securedBy: custom_scheme 172 | 173 | uses: 174 | SongsLib: world-music-api/libraries/songs-library.raml 175 | ApiLib: world-music-api/libraries/api-library.raml 176 | 177 | /api: 178 | get: 179 | queryString: 180 | properties: 181 | start?: number 182 | page-size?: number 183 | post: 184 | body: 185 | application/json: 186 | type: ApiLib.RamlDataType 187 | /entry: 188 | type: collection 189 | post: 190 | responses: 191 | 200: 192 | body: AnotherEntry 193 | /songs: 194 | displayName: Songs 195 | description: Access to all songs inside the music world library. 196 | (ready): 197 | is: [ secured ] 198 | get: 199 | securedBy: [ oauth_2_0, null ] 200 | (monitoringInterval): 30 201 | queryParameters: 202 | genre: 203 | description: filter the songs by genre 204 | post: 205 | /{songId}: 206 | get: 207 | (deprecated): 208 | date: 2016-02-28T16:41:41.090Z 209 | deprecatedBy: 210 | firstname: Christian 211 | comment: no comment 212 | responses: 213 | 200: 214 | body: 215 | application/json: 216 | type: SongsLib.Song 217 | application/xml: 218 | type: !include world-music-api/schemas/songs.xsd 219 | example: !include world-music-api/examples/songs.xml 220 | -------------------------------------------------------------------------------- /test/typeexample.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Test", 3 | "resources": [ 4 | { 5 | "methods": [ 6 | { 7 | "description": "bla", 8 | "method": "get", 9 | "allUriParameters": [ 10 | { 11 | "name": "objectid", 12 | "displayName": "objectid", 13 | "typePropertyKind": "TYPE_EXPRESSION", 14 | "type": "string", 15 | "required": true, 16 | "description": "A valid MongoDB object id.", 17 | "rawType": { 18 | "name": "objectid", 19 | "displayName": "objectid", 20 | "typePropertyKind": "TYPE_EXPRESSION", 21 | "type": [ 22 | "string" 23 | ], 24 | "example": "576bca8b70fddb149c4a9e92", 25 | "description": "A valid MongoDB object id.", 26 | "structuredExample": { 27 | "value": "576bca8b70fddb149c4a9e92", 28 | "strict": true, 29 | "name": null, 30 | "structuredValue": "576bca8b70fddb149c4a9e92" 31 | } 32 | }, 33 | "examples": [ 34 | { 35 | "value": "576bca8b70fddb149c4a9e92", 36 | "strict": true, 37 | "name": null, 38 | "structuredValue": "576bca8b70fddb149c4a9e92" 39 | } 40 | ], 41 | "key": "id" 42 | } 43 | ] 44 | } 45 | ], 46 | "uriParameters": [ 47 | { 48 | "name": "objectid", 49 | "displayName": "objectid", 50 | "typePropertyKind": "TYPE_EXPRESSION", 51 | "type": "string", 52 | "required": true, 53 | "description": "A valid MongoDB object id.", 54 | "rawType": { 55 | "name": "objectid", 56 | "displayName": "objectid", 57 | "typePropertyKind": "TYPE_EXPRESSION", 58 | "type": [ 59 | "string" 60 | ], 61 | "example": "576bca8b70fddb149c4a9e92", 62 | "description": "A valid MongoDB object id.", 63 | "structuredExample": { 64 | "value": "576bca8b70fddb149c4a9e92", 65 | "strict": true, 66 | "name": null, 67 | "structuredValue": "576bca8b70fddb149c4a9e92" 68 | } 69 | }, 70 | "examples": [ 71 | { 72 | "value": "576bca8b70fddb149c4a9e92", 73 | "strict": true, 74 | "name": null, 75 | "structuredValue": "576bca8b70fddb149c4a9e92" 76 | } 77 | ], 78 | "key": "id" 79 | } 80 | ], 81 | "relativeUri": "/company", 82 | "displayName": "/company", 83 | "relativeUriPathSegments": [ 84 | "company" 85 | ], 86 | "absoluteUri": "/company", 87 | "parentUrl": "", 88 | "uniqueId": "company", 89 | "allUriParameters": [ 90 | { 91 | "name": "objectid", 92 | "displayName": "objectid", 93 | "typePropertyKind": "TYPE_EXPRESSION", 94 | "type": "string", 95 | "required": true, 96 | "description": "A valid MongoDB object id.", 97 | "rawType": { 98 | "name": "objectid", 99 | "displayName": "objectid", 100 | "typePropertyKind": "TYPE_EXPRESSION", 101 | "type": [ 102 | "string" 103 | ], 104 | "example": "576bca8b70fddb149c4a9e92", 105 | "description": "A valid MongoDB object id.", 106 | "structuredExample": { 107 | "value": "576bca8b70fddb149c4a9e92", 108 | "strict": true, 109 | "name": null, 110 | "structuredValue": "576bca8b70fddb149c4a9e92" 111 | } 112 | }, 113 | "examples": [ 114 | { 115 | "value": "576bca8b70fddb149c4a9e92", 116 | "strict": true, 117 | "name": null, 118 | "structuredValue": "576bca8b70fddb149c4a9e92" 119 | } 120 | ], 121 | "key": "id" 122 | } 123 | ] 124 | } 125 | ], 126 | "types": { 127 | "objectid": { 128 | "type": "string", 129 | "name": "objectid", 130 | "displayName": "objectid", 131 | "typePropertyKind": "TYPE_EXPRESSION", 132 | "description": "A valid MongoDB object id.", 133 | "rawType": { 134 | "name": "objectid", 135 | "displayName": "objectid", 136 | "typePropertyKind": "TYPE_EXPRESSION", 137 | "type": [ 138 | "string" 139 | ], 140 | "example": "576bca8b70fddb149c4a9e92", 141 | "description": "A valid MongoDB object id.", 142 | "structuredExample": { 143 | "value": "576bca8b70fddb149c4a9e92", 144 | "strict": true, 145 | "name": null, 146 | "structuredValue": "576bca8b70fddb149c4a9e92" 147 | } 148 | }, 149 | "examples": [ 150 | { 151 | "value": "576bca8b70fddb149c4a9e92", 152 | "strict": true, 153 | "name": null, 154 | "structuredValue": "576bca8b70fddb149c4a9e92" 155 | } 156 | ] 157 | } 158 | } 159 | } -------------------------------------------------------------------------------- /test/secured-by.json: -------------------------------------------------------------------------------- 1 | { 2 | "securitySchemes": { 3 | "oauth_2_0": { 4 | "name": "oauth_2_0", 5 | "type": "OAuth 2.0", 6 | "describedBy": { 7 | "headers": [ 8 | { 9 | "name": "Authorization", 10 | "displayName": "Authorization", 11 | "typePropertyKind": "TYPE_EXPRESSION", 12 | "type": "string", 13 | "required": true, 14 | "key": "Authorization" 15 | } 16 | ], 17 | "responses": [ 18 | { 19 | "code": "401", 20 | "description": "Invalid or expired token." 21 | } 22 | ] 23 | }, 24 | "settings": { 25 | "accessTokenUri": "/token", 26 | "authorizationGrants": [ 27 | "client_credentials" 28 | ] 29 | }, 30 | "displayName": "oauth_2_0" 31 | }, 32 | "oauth_2_0_withscopes": { 33 | "name": "oauth_2_0_withscopes", 34 | "type": "OAuth 2.0", 35 | "describedBy": { 36 | "headers": [ 37 | { 38 | "name": "Authorization", 39 | "displayName": "Authorization", 40 | "typePropertyKind": "TYPE_EXPRESSION", 41 | "type": "string", 42 | "required": true, 43 | "key": "Authorization" 44 | } 45 | ], 46 | "responses": [ 47 | { 48 | "code": "401", 49 | "description": "Invalid or expired token." 50 | } 51 | ] 52 | }, 53 | "settings": { 54 | "accessTokenUri": "/token", 55 | "authorizationGrants": [ 56 | "client_credentials" 57 | ], 58 | "scopes": [ 59 | "add-a", 60 | "remove-a", 61 | "add-b", 62 | "remove-b", 63 | "read-c" 64 | ] 65 | }, 66 | "displayName": "oauth_2_0_withscopes" 67 | }, 68 | "custom_scheme": { 69 | "name": "custom_scheme", 70 | "type": "x-custom", 71 | "description": "A custom security scheme for authenticating requests.\n", 72 | "describedBy": { 73 | "headers": [ 74 | { 75 | "name": "SpecialToken", 76 | "displayName": "SpecialToken", 77 | "typePropertyKind": "TYPE_EXPRESSION", 78 | "type": "string", 79 | "required": true, 80 | "description": "Used to send a custom token.\n", 81 | "key": "SpecialToken" 82 | } 83 | ], 84 | "responses": [ 85 | { 86 | "code": "401", 87 | "description": "Bad token.\n" 88 | }, 89 | { 90 | "code": "403" 91 | } 92 | ] 93 | }, 94 | "displayName": "custom_scheme" 95 | } 96 | }, 97 | "title": "Secured By Null", 98 | "resources": [ 99 | { 100 | "methods": [ 101 | { 102 | "method": "get", 103 | "allUriParameters": [] 104 | }, 105 | { 106 | "method": "post", 107 | "allUriParameters": [] 108 | }, 109 | { 110 | "method": "put", 111 | "allUriParameters": [] 112 | }, 113 | { 114 | "method": "delete", 115 | "allUriParameters": [] 116 | } 117 | ], 118 | "relativeUri": "/A", 119 | "displayName": "/A", 120 | "relativeUriPathSegments": [ 121 | "A" 122 | ], 123 | "absoluteUri": "/A", 124 | "parentUrl": "", 125 | "uniqueId": "a", 126 | "allUriParameters": [] 127 | }, 128 | { 129 | "methods": [ 130 | { 131 | "securedBy": [ 132 | { 133 | "schemeName": "oauth_2_0" 134 | } 135 | ], 136 | "method": "get", 137 | "allUriParameters": [] 138 | }, 139 | { 140 | "securedBy": [ 141 | { 142 | "schemeName": "oauth_2_0" 143 | }, 144 | null 145 | ], 146 | "method": "post", 147 | "allUriParameters": [] 148 | }, 149 | { 150 | "securedBy": [ 151 | { 152 | "schemeName": "oauth_2_0_withscopes", 153 | "scopes": [ 154 | "remove-b" 155 | ] 156 | }, 157 | null 158 | ], 159 | "method": "delete", 160 | "allUriParameters": [] 161 | } 162 | ], 163 | "relativeUri": "/B", 164 | "displayName": "/B", 165 | "relativeUriPathSegments": [ 166 | "B" 167 | ], 168 | "absoluteUri": "/B", 169 | "parentUrl": "", 170 | "uniqueId": "b", 171 | "allUriParameters": [] 172 | }, 173 | { 174 | "methods": [ 175 | { 176 | "securedBy": [ 177 | { 178 | "schemeName": "oauth_2_0_withscopes", 179 | "scopes": [ 180 | "read-c" 181 | ] 182 | }, 183 | { 184 | "schemeName": "custom_scheme" 185 | } 186 | ], 187 | "method": "get", 188 | "allUriParameters": [] 189 | } 190 | ], 191 | "relativeUri": "/C", 192 | "displayName": "/C", 193 | "relativeUriPathSegments": [ 194 | "C" 195 | ], 196 | "absoluteUri": "/C", 197 | "parentUrl": "", 198 | "uniqueId": "c", 199 | "allUriParameters": [] 200 | } 201 | ] 202 | } -------------------------------------------------------------------------------- /test/parameters.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('parameters.raml', () => { 10 | let obj; 11 | 12 | before(done => { 13 | raml2obj.parse('test/parameters.raml').then( 14 | result => { 15 | obj = result; 16 | done(); 17 | }, 18 | error => { 19 | console.log('error', error); 20 | } 21 | ); 22 | }); 23 | 24 | it('should test the basic properties of the raml object', () => { 25 | assert.strictEqual(obj.title, 'Lots of parameters'); 26 | assert.strictEqual(obj.resources.length, 1); 27 | }); 28 | 29 | it('should test the /account resource', () => { 30 | const resource = obj.resources[0]; 31 | 32 | assert.strictEqual(resource.relativeUri, '/account'); 33 | assert.strictEqual(resource.displayName, '/account'); 34 | assert.strictEqual(resource.parentUrl, ''); 35 | assert.strictEqual(resource.allUriParameters.length, 0); 36 | assert.strictEqual(resource.methods.length, 1); 37 | 38 | const method = resource.methods[0]; 39 | 40 | assert.strictEqual(method.allUriParameters.length, 0); 41 | assert.strictEqual(method.method, 'post'); 42 | assert.strictEqual(method.body.length, 1); 43 | assert.strictEqual(method.body[0].name, 'application/json'); 44 | assert.strictEqual(method.body[0].type, 'any'); 45 | assert.strictEqual( 46 | method.body[0].examples[0].value, 47 | '{\n "email": "john@example.com",\n "password": "super_secret",\n "name": "John Doe"\n}' 48 | ); 49 | assert.strictEqual(method.responses.length, 1); 50 | assert.strictEqual(method.responses[0].code, '200'); 51 | assert.strictEqual( 52 | method.responses[0].description, 53 | 'Account was created and user is now logged in' 54 | ); 55 | }); 56 | 57 | it('should test the /account/find resource', () => { 58 | const resource = obj.resources[0].resources[0]; 59 | 60 | assert.strictEqual(resource.relativeUri, '/find'); 61 | assert.strictEqual(resource.displayName, '/find'); 62 | assert.strictEqual(resource.parentUrl, '/account'); 63 | assert.deepEqual(resource.allUriParameters, []); 64 | assert.strictEqual(resource.methods.length, 1); 65 | 66 | const method = resource.methods[0]; 67 | 68 | assert.strictEqual(method.allUriParameters.length, 0); 69 | assert.strictEqual(method.method, 'get'); 70 | 71 | assert.strictEqual(method.queryParameters.length, 3); 72 | assert.strictEqual(method.queryParameters[0].name, 'name'); 73 | assert.strictEqual(method.queryParameters[0].displayName, 'name'); 74 | assert.strictEqual(method.queryParameters[0].type, 'string'); 75 | assert.strictEqual( 76 | method.queryParameters[0].examples[0].value, 77 | 'Naruto Uzumaki' 78 | ); 79 | assert.strictEqual(method.queryParameters[0].required, true); 80 | assert.strictEqual( 81 | method.queryParameters[0].description, 82 | 'name on account' 83 | ); 84 | 85 | assert.strictEqual(method.queryParameters[1].name, 'gender'); 86 | assert.strictEqual(method.queryParameters[1].displayName, 'gender'); 87 | assert.strictEqual(method.queryParameters[1].type, 'string'); 88 | assert.strictEqual(method.queryParameters[1].required, true); 89 | assert.deepEqual(method.queryParameters[1].enum, ['male', 'female']); 90 | 91 | assert.strictEqual(method.queryParameters[2].name, 'number'); 92 | assert.strictEqual(method.queryParameters[2].displayName, 'number'); 93 | assert.strictEqual(method.queryParameters[2].type, 'integer'); 94 | assert.strictEqual(method.queryParameters[2].required, true); 95 | assert.strictEqual(method.queryParameters[2].default, 42); 96 | }); 97 | 98 | it('should test the /account/{id} resource', () => { 99 | const resource = obj.resources[0].resources[1]; 100 | 101 | assert.strictEqual(resource.relativeUri, '/{id}'); 102 | assert.strictEqual(resource.displayName, '/{id}'); 103 | assert.strictEqual(resource.parentUrl, '/account'); 104 | assert.strictEqual(resource.methods.length, 3); 105 | 106 | assert.strictEqual(resource.uriParameters.length, 1); 107 | assert.strictEqual(resource.uriParameters[0].name, 'id'); 108 | assert.strictEqual(resource.uriParameters[0].displayName, 'id'); 109 | assert.strictEqual(resource.uriParameters[0].type, 'string'); 110 | assert.strictEqual(resource.uriParameters[0].required, true); 111 | assert.strictEqual( 112 | resource.uriParameters[0].description, 113 | 'account identifier' 114 | ); 115 | assert.strictEqual(resource.uriParameters[0].minLength, 1); 116 | assert.strictEqual(resource.uriParameters[0].maxLength, 10); 117 | 118 | assert.deepEqual(resource.allUriParameters, resource.uriParameters); 119 | 120 | const get = resource.methods[0]; 121 | 122 | assert.strictEqual(get.method, 'get'); 123 | assert.strictEqual(get.allUriParameters.length, 1); 124 | assert.strictEqual(get.allUriParameters[0].name, 'id'); 125 | assert.strictEqual(get.headers.length, 1); 126 | assert.strictEqual(get.headers[0].name, 'Authorization'); 127 | assert.strictEqual(get.headers[0].displayName, 'Authorization'); 128 | assert.strictEqual(get.headers[0].type, 'string'); 129 | assert.strictEqual( 130 | get.headers[0].examples[0].value, 131 | 'Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n' 132 | ); 133 | assert.strictEqual(get.headers[0].required, true); 134 | assert.strictEqual( 135 | get.headers[0].description, 136 | 'Basic authentication header' 137 | ); 138 | 139 | const put = resource.methods[1]; 140 | 141 | assert.strictEqual(put.method, 'put'); 142 | assert.strictEqual(put.allUriParameters.length, 1); 143 | assert.strictEqual(put.allUriParameters[0].name, 'id'); 144 | assert.strictEqual(put.body.length, 1); 145 | assert.strictEqual(put.body[0].name, 'application/x-www-form-urlencoded'); 146 | assert.strictEqual(put.body[0].type, 'object'); 147 | assert.strictEqual(put.body[0].properties.length, 2); 148 | assert.strictEqual(put.body[0].properties[0].name, 'name'); 149 | assert.strictEqual(put.body[0].properties[0].examples.length, 2); 150 | assert.strictEqual( 151 | put.body[0].properties[0].examples[0].value, 152 | 'Naruto Uzumaki' 153 | ); 154 | assert.strictEqual( 155 | put.body[0].properties[0].examples[1].value, 156 | 'Kevin Renskers' 157 | ); 158 | assert.strictEqual(put.body[0].properties[1].name, 'gender'); 159 | }); 160 | 161 | it('should test the /account/{id}/{id} resource', () => { 162 | const resource = obj.resources[0].resources[1].resources[0]; 163 | 164 | assert.strictEqual(resource.relativeUri, '/{id}'); 165 | assert.strictEqual(resource.displayName, '/{id}'); 166 | assert.strictEqual(resource.parentUrl, '/account/{id}'); 167 | 168 | assert.strictEqual(resource.uriParameters.length, 1); 169 | assert.strictEqual(resource.uriParameters[0].name, 'id'); 170 | 171 | assert.strictEqual(resource.allUriParameters.length, 2); 172 | assert.strictEqual(resource.allUriParameters[0].name, 'id'); 173 | assert.strictEqual(resource.allUriParameters[1].name, 'id'); 174 | 175 | const get = resource.methods[0]; 176 | assert.strictEqual(get.responses.length, 1); 177 | assert.strictEqual(get.responses[0].headers.length, 1); 178 | assert.strictEqual(get.responses[0].headers[0].name, 'WWW-Authenticate'); 179 | assert.strictEqual( 180 | get.responses[0].headers[0].description, 181 | 'user was not authorized' 182 | ); 183 | }); 184 | }); 185 | }); 186 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const raml = require('raml-1-parser'); 4 | const tools = require('datatype-expansion'); 5 | const fs = require('fs'); 6 | const makeExamplesAndTypesConsistent = require('./consistency-helpers'); 7 | const helpers = require('./arrays-objects-helpers'); 8 | 9 | function _makeUniqueId(string) { 10 | const stringWithSpacesReplaced = string.replace(/\W/g, '_'); 11 | const stringWithLeadingUnderscoreRemoved = stringWithSpacesReplaced.replace( 12 | new RegExp('^_+'), 13 | '' 14 | ); 15 | return stringWithLeadingUnderscoreRemoved.toLowerCase(); 16 | } 17 | 18 | // Add unique id's and parent URL's plus parent URI parameters to resources 19 | function _addRaml2htmlProperties(ramlObj, parentUrl, allUriParameters) { 20 | // Add unique id's to top level documentation chapters 21 | if (ramlObj.documentation) { 22 | ramlObj.documentation.forEach(docSection => { 23 | docSection.uniqueId = _makeUniqueId(docSection.title); 24 | }); 25 | } 26 | 27 | if (!ramlObj.resources) { 28 | return ramlObj; 29 | } 30 | 31 | ramlObj.resources.forEach(resource => { 32 | resource.parentUrl = parentUrl || ''; 33 | resource.uniqueId = _makeUniqueId( 34 | resource.parentUrl + resource.relativeUri 35 | ); 36 | resource.allUriParameters = []; 37 | 38 | if (allUriParameters) { 39 | resource.allUriParameters.push.apply( 40 | resource.allUriParameters, 41 | allUriParameters 42 | ); 43 | } 44 | 45 | if (resource.uriParameters) { 46 | resource.uriParameters.forEach(uriParameter => { 47 | resource.allUriParameters.push(uriParameter); 48 | }); 49 | } 50 | 51 | // Copy the RESOURCE uri parameters to the METHOD, because that's where they will be rendered. 52 | if (resource.methods) { 53 | resource.methods.forEach(method => { 54 | method.allUriParameters = resource.allUriParameters; 55 | }); 56 | } 57 | 58 | _addRaml2htmlProperties( 59 | resource, 60 | resource.parentUrl + resource.relativeUri, 61 | resource.allUriParameters 62 | ); 63 | }); 64 | 65 | return ramlObj; 66 | } 67 | 68 | // This uses the datatype-expansion library to expand all the root type to their canonical expanded form 69 | function _expandRootTypes(types) { 70 | if (!types) { 71 | return types; 72 | } 73 | 74 | Object.keys(types).forEach(key => { 75 | try { 76 | const original = types[key]; 77 | const expanded = tools.expandedForm(original, types, { 78 | trackOriginalType: true, 79 | }); 80 | const canonical = tools.canonicalForm(expanded, { hoistUnions: false }); 81 | // Save a reference to the type as defined in the RAML, so we can differentiate between declared 82 | // and inherited facets, particularly annotations. 83 | canonical.rawType = original; 84 | types[key] = canonical; 85 | } catch (err) { 86 | // Dump the error to stderr and continue with the non-canonical form 87 | console.error( 88 | 'Warning: Unable to canonicalize type "' + key + '": ' + err.message 89 | ); 90 | } 91 | }); 92 | 93 | return types; 94 | } 95 | 96 | function _enhanceRamlObj(ramlObj, options) { 97 | // Override default options 98 | options = Object.assign( 99 | { 100 | collectionFormat: 'objects', 101 | }, 102 | options 103 | ); 104 | 105 | // Some of the structures (like `types`) are an array that hold key/value pairs, which is very annoying to work with. 106 | // Let's make them into a simple object, this makes it easy to use them for direct lookups. 107 | // 108 | // EXAMPLE of these structures: 109 | // [ 110 | // { foo: { ... } }, 111 | // { bar: { ... } }, 112 | // ] 113 | // 114 | // EXAMPLE of what we want (default option "objects") 115 | // { foo: { ... }, bar: { ... } } 116 | // 117 | // EXAMPLE of what we want (option "arrays") 118 | // [ { key: "foo", ... }, { key: "bar", ... } ] 119 | // the "arrays" option will be evalulated at the very end to so the conversion and cleanup code 120 | // does not have to handle different data structures. 121 | ramlObj = helpers.arraysToObjects(ramlObj); 122 | 123 | // We want to expand inherited root types, so that later on when we copy type properties into an object, 124 | // we get the full graph. 125 | const types = makeExamplesAndTypesConsistent(_expandRootTypes(ramlObj.types)); 126 | // Delete the types from the ramlObj so it's not processed again later on. 127 | delete ramlObj.types; 128 | 129 | // Recursively go over the entire object and make all examples and types consistent. 130 | ramlObj = makeExamplesAndTypesConsistent(ramlObj, types); 131 | 132 | // Other structures (like `responses`) are an object that hold other wrapped objects. 133 | // Flatten this to simple (non-wrapped) objects in an array instead, 134 | // this makes it easy to loop over them in raml2html / raml2md. 135 | // 136 | // EXAMPLE of these structures: 137 | // { 138 | // foo: { 139 | // name: "foo!" 140 | // }, 141 | // bar: { 142 | // name: "bar" 143 | // } 144 | // } 145 | // 146 | // EXAMPLE of what we want: 147 | // [ { name: "foo!", key: "foo" }, { name: "bar", key: "bar" } ] 148 | ramlObj = helpers.recursiveObjectToArray(ramlObj); 149 | 150 | // Now add all the properties and things that we need for raml2html, stuff like the uniqueId, parentUrl, 151 | // and allUriParameters. 152 | ramlObj = _addRaml2htmlProperties(ramlObj); 153 | 154 | if (types) { 155 | ramlObj.types = types; 156 | } 157 | 158 | // convert to optional variations in the output structure: 159 | if (options.collectionFormat === 'arrays') { 160 | // repeat recursive to also clean up the types: 161 | ramlObj = helpers.recursiveObjectToArray(ramlObj); 162 | // modify the top-level collections to be arrays 163 | ramlObj = helpers.objectsToArrays(ramlObj); 164 | } 165 | 166 | return ramlObj; 167 | } 168 | 169 | function _validateLoadedRaml(ramlObj) { 170 | if (ramlObj._node._universe._typedVersion === '0.8') { 171 | throw new Error('_sourceToRamlObj: only RAML 1.0 is supported!'); 172 | } 173 | 174 | if (ramlObj.expand) { 175 | return ramlObj.expand(true).toJSON({ serializeMetadata: false }); 176 | } 177 | 178 | return new Promise((resolve, reject) => { 179 | reject( 180 | new Error( 181 | '_sourceToRamlObj: source could not be parsed. Is it a root RAML file?' 182 | ) 183 | ); 184 | }); 185 | } 186 | 187 | function _sourceToRamlObj(source, options = {}) { 188 | // "options" was originally a validation flag 189 | if (typeof options === 'boolean') { 190 | options = { validate: options }; 191 | } 192 | if (typeof source === 'string') { 193 | if (fs.existsSync(source) || source.indexOf('http') === 0) { 194 | // Parse as file or url 195 | return raml 196 | .loadApi(source, options.extensionsAndOverlays || [], { 197 | rejectOnErrors: !!options.validate, 198 | httpResolver: options.httpResolver, 199 | fsResolver: options.fsResolver, 200 | }) 201 | .then(_validateLoadedRaml); 202 | } else if (source) { 203 | return raml 204 | .parseRAML(source, { 205 | rejectOnErrors: !!options.validate, 206 | httpResolver: options.httpResolver, 207 | fsResolver: options.fsResolver, 208 | }) 209 | .then(_validateLoadedRaml); 210 | } 211 | 212 | return new Promise((resolve, reject) => { 213 | reject( 214 | new Error( 215 | '_sourceToRamlObj: source does not exist or cannot be parsed.' 216 | ) 217 | ); 218 | }); 219 | } else if (source instanceof Buffer) { 220 | return raml 221 | .parseRAML(source.toString(), { 222 | rejectOnErrors: !!options.validate, 223 | httpResolver: options.httpResolver, 224 | fsResolver: options.fsResolver, 225 | }) 226 | .then(_validateLoadedRaml); 227 | } else if (typeof source === 'object') { 228 | // Parse RAML object directly 229 | return new Promise(resolve => { 230 | resolve(source); 231 | }); 232 | } 233 | 234 | return new Promise((resolve, reject) => { 235 | reject( 236 | new Error( 237 | '_sourceToRamlObj: You must supply either file, url or object as source.' 238 | ) 239 | ); 240 | }); 241 | } 242 | 243 | module.exports.parse = function(source, options) { 244 | return _sourceToRamlObj(source, options).then(ramlObj => 245 | _enhanceRamlObj(ramlObj, options) 246 | ); 247 | }; 248 | -------------------------------------------------------------------------------- /test/typeexample.flatObject.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Test", 3 | "resources": [ 4 | { 5 | "methods": [ 6 | { 7 | "description": "bla", 8 | "method": "get", 9 | "allUriParameters": [ 10 | { 11 | "name": "objectid", 12 | "displayName": "objectid", 13 | "typePropertyKind": "TYPE_EXPRESSION", 14 | "type": "string", 15 | "required": true, 16 | "description": "A valid MongoDB object id.", 17 | "orderHint": 0, 18 | "rawType": { 19 | "name": "objectid", 20 | "displayName": "objectid", 21 | "typePropertyKind": "TYPE_EXPRESSION", 22 | "type": [ 23 | "string" 24 | ], 25 | "example": "576bca8b70fddb149c4a9e92", 26 | "description": "A valid MongoDB object id.", 27 | "structuredExample": { 28 | "value": "576bca8b70fddb149c4a9e92", 29 | "strict": true, 30 | "name": null, 31 | "structuredValue": "576bca8b70fddb149c4a9e92" 32 | }, 33 | "orderHint": 0 34 | }, 35 | "examples": [ 36 | { 37 | "value": "576bca8b70fddb149c4a9e92", 38 | "strict": true, 39 | "name": null, 40 | "structuredValue": "576bca8b70fddb149c4a9e92" 41 | } 42 | ], 43 | "key": "id" 44 | } 45 | ] 46 | } 47 | ], 48 | "uriParameters": [ 49 | { 50 | "name": "objectid", 51 | "displayName": "objectid", 52 | "typePropertyKind": "TYPE_EXPRESSION", 53 | "type": "string", 54 | "required": true, 55 | "description": "A valid MongoDB object id.", 56 | "orderHint": 0, 57 | "rawType": { 58 | "name": "objectid", 59 | "displayName": "objectid", 60 | "typePropertyKind": "TYPE_EXPRESSION", 61 | "type": [ 62 | "string" 63 | ], 64 | "example": "576bca8b70fddb149c4a9e92", 65 | "description": "A valid MongoDB object id.", 66 | "structuredExample": { 67 | "value": "576bca8b70fddb149c4a9e92", 68 | "strict": true, 69 | "name": null, 70 | "structuredValue": "576bca8b70fddb149c4a9e92" 71 | }, 72 | "orderHint": 0 73 | }, 74 | "examples": [ 75 | { 76 | "value": "576bca8b70fddb149c4a9e92", 77 | "strict": true, 78 | "name": null, 79 | "structuredValue": "576bca8b70fddb149c4a9e92" 80 | } 81 | ], 82 | "key": "id" 83 | } 84 | ], 85 | "relativeUri": "/company", 86 | "displayName": "/company", 87 | "relativeUriPathSegments": [ 88 | "company" 89 | ], 90 | "absoluteUri": "/company", 91 | "parentUrl": "", 92 | "uniqueId": "company", 93 | "allUriParameters": [ 94 | { 95 | "name": "objectid", 96 | "displayName": "objectid", 97 | "typePropertyKind": "TYPE_EXPRESSION", 98 | "type": "string", 99 | "required": true, 100 | "description": "A valid MongoDB object id.", 101 | "orderHint": 0, 102 | "rawType": { 103 | "name": "objectid", 104 | "displayName": "objectid", 105 | "typePropertyKind": "TYPE_EXPRESSION", 106 | "type": [ 107 | "string" 108 | ], 109 | "example": "576bca8b70fddb149c4a9e92", 110 | "description": "A valid MongoDB object id.", 111 | "structuredExample": { 112 | "value": "576bca8b70fddb149c4a9e92", 113 | "strict": true, 114 | "name": null, 115 | "structuredValue": "576bca8b70fddb149c4a9e92" 116 | }, 117 | "orderHint": 0 118 | }, 119 | "examples": [ 120 | { 121 | "value": "576bca8b70fddb149c4a9e92", 122 | "strict": true, 123 | "name": null, 124 | "structuredValue": "576bca8b70fddb149c4a9e92" 125 | } 126 | ], 127 | "key": "id" 128 | } 129 | ] 130 | } 131 | ], 132 | "types": [ 133 | { 134 | "type": "string", 135 | "name": "objectid", 136 | "displayName": "objectid", 137 | "typePropertyKind": "TYPE_EXPRESSION", 138 | "description": "A valid MongoDB object id.", 139 | "orderHint": 0, 140 | "rawType": { 141 | "name": "objectid", 142 | "displayName": "objectid", 143 | "typePropertyKind": "TYPE_EXPRESSION", 144 | "type": [ 145 | "string" 146 | ], 147 | "example": "576bca8b70fddb149c4a9e92", 148 | "description": "A valid MongoDB object id.", 149 | "structuredExample": { 150 | "value": "576bca8b70fddb149c4a9e92", 151 | "strict": true, 152 | "name": null, 153 | "structuredValue": "576bca8b70fddb149c4a9e92" 154 | }, 155 | "orderHint": 0 156 | }, 157 | "examples": [ 158 | { 159 | "value": "576bca8b70fddb149c4a9e92", 160 | "strict": true, 161 | "name": null, 162 | "structuredValue": "576bca8b70fddb149c4a9e92" 163 | } 164 | ], 165 | "key": "objectid" 166 | }, 167 | { 168 | "type": "object", 169 | "properties": [ 170 | { 171 | "type": "string", 172 | "name": "first", 173 | "displayName": "first", 174 | "typePropertyKind": "TYPE_EXPRESSION", 175 | "required": true, 176 | "key": "first" 177 | }, 178 | { 179 | "type": "number", 180 | "name": "second", 181 | "displayName": "second", 182 | "typePropertyKind": "TYPE_EXPRESSION", 183 | "required": true, 184 | "key": "second" 185 | } 186 | ], 187 | "name": "objectType", 188 | "displayName": "objectType", 189 | "typePropertyKind": "TYPE_EXPRESSION", 190 | "description": "a type with properties.", 191 | "orderHint": 1, 192 | "additionalProperties": true, 193 | "rawType": { 194 | "name": "objectType", 195 | "displayName": "objectType", 196 | "typePropertyKind": "TYPE_EXPRESSION", 197 | "type": [ 198 | "object" 199 | ], 200 | "description": "a type with properties.", 201 | "properties": [ 202 | { 203 | "name": "first", 204 | "displayName": "first", 205 | "typePropertyKind": "TYPE_EXPRESSION", 206 | "type": [ 207 | "string" 208 | ], 209 | "required": true, 210 | "key": "first" 211 | }, 212 | { 213 | "name": "second", 214 | "displayName": "second", 215 | "typePropertyKind": "TYPE_EXPRESSION", 216 | "type": [ 217 | "number" 218 | ], 219 | "required": true, 220 | "key": "second" 221 | } 222 | ], 223 | "orderHint": 1 224 | }, 225 | "key": "objectType" 226 | } 227 | ] 228 | } -------------------------------------------------------------------------------- /test/worldmusic.spec.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node, mocha */ 2 | 3 | 'use strict'; 4 | 5 | const raml2obj = require('..'); 6 | const assert = require('assert'); 7 | 8 | describe('raml2obj', () => { 9 | describe('worldmusic.raml', function() { 10 | this.timeout(10000); 11 | 12 | let obj; 13 | 14 | before(done => { 15 | raml2obj.parse('test/worldmusic.raml').then( 16 | result => { 17 | obj = result; 18 | done(); 19 | }, 20 | error => { 21 | console.log(JSON.stringify(error)); 22 | } 23 | ); 24 | }); 25 | 26 | it('should test the basic properties of the raml object', () => { 27 | assert.strictEqual(obj.title, 'World Music API'); 28 | assert.strictEqual(obj.version, 'v1'); 29 | assert.strictEqual( 30 | obj.baseUri, 31 | 'http://{environment}.musicapi.com/{version}' 32 | ); 33 | assert.strictEqual(obj.resources.length, 3); // /api, /entry, /songs 34 | }); 35 | 36 | it('should test baseUriParameters', () => { 37 | assert.strictEqual(obj.baseUriParameters.length, 2); 38 | 39 | assert.strictEqual(obj.baseUriParameters[0].name, 'environment'); 40 | assert.strictEqual(obj.baseUriParameters[0].type, 'string'); 41 | assert.strictEqual(obj.baseUriParameters[0].required, true); 42 | assert.deepEqual(obj.baseUriParameters[0].enum, [ 43 | 'stg', 44 | 'dev', 45 | 'test', 46 | 'prod', 47 | ]); 48 | 49 | assert.strictEqual(obj.baseUriParameters[1].name, 'version'); 50 | assert.strictEqual(obj.baseUriParameters[1].type, 'string'); 51 | assert.strictEqual(obj.baseUriParameters[1].required, true); 52 | assert.deepEqual(obj.baseUriParameters[1].enum, ['v1']); 53 | }); 54 | 55 | it('should test the documentation', () => { 56 | assert.strictEqual(obj.documentation.length, 2); 57 | 58 | const first = obj.documentation[0]; 59 | const second = obj.documentation[1]; 60 | 61 | assert.strictEqual(first.title, 'Getting Started'); 62 | assert.strictEqual( 63 | first.content, 64 | 'This is a getting started guide for the World Music API.\n' 65 | ); 66 | assert.strictEqual(first.uniqueId, 'getting_started'); 67 | 68 | assert.strictEqual(second.title, 'Legal'); 69 | assert.strictEqual(second.content, 'See http://legal.musicapi.com'); 70 | assert.strictEqual(second.uniqueId, 'legal'); 71 | }); 72 | 73 | it('should test the /api resource', () => { 74 | const resource = obj.resources[0]; 75 | 76 | assert.strictEqual(resource.relativeUri, '/api'); 77 | assert.strictEqual(resource.displayName, '/api'); 78 | assert.strictEqual(resource.parentUrl, ''); 79 | assert.strictEqual(resource.uniqueId, 'api'); 80 | assert.deepEqual(resource.securedBy, [{ schemeName: 'custom_scheme' }]); 81 | assert.strictEqual(resource.allUriParameters.length, 0); 82 | }); 83 | 84 | it('should test the /api methods', () => { 85 | const methods = obj.resources[0].methods; 86 | 87 | assert.strictEqual(methods.length, 2); 88 | 89 | const get = methods[0]; 90 | 91 | assert.strictEqual(get.method, 'get'); 92 | assert.strictEqual(get.allUriParameters.length, 0); 93 | assert.deepEqual(get.securedBy, [{ schemeName: 'custom_scheme' }]); 94 | 95 | assert.strictEqual(get.queryString.name, 'queryString'); 96 | assert.strictEqual(get.queryString.type, 'object'); 97 | assert.strictEqual(get.queryString.properties.length, 2); 98 | assert.strictEqual(get.queryString.properties[0].name, 'start'); 99 | assert.strictEqual(get.queryString.properties[0].required, false); 100 | assert.strictEqual(get.queryString.properties[0].type, 'number'); 101 | 102 | assert.strictEqual(get.queryString.properties[1].name, 'page-size'); 103 | assert.strictEqual(get.queryString.properties[1].required, false); 104 | assert.strictEqual(get.queryString.properties[1].type, 'number'); 105 | 106 | const post = methods[1]; 107 | 108 | assert.strictEqual(post.method, 'post'); 109 | assert.strictEqual(post.allUriParameters.length, 0); 110 | assert.deepEqual(post.securedBy, [{ schemeName: 'custom_scheme' }]); 111 | assert.strictEqual(post.body.length, 1); 112 | assert.strictEqual(post.body[0].name, 'RamlDataType'); 113 | assert.strictEqual(post.body[0].key, 'application/json'); 114 | assert.strictEqual(post.body[0].type, 'object'); 115 | assert.strictEqual(post.body[0].properties.length, 14); 116 | assert.strictEqual( 117 | post.body[0].properties[4].examples[0].value, 118 | 'very well made' 119 | ); 120 | assert.strictEqual(post.body[0].properties[10].key, 'NilValue'); 121 | assert.strictEqual( 122 | post.body[0].properties[10].properties[1].name, 123 | 'comment' 124 | ); 125 | assert.strictEqual( 126 | post.body[0].properties[10].properties[1].type, 127 | 'union' 128 | ); 129 | assert.strictEqual( 130 | post.body[0].properties[10].properties[1].anyOf[0].type, 131 | 'string' 132 | ); 133 | assert.strictEqual( 134 | post.body[0].properties[10].properties[1].anyOf[1].type, 135 | 'nil' 136 | ); 137 | assert.strictEqual(post.body[0].properties[11].key, 'CatOrDog'); 138 | assert.strictEqual(post.body[0].properties[11].type, 'union'); 139 | assert.strictEqual(post.body[0].properties[11].anyOf[0].type, 'object'); 140 | assert.strictEqual( 141 | post.body[0].properties[11].anyOf[0].originalType, 142 | 'ApiLib.Cat' 143 | ); 144 | assert.strictEqual(post.body[0].properties[11].anyOf[1].type, 'object'); 145 | assert.strictEqual( 146 | post.body[0].properties[11].anyOf[1].originalType, 147 | 'ApiLib.Dog' 148 | ); 149 | }); 150 | 151 | it('should test the /entry resource', () => { 152 | const resource = obj.resources[1]; 153 | 154 | assert.strictEqual(resource.relativeUri, '/entry'); 155 | assert.strictEqual(resource.displayName, '/entry'); 156 | assert.strictEqual(resource.parentUrl, ''); 157 | assert.strictEqual(resource.uniqueId, 'entry'); 158 | assert.strictEqual(resource.type, 'collection'); 159 | assert.deepEqual(resource.securedBy, [{ schemeName: 'custom_scheme' }]); 160 | assert.strictEqual(resource.allUriParameters.length, 0); 161 | }); 162 | 163 | it('should test the /entry methods', () => { 164 | const methods = obj.resources[1].methods; 165 | 166 | assert.strictEqual(methods.length, 2); 167 | 168 | const post = methods[0]; 169 | 170 | assert.strictEqual(post.method, 'post'); 171 | assert.strictEqual(post.allUriParameters.length, 0); 172 | assert.deepEqual(post.securedBy, [{ schemeName: 'custom_scheme' }]); 173 | assert.strictEqual(post.responses.length, 1); 174 | assert.strictEqual(post.responses[0].code, '200'); 175 | assert.strictEqual(post.responses[0].body.length, 1); 176 | assert.strictEqual(post.responses[0].body[0].name, 'AnotherEntry'); 177 | assert.strictEqual(post.responses[0].body[0].key, 'application/json'); 178 | assert.strictEqual(post.responses[0].body[0].type, 'json'); 179 | assert.strictEqual( 180 | post.responses[0].body[0].content.indexOf('{\n "type": "array"'), 181 | 0 182 | ); 183 | 184 | const get = methods[1]; 185 | 186 | assert.strictEqual(get.method, 'get'); 187 | assert.strictEqual(get.description, 'returns a list of entry'); 188 | assert.strictEqual(get.allUriParameters.length, 0); 189 | assert.deepEqual(get.securedBy, [{ schemeName: 'custom_scheme' }]); 190 | assert.strictEqual(get.responses.length, 1); 191 | assert.strictEqual(get.responses[0].code, '200'); 192 | assert.strictEqual(get.responses[0].body.length, 1); 193 | assert.strictEqual(get.responses[0].body[0].name, 'application/json'); 194 | assert.deepEqual(get.responses[0].body[0].schema, ['Entry']); 195 | }); 196 | 197 | it('should test the /songs resource', () => { 198 | const resource = obj.resources[2]; 199 | 200 | assert.strictEqual(resource.relativeUri, '/songs'); 201 | assert.strictEqual(resource.displayName, 'Songs'); 202 | assert.strictEqual( 203 | resource.description, 204 | 'Access to all songs inside the music world library.' 205 | ); 206 | assert.strictEqual(resource.parentUrl, ''); 207 | assert.strictEqual(resource.uniqueId, 'songs'); 208 | assert.deepEqual(resource.securedBy, [{ schemeName: 'custom_scheme' }]); 209 | assert.strictEqual(resource.allUriParameters.length, 0); 210 | assert.strictEqual(resource.annotations.length, 1); 211 | assert.strictEqual(resource.annotations[0].name, 'ready'); 212 | assert.deepEqual(resource.is, ['secured']); 213 | assert.strictEqual(resource.resources.length, 1); 214 | }); 215 | 216 | it('should test the /songs methods', () => { 217 | const methods = obj.resources[2].methods; 218 | 219 | assert.strictEqual(methods.length, 2); 220 | 221 | const get = methods[0]; 222 | 223 | assert.strictEqual(get.method, 'get'); 224 | assert.strictEqual(get.allUriParameters.length, 0); 225 | assert.deepEqual(get.securedBy, [{ schemeName: 'oauth_2_0' }, null]); 226 | 227 | assert.strictEqual(get.annotations.length, 1); 228 | assert.strictEqual(get.annotations[0].name, 'monitoringInterval'); 229 | assert.strictEqual(get.annotations[0].structuredValue, 30); 230 | 231 | assert.strictEqual(get.queryParameters.length, 2); 232 | 233 | const post = methods[1]; 234 | 235 | assert.strictEqual(post.method, 'post'); 236 | assert.deepEqual(post.securedBy, [{ schemeName: 'custom_scheme' }]); 237 | assert.strictEqual(post.queryParameters.length, 1); 238 | }); 239 | 240 | it('should test the /songs/{songId} resource', () => { 241 | const resource = obj.resources[2].resources[0]; 242 | 243 | assert.strictEqual(resource.relativeUri, '/{songId}'); 244 | assert.strictEqual(resource.displayName, '/{songId}'); 245 | assert.strictEqual(resource.parentUrl, '/songs'); 246 | assert.strictEqual(resource.uniqueId, 'songs__songid_'); 247 | assert.deepEqual(resource.securedBy, [{ schemeName: 'custom_scheme' }]); 248 | assert.strictEqual(resource.allUriParameters.length, 1); 249 | }); 250 | 251 | it('should test the /songs/{songId} methods', () => { 252 | const methods = obj.resources[2].resources[0].methods; 253 | 254 | assert.strictEqual(methods.length, 1); 255 | 256 | const get = methods[0]; 257 | 258 | assert.strictEqual(get.method, 'get'); 259 | assert.strictEqual(get.allUriParameters.length, 1); 260 | assert.strictEqual(get.annotations.length, 1); 261 | assert.strictEqual(get.responses.length, 1); 262 | assert.deepEqual(get.securedBy, [{ schemeName: 'custom_scheme' }]); 263 | 264 | assert.strictEqual(get.responses[0].body.length, 2); 265 | assert.strictEqual(get.responses[0].body[0].displayName, 'Song'); 266 | assert.strictEqual(get.responses[0].body[0].key, 'application/json'); 267 | assert.strictEqual(get.responses[0].body[0].examples.length, 2); 268 | assert.strictEqual(get.responses[0].body[0].type, 'object'); 269 | 270 | assert.strictEqual( 271 | get.responses[0].body[1].type.indexOf( 272 | '' 273 | ), 274 | 0 275 | ); 276 | }); 277 | }); 278 | }); 279 | -------------------------------------------------------------------------------- /test/array-of-foo.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Test", 3 | "description": "Test of array support", 4 | "version": "v1", 5 | "baseUri": "https://exmaple.com/{version}", 6 | "baseUriParameters": [ 7 | { 8 | "name": "version", 9 | "displayName": "version", 10 | "typePropertyKind": "TYPE_EXPRESSION", 11 | "type": "string", 12 | "required": true, 13 | "enum": [ 14 | "v1" 15 | ], 16 | "key": "version" 17 | } 18 | ], 19 | "protocols": [ 20 | "HTTPS" 21 | ], 22 | "mediaType": "application/json", 23 | "resources": [ 24 | { 25 | "methods": [ 26 | { 27 | "responses": [ 28 | { 29 | "code": "200", 30 | "body": [ 31 | { 32 | "name": "Foos", 33 | "displayName": "Foos", 34 | "typePropertyKind": "TYPE_EXPRESSION", 35 | "type": "array", 36 | "items": { 37 | "type": "object", 38 | "properties": [ 39 | { 40 | "type": "string", 41 | "name": "id", 42 | "displayName": "id", 43 | "typePropertyKind": "TYPE_EXPRESSION", 44 | "required": true, 45 | "key": "id" 46 | } 47 | ], 48 | "name": "Foo", 49 | "displayName": "Foo", 50 | "typePropertyKind": "TYPE_EXPRESSION", 51 | "additionalProperties": true, 52 | "rawType": { 53 | "name": "Foo", 54 | "displayName": "Foo", 55 | "typePropertyKind": "TYPE_EXPRESSION", 56 | "type": [ 57 | "object" 58 | ], 59 | "properties": [ 60 | { 61 | "name": "id", 62 | "displayName": "id", 63 | "typePropertyKind": "TYPE_EXPRESSION", 64 | "type": [ 65 | "string" 66 | ], 67 | "required": true, 68 | "key": "id" 69 | } 70 | ] 71 | }, 72 | "originalType": "Foo" 73 | }, 74 | "rawType": { 75 | "name": "Foos", 76 | "displayName": "Foos", 77 | "typePropertyKind": "TYPE_EXPRESSION", 78 | "type": [ 79 | "array" 80 | ], 81 | "items": "Foo" 82 | }, 83 | "key": "application/json" 84 | } 85 | ], 86 | "key": "200" 87 | } 88 | ], 89 | "protocols": [ 90 | "HTTPS" 91 | ], 92 | "method": "get", 93 | "allUriParameters": [] 94 | }, 95 | { 96 | "responses": [ 97 | { 98 | "code": "200", 99 | "body": [ 100 | { 101 | "name": "application/json", 102 | "displayName": "application/json", 103 | "typePropertyKind": "TYPE_EXPRESSION", 104 | "type": "array", 105 | "items": { 106 | "type": "object", 107 | "properties": [ 108 | { 109 | "type": "string", 110 | "name": "id", 111 | "displayName": "id", 112 | "typePropertyKind": "TYPE_EXPRESSION", 113 | "required": true, 114 | "key": "id" 115 | } 116 | ], 117 | "name": "Foo", 118 | "displayName": "Foo", 119 | "typePropertyKind": "TYPE_EXPRESSION", 120 | "additionalProperties": true, 121 | "rawType": { 122 | "name": "Foo", 123 | "displayName": "Foo", 124 | "typePropertyKind": "TYPE_EXPRESSION", 125 | "type": [ 126 | "object" 127 | ], 128 | "properties": [ 129 | { 130 | "name": "id", 131 | "displayName": "id", 132 | "typePropertyKind": "TYPE_EXPRESSION", 133 | "type": [ 134 | "string" 135 | ], 136 | "required": true, 137 | "key": "id" 138 | } 139 | ] 140 | } 141 | }, 142 | "minItems": 1, 143 | "maxItems": 200, 144 | "key": "application/json" 145 | } 146 | ], 147 | "key": "200" 148 | } 149 | ], 150 | "protocols": [ 151 | "HTTPS" 152 | ], 153 | "method": "post", 154 | "allUriParameters": [] 155 | } 156 | ], 157 | "relativeUri": "/foos", 158 | "displayName": "/foos", 159 | "relativeUriPathSegments": [ 160 | "foos" 161 | ], 162 | "absoluteUri": "https://exmaple.com/{version}/foos", 163 | "parentUrl": "", 164 | "uniqueId": "foos", 165 | "allUriParameters": [] 166 | } 167 | ], 168 | "types": { 169 | "Foo": { 170 | "type": "object", 171 | "properties": [ 172 | { 173 | "type": "string", 174 | "name": "id", 175 | "displayName": "id", 176 | "typePropertyKind": "TYPE_EXPRESSION", 177 | "required": true, 178 | "key": "id" 179 | } 180 | ], 181 | "name": "Foo", 182 | "displayName": "Foo", 183 | "typePropertyKind": "TYPE_EXPRESSION", 184 | "additionalProperties": true, 185 | "rawType": { 186 | "name": "Foo", 187 | "displayName": "Foo", 188 | "typePropertyKind": "TYPE_EXPRESSION", 189 | "type": [ 190 | "object" 191 | ], 192 | "properties": [ 193 | { 194 | "name": "id", 195 | "displayName": "id", 196 | "typePropertyKind": "TYPE_EXPRESSION", 197 | "type": [ 198 | "string" 199 | ], 200 | "required": true, 201 | "key": "id" 202 | } 203 | ] 204 | } 205 | }, 206 | "Foos": { 207 | "type": "array", 208 | "items": { 209 | "type": "object", 210 | "properties": [ 211 | { 212 | "type": "string", 213 | "name": "id", 214 | "displayName": "id", 215 | "typePropertyKind": "TYPE_EXPRESSION", 216 | "required": true, 217 | "key": "id" 218 | } 219 | ], 220 | "name": "Foo", 221 | "displayName": "Foo", 222 | "typePropertyKind": "TYPE_EXPRESSION", 223 | "additionalProperties": true, 224 | "rawType": { 225 | "name": "Foo", 226 | "displayName": "Foo", 227 | "typePropertyKind": "TYPE_EXPRESSION", 228 | "type": [ 229 | "object" 230 | ], 231 | "properties": [ 232 | { 233 | "name": "id", 234 | "displayName": "id", 235 | "typePropertyKind": "TYPE_EXPRESSION", 236 | "type": [ 237 | "string" 238 | ], 239 | "required": true, 240 | "key": "id" 241 | } 242 | ] 243 | }, 244 | "originalType": "Foo" 245 | }, 246 | "name": "Foos", 247 | "displayName": "Foos", 248 | "typePropertyKind": "TYPE_EXPRESSION", 249 | "rawType": { 250 | "name": "Foos", 251 | "displayName": "Foos", 252 | "typePropertyKind": "TYPE_EXPRESSION", 253 | "type": [ 254 | "array" 255 | ], 256 | "items": "Foo" 257 | } 258 | } 259 | } 260 | } -------------------------------------------------------------------------------- /test/resourceexample.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "resource example", 3 | "resources": [ 4 | { 5 | "methods": [ 6 | { 7 | "responses": [ 8 | { 9 | "code": "200", 10 | "body": [ 11 | { 12 | "name": "ResourceExampleType", 13 | "displayName": "ResourceExampleType", 14 | "typePropertyKind": "TYPE_EXPRESSION", 15 | "type": "object", 16 | "properties": [ 17 | { 18 | "type": "string", 19 | "name": "content", 20 | "displayName": "content", 21 | "typePropertyKind": "TYPE_EXPRESSION", 22 | "required": true, 23 | "key": "content" 24 | } 25 | ], 26 | "examples": [ 27 | { 28 | "value": "{\n \"content\": \"typeExample1\"\n}", 29 | "strict": true, 30 | "name": "typeExample1", 31 | "structuredValue": { 32 | "content": "typeExample1" 33 | }, 34 | "displayName": "typeExample1" 35 | }, 36 | { 37 | "value": "{\n \"content\": \"typeExample2\"\n}", 38 | "strict": true, 39 | "name": "typeExample2", 40 | "structuredValue": { 41 | "content": "typeExample2" 42 | }, 43 | "displayName": "typeExample2" 44 | } 45 | ], 46 | "additionalProperties": false, 47 | "rawType": { 48 | "name": "ResourceExampleType", 49 | "displayName": "ResourceExampleType", 50 | "typePropertyKind": "TYPE_EXPRESSION", 51 | "type": [ 52 | "object" 53 | ], 54 | "examples": [ 55 | { 56 | "value": "{\n \"content\": \"typeExample1\"\n}", 57 | "strict": true, 58 | "name": "typeExample1", 59 | "structuredValue": { 60 | "content": "typeExample1" 61 | } 62 | }, 63 | { 64 | "value": "{\n \"content\": \"typeExample2\"\n}", 65 | "strict": true, 66 | "name": "typeExample2", 67 | "structuredValue": { 68 | "content": "typeExample2" 69 | } 70 | } 71 | ], 72 | "properties": [ 73 | { 74 | "name": "content", 75 | "displayName": "content", 76 | "typePropertyKind": "TYPE_EXPRESSION", 77 | "type": [ 78 | "string" 79 | ], 80 | "required": true, 81 | "key": "content" 82 | } 83 | ], 84 | "additionalProperties": false 85 | }, 86 | "key": "application/json" 87 | } 88 | ], 89 | "key": "200" 90 | }, 91 | { 92 | "code": "202", 93 | "body": [ 94 | { 95 | "name": "ResourceExampleType", 96 | "displayName": "ResourceExampleType", 97 | "typePropertyKind": "TYPE_EXPRESSION", 98 | "type": "object", 99 | "examples": [ 100 | { 101 | "value": "{\n \"content\": \"resourceExample1\"\n}", 102 | "strict": true, 103 | "name": "resourceExample1", 104 | "structuredValue": { 105 | "content": "resourceExample1" 106 | }, 107 | "displayName": "resourceExample1" 108 | }, 109 | { 110 | "value": "{\n \"content\": \"resourceExample2\"\n}", 111 | "strict": true, 112 | "name": "resourceExample2", 113 | "structuredValue": { 114 | "content": "resourceExample2" 115 | }, 116 | "displayName": "resourceExample2" 117 | } 118 | ], 119 | "properties": [ 120 | { 121 | "type": "string", 122 | "name": "content", 123 | "displayName": "content", 124 | "typePropertyKind": "TYPE_EXPRESSION", 125 | "required": true, 126 | "key": "content" 127 | } 128 | ], 129 | "additionalProperties": false, 130 | "rawType": { 131 | "name": "ResourceExampleType", 132 | "displayName": "ResourceExampleType", 133 | "typePropertyKind": "TYPE_EXPRESSION", 134 | "type": [ 135 | "object" 136 | ], 137 | "examples": [ 138 | { 139 | "value": "{\n \"content\": \"typeExample1\"\n}", 140 | "strict": true, 141 | "name": "typeExample1", 142 | "structuredValue": { 143 | "content": "typeExample1" 144 | } 145 | }, 146 | { 147 | "value": "{\n \"content\": \"typeExample2\"\n}", 148 | "strict": true, 149 | "name": "typeExample2", 150 | "structuredValue": { 151 | "content": "typeExample2" 152 | } 153 | } 154 | ], 155 | "properties": [ 156 | { 157 | "name": "content", 158 | "displayName": "content", 159 | "typePropertyKind": "TYPE_EXPRESSION", 160 | "type": [ 161 | "string" 162 | ], 163 | "required": true, 164 | "key": "content" 165 | } 166 | ], 167 | "additionalProperties": false 168 | }, 169 | "key": "application/json" 170 | } 171 | ], 172 | "key": "202" 173 | } 174 | ], 175 | "method": "get", 176 | "allUriParameters": [] 177 | } 178 | ], 179 | "relativeUri": "/example", 180 | "displayName": "/example", 181 | "relativeUriPathSegments": [ 182 | "example" 183 | ], 184 | "absoluteUri": "/example", 185 | "parentUrl": "", 186 | "uniqueId": "example", 187 | "allUriParameters": [] 188 | } 189 | ], 190 | "types": { 191 | "ResourceExampleType": { 192 | "type": "object", 193 | "properties": { 194 | "content": { 195 | "type": "string", 196 | "name": "content", 197 | "displayName": "content", 198 | "typePropertyKind": "TYPE_EXPRESSION", 199 | "required": true, 200 | "key": "content" 201 | } 202 | }, 203 | "name": "ResourceExampleType", 204 | "displayName": "ResourceExampleType", 205 | "typePropertyKind": "TYPE_EXPRESSION", 206 | "examples": [ 207 | { 208 | "value": "{\n \"content\": \"typeExample1\"\n}", 209 | "strict": true, 210 | "name": "typeExample1", 211 | "structuredValue": { 212 | "content": "typeExample1" 213 | }, 214 | "displayName": "typeExample1" 215 | }, 216 | { 217 | "value": "{\n \"content\": \"typeExample2\"\n}", 218 | "strict": true, 219 | "name": "typeExample2", 220 | "structuredValue": { 221 | "content": "typeExample2" 222 | }, 223 | "displayName": "typeExample2" 224 | } 225 | ], 226 | "additionalProperties": false, 227 | "rawType": { 228 | "name": "ResourceExampleType", 229 | "displayName": "ResourceExampleType", 230 | "typePropertyKind": "TYPE_EXPRESSION", 231 | "type": [ 232 | "object" 233 | ], 234 | "examples": [ 235 | { 236 | "value": "{\n \"content\": \"typeExample1\"\n}", 237 | "strict": true, 238 | "name": "typeExample1", 239 | "structuredValue": { 240 | "content": "typeExample1" 241 | } 242 | }, 243 | { 244 | "value": "{\n \"content\": \"typeExample2\"\n}", 245 | "strict": true, 246 | "name": "typeExample2", 247 | "structuredValue": { 248 | "content": "typeExample2" 249 | } 250 | } 251 | ], 252 | "properties": [ 253 | { 254 | "name": "content", 255 | "displayName": "content", 256 | "typePropertyKind": "TYPE_EXPRESSION", 257 | "type": [ 258 | "string" 259 | ], 260 | "required": true, 261 | "key": "content" 262 | } 263 | ], 264 | "additionalProperties": false 265 | } 266 | } 267 | } 268 | } -------------------------------------------------------------------------------- /test/parameters.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Lots of parameters", 3 | "resources": [ 4 | { 5 | "methods": [ 6 | { 7 | "responses": [ 8 | { 9 | "code": "200", 10 | "description": "Account was created and user is now logged in", 11 | "key": "200" 12 | } 13 | ], 14 | "body": [ 15 | { 16 | "name": "application/json", 17 | "displayName": "application/json", 18 | "typePropertyKind": "TYPE_EXPRESSION", 19 | "type": "any", 20 | "examples": [ 21 | { 22 | "value": "{\n \"email\": \"john@example.com\",\n \"password\": \"super_secret\",\n \"name\": \"John Doe\"\n}", 23 | "strict": true, 24 | "name": "example1", 25 | "structuredValue": { 26 | "email": "john@example.com", 27 | "password": "super_secret", 28 | "name": "John Doe" 29 | }, 30 | "displayName": "example1" 31 | } 32 | ], 33 | "key": "application/json" 34 | } 35 | ], 36 | "method": "post", 37 | "allUriParameters": [] 38 | } 39 | ], 40 | "relativeUri": "/account", 41 | "displayName": "/account", 42 | "resources": [ 43 | { 44 | "methods": [ 45 | { 46 | "queryParameters": [ 47 | { 48 | "name": "name", 49 | "displayName": "name", 50 | "typePropertyKind": "TYPE_EXPRESSION", 51 | "type": "string", 52 | "required": true, 53 | "description": "name on account", 54 | "examples": [ 55 | { 56 | "value": "Naruto Uzumaki", 57 | "strict": true, 58 | "name": null, 59 | "structuredValue": "Naruto Uzumaki" 60 | } 61 | ], 62 | "key": "name" 63 | }, 64 | { 65 | "name": "gender", 66 | "displayName": "gender", 67 | "typePropertyKind": "TYPE_EXPRESSION", 68 | "type": "string", 69 | "required": true, 70 | "enum": [ 71 | "male", 72 | "female" 73 | ], 74 | "key": "gender" 75 | }, 76 | { 77 | "name": "number", 78 | "displayName": "number", 79 | "typePropertyKind": "TYPE_EXPRESSION", 80 | "type": "integer", 81 | "default": 42, 82 | "required": true, 83 | "key": "number" 84 | } 85 | ], 86 | "method": "get", 87 | "allUriParameters": [] 88 | } 89 | ], 90 | "relativeUri": "/find", 91 | "displayName": "/find", 92 | "relativeUriPathSegments": [ 93 | "find" 94 | ], 95 | "absoluteUri": "/account/find", 96 | "parentUrl": "/account", 97 | "uniqueId": "account_find", 98 | "allUriParameters": [] 99 | }, 100 | { 101 | "methods": [ 102 | { 103 | "headers": [ 104 | { 105 | "name": "Authorization", 106 | "displayName": "Authorization", 107 | "typePropertyKind": "TYPE_EXPRESSION", 108 | "type": "string", 109 | "required": true, 110 | "description": "Basic authentication header", 111 | "examples": [ 112 | { 113 | "value": "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n", 114 | "strict": true, 115 | "name": null, 116 | "structuredValue": "Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n" 117 | } 118 | ], 119 | "key": "Authorization" 120 | } 121 | ], 122 | "method": "get", 123 | "allUriParameters": [ 124 | { 125 | "name": "id", 126 | "displayName": "id", 127 | "typePropertyKind": "TYPE_EXPRESSION", 128 | "type": "string", 129 | "required": true, 130 | "description": "account identifier", 131 | "minLength": 1, 132 | "maxLength": 10, 133 | "key": "id" 134 | } 135 | ] 136 | }, 137 | { 138 | "body": [ 139 | { 140 | "name": "application/x-www-form-urlencoded", 141 | "displayName": "application/x-www-form-urlencoded", 142 | "typePropertyKind": "TYPE_EXPRESSION", 143 | "type": "object", 144 | "properties": [ 145 | { 146 | "name": "name", 147 | "displayName": "name", 148 | "typePropertyKind": "TYPE_EXPRESSION", 149 | "type": "string", 150 | "examples": [ 151 | { 152 | "value": "Naruto Uzumaki", 153 | "strict": true, 154 | "name": "example1", 155 | "structuredValue": "Naruto Uzumaki", 156 | "displayName": "example1" 157 | }, 158 | { 159 | "value": "Kevin Renskers", 160 | "strict": true, 161 | "name": "example2", 162 | "structuredValue": "Kevin Renskers", 163 | "displayName": "example2" 164 | } 165 | ], 166 | "required": true, 167 | "description": "name on account", 168 | "key": "name" 169 | }, 170 | { 171 | "name": "gender", 172 | "displayName": "gender", 173 | "typePropertyKind": "TYPE_EXPRESSION", 174 | "type": "string", 175 | "required": true, 176 | "enum": [ 177 | "male", 178 | "female" 179 | ], 180 | "key": "gender" 181 | } 182 | ], 183 | "key": "application/x-www-form-urlencoded" 184 | } 185 | ], 186 | "method": "put", 187 | "allUriParameters": [ 188 | { 189 | "name": "id", 190 | "displayName": "id", 191 | "typePropertyKind": "TYPE_EXPRESSION", 192 | "type": "string", 193 | "required": true, 194 | "description": "account identifier", 195 | "minLength": 1, 196 | "maxLength": 10, 197 | "key": "id" 198 | } 199 | ] 200 | }, 201 | { 202 | "description": "Delete the account", 203 | "method": "delete", 204 | "allUriParameters": [ 205 | { 206 | "name": "id", 207 | "displayName": "id", 208 | "typePropertyKind": "TYPE_EXPRESSION", 209 | "type": "string", 210 | "required": true, 211 | "description": "account identifier", 212 | "minLength": 1, 213 | "maxLength": 10, 214 | "key": "id" 215 | } 216 | ] 217 | } 218 | ], 219 | "uriParameters": [ 220 | { 221 | "name": "id", 222 | "displayName": "id", 223 | "typePropertyKind": "TYPE_EXPRESSION", 224 | "type": "string", 225 | "required": true, 226 | "description": "account identifier", 227 | "minLength": 1, 228 | "maxLength": 10, 229 | "key": "id" 230 | } 231 | ], 232 | "relativeUri": "/{id}", 233 | "displayName": "/{id}", 234 | "resources": [ 235 | { 236 | "methods": [ 237 | { 238 | "responses": [ 239 | { 240 | "code": "401", 241 | "headers": [ 242 | { 243 | "name": "WWW-Authenticate", 244 | "displayName": "WWW-Authenticate", 245 | "typePropertyKind": "TYPE_EXPRESSION", 246 | "type": "string", 247 | "required": true, 248 | "description": "user was not authorized", 249 | "examples": [ 250 | { 251 | "value": "WWW-Authenticate: Basic realm=\"raml2html\"\n", 252 | "strict": true, 253 | "name": null, 254 | "structuredValue": "WWW-Authenticate: Basic realm=\"raml2html\"\n" 255 | } 256 | ], 257 | "key": "WWW-Authenticate" 258 | } 259 | ], 260 | "description": "Not authorized", 261 | "key": "401" 262 | } 263 | ], 264 | "method": "get", 265 | "allUriParameters": [ 266 | { 267 | "name": "id", 268 | "displayName": "id", 269 | "typePropertyKind": "TYPE_EXPRESSION", 270 | "type": "string", 271 | "required": true, 272 | "description": "account identifier", 273 | "minLength": 1, 274 | "maxLength": 10, 275 | "key": "id" 276 | }, 277 | { 278 | "name": "id", 279 | "displayName": "id", 280 | "typePropertyKind": "TYPE_EXPRESSION", 281 | "type": "string", 282 | "required": true, 283 | "description": "sub account identifier", 284 | "minLength": 1, 285 | "maxLength": 10, 286 | "key": "id" 287 | } 288 | ] 289 | } 290 | ], 291 | "uriParameters": [ 292 | { 293 | "name": "id", 294 | "displayName": "id", 295 | "typePropertyKind": "TYPE_EXPRESSION", 296 | "type": "string", 297 | "required": true, 298 | "description": "sub account identifier", 299 | "minLength": 1, 300 | "maxLength": 10, 301 | "key": "id" 302 | } 303 | ], 304 | "relativeUri": "/{id}", 305 | "displayName": "/{id}", 306 | "relativeUriPathSegments": [ 307 | "{id}" 308 | ], 309 | "absoluteUri": "/account/{id}/{id}", 310 | "parentUrl": "/account/{id}", 311 | "uniqueId": "account__id___id_", 312 | "allUriParameters": [ 313 | { 314 | "name": "id", 315 | "displayName": "id", 316 | "typePropertyKind": "TYPE_EXPRESSION", 317 | "type": "string", 318 | "required": true, 319 | "description": "account identifier", 320 | "minLength": 1, 321 | "maxLength": 10, 322 | "key": "id" 323 | }, 324 | { 325 | "name": "id", 326 | "displayName": "id", 327 | "typePropertyKind": "TYPE_EXPRESSION", 328 | "type": "string", 329 | "required": true, 330 | "description": "sub account identifier", 331 | "minLength": 1, 332 | "maxLength": 10, 333 | "key": "id" 334 | } 335 | ] 336 | } 337 | ], 338 | "relativeUriPathSegments": [ 339 | "{id}" 340 | ], 341 | "absoluteUri": "/account/{id}", 342 | "parentUrl": "/account", 343 | "uniqueId": "account__id_", 344 | "allUriParameters": [ 345 | { 346 | "name": "id", 347 | "displayName": "id", 348 | "typePropertyKind": "TYPE_EXPRESSION", 349 | "type": "string", 350 | "required": true, 351 | "description": "account identifier", 352 | "minLength": 1, 353 | "maxLength": 10, 354 | "key": "id" 355 | } 356 | ] 357 | } 358 | ], 359 | "relativeUriPathSegments": [ 360 | "account" 361 | ], 362 | "absoluteUri": "/account", 363 | "parentUrl": "", 364 | "uniqueId": "account", 365 | "allUriParameters": [] 366 | } 367 | ] 368 | } -------------------------------------------------------------------------------- /test/inheritance.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Inheritance Test", 3 | "resources": [ 4 | { 5 | "methods": [ 6 | { 7 | "body": [ 8 | { 9 | "name": "PasswordProtectedAccount", 10 | "displayName": "PasswordProtectedAccount", 11 | "typePropertyKind": "TYPE_EXPRESSION", 12 | "type": "object", 13 | "properties": [ 14 | { 15 | "type": "string", 16 | "name": "name", 17 | "displayName": "name", 18 | "typePropertyKind": "TYPE_EXPRESSION", 19 | "required": true, 20 | "key": "name" 21 | }, 22 | { 23 | "type": "string", 24 | "name": "email", 25 | "displayName": "email", 26 | "typePropertyKind": "TYPE_EXPRESSION", 27 | "pattern": "^.+@.+\\..+$", 28 | "rawType": { 29 | "name": "Email", 30 | "displayName": "Email", 31 | "typePropertyKind": "TYPE_EXPRESSION", 32 | "type": [ 33 | "string" 34 | ], 35 | "pattern": "^.+@.+\\..+$" 36 | }, 37 | "originalType": "Email", 38 | "required": true, 39 | "key": "email" 40 | }, 41 | { 42 | "type": "string", 43 | "name": "gender", 44 | "displayName": "gender", 45 | "typePropertyKind": "TYPE_EXPRESSION", 46 | "required": false, 47 | "enum": [ 48 | "male", 49 | "female" 50 | ], 51 | "key": "gender" 52 | }, 53 | { 54 | "type": "string", 55 | "name": "password", 56 | "displayName": "password", 57 | "typePropertyKind": "TYPE_EXPRESSION", 58 | "required": true, 59 | "key": "password" 60 | } 61 | ], 62 | "description": "An account which is password protected.", 63 | "additionalProperties": true, 64 | "rawType": { 65 | "name": "PasswordProtectedAccount", 66 | "displayName": "PasswordProtectedAccount", 67 | "typePropertyKind": "TYPE_EXPRESSION", 68 | "type": [ 69 | "Account" 70 | ], 71 | "description": "An account which is password protected.", 72 | "properties": [ 73 | { 74 | "name": "password", 75 | "displayName": "password", 76 | "typePropertyKind": "TYPE_EXPRESSION", 77 | "type": [ 78 | "string" 79 | ], 80 | "required": true, 81 | "key": "password" 82 | } 83 | ] 84 | }, 85 | "originalType": "Account", 86 | "key": "application/json" 87 | } 88 | ], 89 | "description": "Creates a new account\n", 90 | "method": "post", 91 | "allUriParameters": [] 92 | }, 93 | { 94 | "body": [ 95 | { 96 | "name": "BannableAccount", 97 | "displayName": "BannableAccount", 98 | "typePropertyKind": "TYPE_EXPRESSION", 99 | "type": "object", 100 | "properties": [ 101 | { 102 | "type": "string", 103 | "name": "name", 104 | "displayName": "name", 105 | "typePropertyKind": "TYPE_EXPRESSION", 106 | "required": true, 107 | "key": "name" 108 | }, 109 | { 110 | "type": "string", 111 | "name": "email", 112 | "displayName": "email", 113 | "typePropertyKind": "TYPE_EXPRESSION", 114 | "pattern": "^.+@.+\\..+$", 115 | "rawType": { 116 | "name": "Email", 117 | "displayName": "Email", 118 | "typePropertyKind": "TYPE_EXPRESSION", 119 | "type": [ 120 | "string" 121 | ], 122 | "pattern": "^.+@.+\\..+$" 123 | }, 124 | "originalType": "Email", 125 | "required": true, 126 | "key": "email" 127 | }, 128 | { 129 | "type": "string", 130 | "name": "gender", 131 | "displayName": "gender", 132 | "typePropertyKind": "TYPE_EXPRESSION", 133 | "required": false, 134 | "enum": [ 135 | "male", 136 | "female" 137 | ], 138 | "key": "gender" 139 | }, 140 | { 141 | "type": "string", 142 | "name": "password", 143 | "displayName": "password", 144 | "typePropertyKind": "TYPE_EXPRESSION", 145 | "required": true, 146 | "key": "password" 147 | }, 148 | { 149 | "type": "boolean", 150 | "name": "banned", 151 | "displayName": "banned", 152 | "typePropertyKind": "TYPE_EXPRESSION", 153 | "required": true, 154 | "key": "banned" 155 | } 156 | ], 157 | "description": "An account which is password protected.", 158 | "additionalProperties": true, 159 | "rawType": { 160 | "name": "BannableAccount", 161 | "displayName": "BannableAccount", 162 | "typePropertyKind": "TYPE_EXPRESSION", 163 | "type": [ 164 | "PasswordProtectedAccount" 165 | ], 166 | "properties": [ 167 | { 168 | "name": "banned", 169 | "displayName": "banned", 170 | "typePropertyKind": "TYPE_EXPRESSION", 171 | "type": [ 172 | "boolean" 173 | ], 174 | "required": true, 175 | "key": "banned" 176 | } 177 | ] 178 | }, 179 | "originalType": "PasswordProtectedAccount", 180 | "key": "application/json" 181 | } 182 | ], 183 | "method": "put", 184 | "allUriParameters": [] 185 | } 186 | ], 187 | "relativeUri": "/account", 188 | "displayName": "ACCOUNT", 189 | "relativeUriPathSegments": [ 190 | "account" 191 | ], 192 | "absoluteUri": "/account", 193 | "parentUrl": "", 194 | "uniqueId": "account", 195 | "allUriParameters": [] 196 | } 197 | ], 198 | "types": { 199 | "Email": { 200 | "type": "string", 201 | "name": "Email", 202 | "displayName": "Email", 203 | "typePropertyKind": "TYPE_EXPRESSION", 204 | "pattern": "^.+@.+\\..+$", 205 | "rawType": { 206 | "name": "Email", 207 | "displayName": "Email", 208 | "typePropertyKind": "TYPE_EXPRESSION", 209 | "type": [ 210 | "string" 211 | ], 212 | "pattern": "^.+@.+\\..+$" 213 | } 214 | }, 215 | "Account": { 216 | "type": "object", 217 | "properties": { 218 | "name": { 219 | "type": "string", 220 | "name": "name", 221 | "displayName": "name", 222 | "typePropertyKind": "TYPE_EXPRESSION", 223 | "required": true 224 | }, 225 | "email": { 226 | "type": "string", 227 | "name": "email", 228 | "displayName": "email", 229 | "typePropertyKind": "TYPE_EXPRESSION", 230 | "pattern": "^.+@.+\\..+$", 231 | "rawType": { 232 | "name": "Email", 233 | "displayName": "Email", 234 | "typePropertyKind": "TYPE_EXPRESSION", 235 | "type": [ 236 | "string" 237 | ], 238 | "pattern": "^.+@.+\\..+$" 239 | }, 240 | "originalType": "Email", 241 | "required": true 242 | }, 243 | "gender": { 244 | "type": "string", 245 | "name": "gender", 246 | "displayName": "gender", 247 | "typePropertyKind": "TYPE_EXPRESSION", 248 | "required": false, 249 | "enum": [ 250 | "male", 251 | "female" 252 | ] 253 | } 254 | }, 255 | "name": "Account", 256 | "displayName": "Account", 257 | "typePropertyKind": "TYPE_EXPRESSION", 258 | "description": "A basic account in the inheritance test", 259 | "additionalProperties": true, 260 | "rawType": { 261 | "name": "Account", 262 | "displayName": "Account", 263 | "typePropertyKind": "TYPE_EXPRESSION", 264 | "type": [ 265 | "object" 266 | ], 267 | "description": "A basic account in the inheritance test", 268 | "properties": { 269 | "name": { 270 | "name": "name", 271 | "displayName": "name", 272 | "typePropertyKind": "TYPE_EXPRESSION", 273 | "type": [ 274 | "string" 275 | ], 276 | "required": true 277 | }, 278 | "email": { 279 | "name": "email", 280 | "displayName": "email", 281 | "typePropertyKind": "TYPE_EXPRESSION", 282 | "type": [ 283 | "Email" 284 | ], 285 | "required": true 286 | }, 287 | "gender": { 288 | "name": "gender", 289 | "displayName": "gender", 290 | "typePropertyKind": "TYPE_EXPRESSION", 291 | "type": [ 292 | "string" 293 | ], 294 | "required": false, 295 | "enum": [ 296 | "male", 297 | "female" 298 | ] 299 | } 300 | } 301 | } 302 | }, 303 | "PasswordProtectedAccount": { 304 | "type": "object", 305 | "properties": { 306 | "name": { 307 | "type": "string", 308 | "name": "name", 309 | "displayName": "name", 310 | "typePropertyKind": "TYPE_EXPRESSION", 311 | "required": true, 312 | "key": "name" 313 | }, 314 | "email": { 315 | "type": "string", 316 | "name": "email", 317 | "displayName": "email", 318 | "typePropertyKind": "TYPE_EXPRESSION", 319 | "pattern": "^.+@.+\\..+$", 320 | "rawType": { 321 | "name": "Email", 322 | "displayName": "Email", 323 | "typePropertyKind": "TYPE_EXPRESSION", 324 | "type": [ 325 | "string" 326 | ], 327 | "pattern": "^.+@.+\\..+$" 328 | }, 329 | "originalType": "Email", 330 | "required": true, 331 | "key": "email" 332 | }, 333 | "gender": { 334 | "type": "string", 335 | "name": "gender", 336 | "displayName": "gender", 337 | "typePropertyKind": "TYPE_EXPRESSION", 338 | "required": false, 339 | "enum": [ 340 | "male", 341 | "female" 342 | ], 343 | "key": "gender" 344 | }, 345 | "password": { 346 | "type": "string", 347 | "name": "password", 348 | "displayName": "password", 349 | "typePropertyKind": "TYPE_EXPRESSION", 350 | "required": true, 351 | "key": "password" 352 | } 353 | }, 354 | "name": "PasswordProtectedAccount", 355 | "displayName": "PasswordProtectedAccount", 356 | "typePropertyKind": "TYPE_EXPRESSION", 357 | "description": "An account which is password protected.", 358 | "additionalProperties": true, 359 | "rawType": { 360 | "name": "PasswordProtectedAccount", 361 | "displayName": "PasswordProtectedAccount", 362 | "typePropertyKind": "TYPE_EXPRESSION", 363 | "type": [ 364 | "Account" 365 | ], 366 | "description": "An account which is password protected.", 367 | "properties": [ 368 | { 369 | "name": "password", 370 | "displayName": "password", 371 | "typePropertyKind": "TYPE_EXPRESSION", 372 | "type": [ 373 | "string" 374 | ], 375 | "required": true, 376 | "key": "password" 377 | } 378 | ] 379 | }, 380 | "originalType": "Account" 381 | }, 382 | "BannableAccount": { 383 | "type": "object", 384 | "properties": { 385 | "name": { 386 | "type": "string", 387 | "name": "name", 388 | "displayName": "name", 389 | "typePropertyKind": "TYPE_EXPRESSION", 390 | "required": true, 391 | "key": "name" 392 | }, 393 | "email": { 394 | "type": "string", 395 | "name": "email", 396 | "displayName": "email", 397 | "typePropertyKind": "TYPE_EXPRESSION", 398 | "pattern": "^.+@.+\\..+$", 399 | "rawType": { 400 | "name": "Email", 401 | "displayName": "Email", 402 | "typePropertyKind": "TYPE_EXPRESSION", 403 | "type": [ 404 | "string" 405 | ], 406 | "pattern": "^.+@.+\\..+$" 407 | }, 408 | "originalType": "Email", 409 | "required": true, 410 | "key": "email" 411 | }, 412 | "gender": { 413 | "type": "string", 414 | "name": "gender", 415 | "displayName": "gender", 416 | "typePropertyKind": "TYPE_EXPRESSION", 417 | "required": false, 418 | "enum": [ 419 | "male", 420 | "female" 421 | ], 422 | "key": "gender" 423 | }, 424 | "password": { 425 | "type": "string", 426 | "name": "password", 427 | "displayName": "password", 428 | "typePropertyKind": "TYPE_EXPRESSION", 429 | "required": true, 430 | "key": "password" 431 | }, 432 | "banned": { 433 | "type": "boolean", 434 | "name": "banned", 435 | "displayName": "banned", 436 | "typePropertyKind": "TYPE_EXPRESSION", 437 | "required": true, 438 | "key": "banned" 439 | } 440 | }, 441 | "name": "BannableAccount", 442 | "displayName": "BannableAccount", 443 | "typePropertyKind": "TYPE_EXPRESSION", 444 | "description": "An account which is password protected.", 445 | "additionalProperties": true, 446 | "rawType": { 447 | "name": "BannableAccount", 448 | "displayName": "BannableAccount", 449 | "typePropertyKind": "TYPE_EXPRESSION", 450 | "type": [ 451 | "PasswordProtectedAccount" 452 | ], 453 | "properties": [ 454 | { 455 | "name": "banned", 456 | "displayName": "banned", 457 | "typePropertyKind": "TYPE_EXPRESSION", 458 | "type": [ 459 | "boolean" 460 | ], 461 | "required": true, 462 | "key": "banned" 463 | } 464 | ] 465 | }, 466 | "originalType": "PasswordProtectedAccount" 467 | } 468 | } 469 | } --------------------------------------------------------------------------------