├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── LICENSE.txt ├── README.md ├── Rakefile ├── lib ├── raml_parser.rb └── raml_parser │ ├── model.rb │ ├── snippet_generator.rb │ ├── version.rb │ └── yaml_helper.rb ├── raml_parser.gemspec └── spec ├── examples ├── raml │ ├── baseuriparameters1.raml │ ├── baseuriparameters2.raml │ ├── documentation.raml │ ├── external │ │ ├── box.raml │ │ ├── bug.raml │ │ ├── github.raml │ │ ├── groups_and_nesting.raml │ │ ├── instagram.yml │ │ ├── json_schema.json │ │ ├── jukebox-api.raml │ │ ├── jukebox-include-album-new.sample │ │ ├── jukebox-include-album-retrieve.sample │ │ ├── jukebox-include-album-songs.sample │ │ ├── jukebox-include-album.schema │ │ ├── jukebox-include-albums.sample │ │ ├── jukebox-include-artist-albums.sample │ │ ├── jukebox-include-artist-new.sample │ │ ├── jukebox-include-artist-retrieve.sample │ │ ├── jukebox-include-artist.schema │ │ ├── jukebox-include-artists.sample │ │ ├── jukebox-include-song-new.sample │ │ ├── jukebox-include-song-retrieve.sample │ │ ├── jukebox-include-song.schema │ │ ├── jukebox-include-songs.sample │ │ ├── linkedin-v1-single.yml │ │ ├── mule_sales_enablement.raml │ │ ├── multiple-methods.raml │ │ ├── named_parameters.raml │ │ ├── requests-responses.raml │ │ ├── resource_summary_spacing.raml │ │ ├── simple.raml │ │ ├── stripe.raml │ │ ├── test.raml │ │ ├── twitter.raml │ │ ├── xml_example.xml │ │ └── xml_schema.xsd │ ├── formparameters.raml │ ├── headers.raml │ ├── issue2.raml │ ├── methods.raml │ ├── parameters.raml │ ├── parametersinflection.raml │ ├── protocols1.raml │ ├── protocols2.raml │ ├── protocols3.raml │ ├── queryparameters.raml │ ├── requestbodies.raml │ ├── required.raml │ ├── resources.raml │ ├── resourcetypes.raml │ ├── responses.raml │ ├── schemas.raml │ ├── securedby1.raml │ ├── securedby2.raml │ ├── securityschemes.raml │ ├── simple.raml │ ├── traits.raml │ └── uriparameters.raml ├── raml_bad │ └── uriparameters.raml └── yaml │ ├── include1.yml │ ├── include2.yml │ ├── simple.yml │ └── traversing.yml ├── lib ├── raml_parser │ ├── snippet_generator_spec.rb │ └── yaml_helper_spec.rb └── raml_parser_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /Gemfile.lock 4 | /_yardoc/ 5 | /coverage/ 6 | /doc/ 7 | /pkg/ 8 | /spec/reports/ 9 | /tmp/ 10 | *.bundle 11 | *.so 12 | *.o 13 | *.a 14 | mkmf.log 15 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 1.9.3 5 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | # Specify your gem's dependencies in my_ruby_gem.gemspec 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Christian Hoffmeister 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RamlParser 2 | 3 | [![build](https://img.shields.io/travis/ePages-de/raml_parser/develop.svg)](https://travis-ci.org/ePages-de/raml_parser) 4 | [![gem](https://img.shields.io/gem/v/raml_parser.svg)](https://rubygems.org/gems/raml_parser) 5 | [![license](https://img.shields.io/badge/license-MIT-lightgrey.svg)](http://opensource.org/licenses/MIT) 6 | 7 | A parser for the [RAML](http://raml.org/) API modeling language. 8 | 9 | ## Installation 10 | 11 | Add this line to your application's Gemfile: 12 | 13 | ```ruby 14 | source 'https://rubygems.org' 15 | 16 | gem 'raml_parser' 17 | ``` 18 | 19 | or for bleeding edge: 20 | 21 | ```ruby 22 | gem 'raml_parser', :git => 'https://github.com/ePages-de/raml_parser.git', :branch => 'develop' 23 | ``` 24 | 25 | And then execute: 26 | 27 | $ bundle 28 | 29 | ## Usage 30 | 31 | ```ruby 32 | require 'raml_parser' 33 | 34 | raml = RamlParser::Parser.parse_file('path/to/api.raml') 35 | 36 | # generate some markdown out of it 37 | for res in raml.resources 38 | puts '# Resource ' + res.absolute_uri + "\n\n" 39 | for name, meth in res.methods 40 | puts '## Method ' + meth.method + "\n\n" 41 | unless meth.description.nil? 42 | puts meth.description + "\n\n" 43 | else 44 | puts "(TODO)\n\n" 45 | end 46 | end 47 | end 48 | ``` 49 | 50 | ## What parts of RAML are not supported 51 | 52 | These are features of the RAML 0.8 specification that are not fully handled yet. This list should be complete, i.e. everything not listed here should work. 53 | 54 | * [Named parameters with multiple types](http://raml.org/spec.html#named-parameters-with-multiple-types) 55 | * [Optional properties in resource types](http://raml.org/spec.html#optional-properties) 56 | 57 | ## Contributing 58 | 59 | 1. Fork it ( https://github.com/ePages-de/raml_parser/fork ) 60 | 2. Create your feature branch (`git checkout -b my-new-feature`) 61 | 3. Commit your changes (`git commit -am 'Add some feature'`) 62 | 4. Push to the branch (`git push origin my-new-feature`) 63 | 5. Create a new Pull Request 64 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | 3 | multitask :default => [:test] 4 | task :spec => :test 5 | 6 | require "rspec/core/rake_task" 7 | RSpec::Core::RakeTask.new(:test) 8 | -------------------------------------------------------------------------------- /lib/raml_parser.rb: -------------------------------------------------------------------------------- 1 | require 'raml_parser/yaml_helper' 2 | require 'raml_parser/model' 3 | 4 | module RamlParser 5 | class Parser 6 | def self.parse_file(path) 7 | ensure_raml_0_8(path) 8 | node = YamlNode.new(nil, 'root', YamlHelper.read_yaml(path)) 9 | parse_root(node) 10 | end 11 | 12 | def self.parse_file_with_marks(path) 13 | ensure_raml_0_8(path) 14 | node = YamlNode.new(nil, 'root', YamlHelper.read_yaml(path)) 15 | node.mark_all(:unused) 16 | node.mark(:used) 17 | root = parse_root(node) 18 | { :root => root, :marks => node.marks } 19 | end 20 | 21 | private 22 | 23 | def self.ensure_raml_0_8(path) 24 | first_line = File.open(path) { |f| f.readline }.strip 25 | raise "File #{path} does not start with RAML 0.8 comment" unless first_line == '#%RAML 0.8' 26 | end 27 | 28 | def self.parse_root(node) 29 | root = Model::Root.new 30 | root.title = node.hash('title').or_default('').value 31 | root.version = node.hash('version').value 32 | root.base_uri = node.hash('baseUri').or_default('').value.gsub('{version}', root.version || '') 33 | root.media_type = node.hash('mediaType').value 34 | root.secured_by = node.hash('securedBy').or_default([]).array_values { |n| n.value } 35 | root.documentation = node.hash('documentation').array_values { |n| parse_documenation(n) } 36 | root.schemas = node.hash('schemas').arrayhash_values { |n| n.value } 37 | root.security_schemes = node.hash('securitySchemes').arrayhash_values { |n| parse_security_scheme(n) } 38 | root.resource_types = node.hash('resourceTypes').mark_all(:used).arrayhash_values { |n| n } 39 | root.traits = node.hash('traits').mark_all(:used).arrayhash_values { |n| n } 40 | 41 | implicit_protocols = (root.base_uri.scan(/^(http|https):\/\//).first || []).map { |p| p.upcase } 42 | explicit_protocols = node.hash('protocols').array_values { |n| n.value } 43 | root.protocols = explicit_protocols.empty? ? implicit_protocols : explicit_protocols 44 | 45 | implicit_base_uri_parameters = extract_uri_parameters(root.base_uri, true) 46 | explicit_base_uri_parameters = node.hash('baseUriParameters').hash_values { |n| parse_named_parameter(n, true) } 47 | root.base_uri_parameters = implicit_base_uri_parameters.merge(explicit_base_uri_parameters) 48 | 49 | root.resources = traverse_resources(node, nil) do |n,parent| 50 | parent_absolute_uri = parent != nil ? parent.absolute_uri : root.base_uri || '' 51 | parent_relative_uri = parent != nil ? parent.relative_uri : '' 52 | parent_uri_parameters = parent != nil ? parent.uri_parameters.clone : {} 53 | parse_resource(n, root, parent_absolute_uri, parent_relative_uri, parent_uri_parameters, false) 54 | end 55 | 56 | root 57 | end 58 | 59 | def self.parse_resource(node, root, parent_absolute_uri, parent_relative_uri, parent_uri_parameters, as_partial) 60 | node = node.or_default({}) 61 | resource = Model::Resource.new(parent_absolute_uri + node.key, parent_relative_uri + node.key) 62 | resource.display_name = node.hash('displayName').value 63 | resource.description = node.hash('description').value 64 | resource.type = parse_type(node.hash('type')) 65 | resource.is = parse_is(node.hash('is')) 66 | resource.secured_by = (root.secured_by + node.hash('securedBy').or_default([]).array_values { |n| n.value }).uniq 67 | resource.methods = Hash[find_method_nodes(node).map { |n| [n.key, parse_method(n, root, resource, as_partial)] }] 68 | 69 | root_base_uri_parameters = root.base_uri_parameters 70 | own_base_uri_parameters = node.hash('baseUriParameters').hash_values { |n| parse_named_parameter(n, true) } 71 | resource.base_uri_parameters = root_base_uri_parameters.merge(own_base_uri_parameters) 72 | 73 | implicit_uri_parameters = extract_uri_parameters(node.key, true) 74 | explicit_uri_parameters = node.hash('uriParameters').hash_values { |n| parse_named_parameter(n, true) } 75 | raise 'Can only explicitly specify URI parameters from the current relative URI' unless as_partial or (explicit_uri_parameters.keys - implicit_uri_parameters.keys).empty? 76 | resource.uri_parameters = parent_uri_parameters.merge(implicit_uri_parameters).merge(explicit_uri_parameters) 77 | 78 | unless as_partial 79 | resource = mixin_resource_types(node, root, resource) 80 | resource.display_name = resource.relative_uri unless resource.display_name 81 | end 82 | 83 | resource 84 | end 85 | 86 | def self.parse_method(node, root, resource, as_partial) 87 | node = node.or_default({}) 88 | method = Model::Method.new(node.key.upcase) 89 | method.description = node.hash('description').value 90 | method.query_parameters = node.hash('queryParameters').hash_values { |n| parse_named_parameter(n, false) } 91 | method.bodies = node.hash('body').hash_values { |n| parse_body(n, root) } 92 | method.responses = node.hash('responses').hash_values { |n| parse_response(n, root) } 93 | method.headers = node.hash('headers').hash_values { |n| parse_named_parameter(n, false) } 94 | method.secured_by = (resource.secured_by + node.hash('securedBy').or_default([]).array_values { |n| n.value }).uniq if resource 95 | method.is = parse_is(node.hash('is')) 96 | 97 | root_protocols = as_partial ? [] : root.protocols 98 | explicit_protocols = node.hash('protocols').array_values { |n| n.value } 99 | method.protocols = explicit_protocols.empty? ? root_protocols : explicit_protocols 100 | 101 | unless as_partial 102 | method = mixin_traits(node, root, method, resource) 103 | end 104 | 105 | method 106 | end 107 | 108 | def self.parse_response(node, root) 109 | node = node.or_default({}) 110 | response = Model::Response.new(node.key) 111 | response.display_name = node.hash('displayName').value 112 | response.description = node.hash('description').value 113 | response.bodies = node.hash('body').hash_values { |n| parse_body(n, root) } 114 | response.headers = node.hash('headers').hash_values { |n| parse_named_parameter(n, false) } 115 | response 116 | end 117 | 118 | def self.parse_named_parameter(node, required_per_default) 119 | if node.value.is_a? Array 120 | node.mark_all(:unsupported) 121 | # TODO: Not yet supported named parameters with multiple types 122 | return Model::NamedParameter.new(node.key) 123 | end 124 | 125 | node = node.or_default({}) 126 | named_parameter = Model::NamedParameter.new(node.key) 127 | named_parameter.type = node.hash('type').or_default('string').value 128 | named_parameter.display_name = node.hash('displayName').or_default(named_parameter.name).value 129 | named_parameter.description = node.hash('description').value 130 | named_parameter.required = node.hash('required').or_default(required_per_default).value 131 | named_parameter.default = node.hash('default').value 132 | named_parameter.example = node.hash('example').value 133 | named_parameter.min_length = node.hash('minLength').value 134 | named_parameter.max_length = node.hash('maxLength').value 135 | named_parameter.minimum = node.hash('minimum').value 136 | named_parameter.maximum = node.hash('maximum').value 137 | named_parameter.repeat = node.hash('repeat').value 138 | named_parameter.enum = node.hash('enum').or_default([]).array_values { |n| n.value } 139 | named_parameter.pattern = node.hash('pattern').value 140 | named_parameter 141 | end 142 | 143 | def self.parse_body(node, root) 144 | node = node.or_default({}) 145 | body = Model::Body.new(node.key) 146 | body.example = node.hash('example').value 147 | body.schema = node.hash('schema').value 148 | body.schema = root.schemas[body.schema] if root.schemas.has_key? body.schema 149 | body.form_parameters = node.hash('formParameters').hash_values { |n| parse_named_parameter(n, false) } 150 | # TODO: Form parameters are only allowed for media type application/x-www-form-urlencoded or multipart/form-data 151 | body 152 | end 153 | 154 | def self.parse_security_scheme(node) 155 | node = node.or_default({}) 156 | security_scheme = Model::SecurityScheme.new(node.key) 157 | security_scheme.type = node.hash('type').value 158 | security_scheme.description = node.hash('description').value 159 | security_scheme.described_by = parse_method(node.hash('describedBy'), nil, nil, true) 160 | security_scheme.settings = node.hash('settings').mark_all(:used).value 161 | security_scheme 162 | end 163 | 164 | def self.parse_documenation(node) 165 | node = node.or_default({}) 166 | documentation = Model::Documentation.new 167 | documentation.title = node.hash('title').value 168 | documentation.content = node.hash('content').value 169 | documentation 170 | end 171 | 172 | def self.parse_type(node) 173 | node = node.or_default({}).mark_all(:used) 174 | result = {} 175 | if node.value.is_a? String 176 | result = { node.value => nil } 177 | elsif node.value.is_a? Hash 178 | result = node.value 179 | else 180 | raise "Invalid syntax for 'type' property at #{node.path}" 181 | end 182 | result 183 | end 184 | 185 | def self.parse_is(node) 186 | node = node.or_default({}).mark_all(:used) 187 | result = {} 188 | node.value.each { |n| 189 | if n.is_a? String 190 | result = result.merge({ n => nil }) 191 | elsif n.is_a? Hash 192 | result = result.merge(n) 193 | else 194 | raise "Invalid syntax for 'is' property at #{node.path}" 195 | end 196 | } 197 | result 198 | end 199 | 200 | def self.mixin_resource_types(node, root, resource) 201 | result = Model::Resource.new(nil, nil) 202 | resource.type.each do |name,value| 203 | params = (value || {}).merge({ 204 | 'resourcePath' => resource.relative_uri, 205 | 'resourcePathName' => resource.relative_uri.match(/[^\/]*$/).to_s 206 | }) 207 | resource_type = root.resource_types.has_key?(name) ? parse_resource(resolve_parametrization(root.resource_types[name], params), root, '', '', {}, true) : nil 208 | if resource_type != nil 209 | result = Model::Resource.merge(result, resource_type) 210 | else 211 | raise "Referencing unknown resource type #{name} at #{node.path}" 212 | end 213 | end 214 | 215 | Model::Resource.merge(result, resource) 216 | end 217 | 218 | def self.mixin_traits(node, root, method, resource) 219 | result = Model::Method.new(nil) 220 | (resource.is.merge(method.is)).each do |name,value| 221 | params = (value || {}).merge({ 222 | 'resourcePath' => resource.relative_uri, 223 | 'resourcePathName' => resource.relative_uri.match(/[^\/]*$/).to_s, 224 | 'methodName' => method.method.downcase 225 | }) 226 | trait = root.traits.has_key?(name) ? parse_method(resolve_parametrization(root.traits[name], params), root, nil, true) : nil 227 | if trait != nil 228 | result = Model::Method.merge(result, trait) 229 | else 230 | raise "Referencing unknown trait #{name} at #{node.path}" 231 | end 232 | end 233 | 234 | Model::Method.merge(result, method) 235 | end 236 | 237 | def self.resolve_parametrization(node, params) 238 | require 'active_support/core_ext/string/inflections' 239 | 240 | def self.alter_string(str, params, node) 241 | str.gsub(/<<([a-zA-Z]+)(\s*\|\s*!([a-zA-Z_\-]+))?>>/) do |a,b| 242 | case $3 243 | when nil 244 | params[$1].to_s 245 | when 'singularize' 246 | params[$1].to_s.singularize 247 | when 'pluralize' 248 | params[$1].to_s.pluralize 249 | else 250 | raise "Using unknown parametrization function #{$3} at #{node.path}" 251 | end 252 | end 253 | end 254 | 255 | def self.traverse(raw, params, node) 256 | if raw.is_a? Hash 257 | Hash[raw.map { |k,v| [traverse(k, params, node), traverse(v, params, node)] }] 258 | elsif raw.is_a? Array 259 | raw.map { |i| traverse(i, params, node) } 260 | elsif raw.is_a? String 261 | alter_string(raw, params, node) 262 | else 263 | raw 264 | end 265 | end 266 | 267 | YamlNode.new(node.parent, node.key, traverse(node.value, params, node)) 268 | end 269 | 270 | def self.find_resource_nodes(node) 271 | def self.is_resource(key) 272 | key =~ /^\// 273 | end 274 | (node.value || {}).select { |k,_| is_resource(k) }.map { |k,_| node.hash(k) } 275 | end 276 | 277 | def self.find_method_nodes(node) 278 | def self.is_method(key) 279 | %w(get post put delete head patch options trace connect).include? key 280 | end 281 | (node.value || {}).select { |k,_| is_method(k) }.map { |k,_| node.hash(k) } 282 | end 283 | 284 | def self.extract_uri_parameters(uri, required_per_default) 285 | names = uri.scan(/\{([a-zA-Z\_\-]+)\}/).map { |m| m.first } 286 | Hash[names.map { |name| [name, Model::NamedParameter.new(name, 'string', name, nil, required_per_default)] }] 287 | end 288 | 289 | def self.traverse_resources(node, parent_resource, &code) 290 | find_resource_nodes(node).map { |n| 291 | resource = code.call(n, parent_resource) 292 | [resource] + traverse_resources(n, resource, &code) 293 | }.flatten 294 | end 295 | end 296 | end 297 | -------------------------------------------------------------------------------- /lib/raml_parser/model.rb: -------------------------------------------------------------------------------- 1 | module RamlParser 2 | module Model 3 | class Root 4 | attr_accessor :title, :base_uri, :version, :media_type, :schemas, :security_schemes, :base_uri_parameters, :resource_types, :traits, :protocols, :secured_by, :documentation, :resources 5 | 6 | def initialize(title = nil, base_uri = nil, version = nil, media_type = nil, schemas = {}, security_schemes = {}, base_uri_parameters = {}, resource_types = {}, traits = {}, protocols = [], secured_by = [], documentation = [], resources = []) 7 | @title = title 8 | @base_uri = base_uri 9 | @version = version 10 | @media_type = media_type 11 | @schemas = schemas 12 | @security_schemes = security_schemes 13 | @base_uri_parameters = base_uri_parameters 14 | @resource_types = resource_types 15 | @traits = traits 16 | @protocols = protocols 17 | @secured_by = secured_by 18 | @documentation = documentation 19 | @resources = resources 20 | end 21 | end 22 | 23 | class Resource 24 | attr_accessor :absolute_uri, :relative_uri, :display_name, :description, :base_uri_parameters, :uri_parameters, :methods, :type, :is, :secured_by 25 | 26 | def initialize(absolute_uri, relative_uri, display_name = nil, description = nil, base_uri_parameters = {}, uri_parameters = {}, methods = {}, type = {}, is = {}, secured_by = []) 27 | @absolute_uri = absolute_uri 28 | @relative_uri = relative_uri 29 | @display_name = display_name 30 | @description = description 31 | @base_uri_parameters = base_uri_parameters 32 | @uri_parameters = uri_parameters 33 | @methods = methods 34 | @type = type 35 | @is = is 36 | @secured_by = secured_by 37 | end 38 | 39 | def self.merge(a, b) 40 | resource = Resource.new(b.absolute_uri, b.relative_uri) 41 | 42 | resource.display_name = if b.display_name then b.display_name else a.display_name end 43 | resource.description = if b.description then b.description else a.description end 44 | resource.base_uri_parameters = a.base_uri_parameters.merge(b.base_uri_parameters) 45 | resource.uri_parameters = a.uri_parameters.merge(b.uri_parameters) 46 | resource.methods = a.methods.merge(b.methods) 47 | resource.type = a.type.merge(b.type) 48 | resource.is = a.is.merge(b.is) 49 | resource.secured_by = (a.secured_by + b.secured_by).uniq 50 | 51 | resource 52 | end 53 | end 54 | 55 | class Method 56 | attr_accessor :method, :description, :query_parameters, :responses, :bodies, :headers, :is, :protocols, :secured_by 57 | 58 | def initialize(method, description = nil, query_parameters = {}, responses = {}, bodies = {}, headers = {}, is = {}, protocols = [], secured_by = []) 59 | @method = method 60 | @description = description 61 | @query_parameters = query_parameters 62 | @responses = responses 63 | @bodies = bodies 64 | @headers = headers 65 | @is = is 66 | @protocols = protocols 67 | @secured_by = secured_by 68 | end 69 | 70 | def self.merge(a, b) 71 | method = Method.new(b.method) 72 | 73 | method.description = if b.description then b.description else a.description end 74 | method.query_parameters = a.query_parameters.merge(b.query_parameters) 75 | method.responses = a.responses.merge(b.responses) 76 | method.bodies = a.bodies.merge(b.bodies) 77 | method.headers = a.headers.merge(b.headers) 78 | method.is = a.is.merge(b.is) 79 | method.protocols = (a.protocols + b.protocols).uniq 80 | method.secured_by = (a.secured_by + b.secured_by).uniq 81 | 82 | method 83 | end 84 | end 85 | 86 | class Response 87 | attr_accessor :status_code, :display_name, :description, :bodies, :headers 88 | 89 | def initialize(status_code, display_name = nil, description = nil, bodies = {}, headers = {}) 90 | @status_code = status_code 91 | @display_name = display_name 92 | @description = description 93 | @bodies = bodies 94 | @headers = headers 95 | end 96 | end 97 | 98 | class Body 99 | attr_accessor :media_type, :example, :schema, :form_parameters 100 | 101 | def initialize(media_type, example = nil, schema = nil, form_parameters = {}) 102 | @media_type = media_type 103 | @example = example 104 | @schema = schema 105 | @form_parameters = form_parameters 106 | end 107 | end 108 | 109 | class NamedParameter 110 | attr_accessor :name, :type, :display_name, :description, :required, :default, :example, :min_length, :max_length, :minimum, :maximum, :repeat, :enum, :pattern 111 | 112 | def initialize(name, type = nil, display_name = nil, description = nil, required = false, default = nil, example = nil, min_length = nil, max_length = nil, minimum = nil, maximum = nil, repeat = nil, enum = nil, pattern = nil) 113 | @name = name 114 | @type = type 115 | @display_name = display_name 116 | @description = description 117 | @required = required 118 | @default = default 119 | @example = example 120 | @min_length = min_length 121 | @max_length = max_length 122 | @minimum = minimum 123 | @maximum = maximum 124 | @repeat = repeat 125 | @enum = enum 126 | @pattern = pattern 127 | end 128 | end 129 | 130 | class Documentation 131 | attr_accessor :title, :content 132 | 133 | def initialize(title = nil, content = nil) 134 | @title = title 135 | @content = content 136 | end 137 | end 138 | 139 | class SecurityScheme 140 | attr_accessor :name, :type, :description, :described_by, :settings 141 | 142 | def initialize(name, type = nil, description = nil, described_by = nil, settings = {}) 143 | @name = name 144 | @type = type 145 | @description = description 146 | @described_by = described_by 147 | @settings = settings 148 | end 149 | end 150 | end 151 | end 152 | -------------------------------------------------------------------------------- /lib/raml_parser/snippet_generator.rb: -------------------------------------------------------------------------------- 1 | module RamlParser 2 | class SnippetGenerator 3 | attr_accessor :raml 4 | 5 | def initialize(raml) 6 | @raml = raml 7 | end 8 | 9 | def curl(resource, method_name) 10 | method = resource.methods[method_name] 11 | 12 | curl_method = "-X#{method.method.upcase}" 13 | curl_content_type = map_nonempty(method.bodies.values.first) { |b| "-H \"Accept: #{b.media_type}\"" } 14 | query_parameters = map_nonempty(render_query_parameters(method.query_parameters.values)) { |s| '?' + s } 15 | curl_data = map_nonempty(method.bodies.values.first) { |b| "-d \"#{b.example}\"" } 16 | 17 | ['curl', curl_method, curl_content_type, resource.absolute_uri + query_parameters, curl_data].select { |p| not is_falsy(p) }.join(' ') 18 | end 19 | 20 | def javascript_vanilla(resource, method_name) 21 | method = resource.methods[method_name] 22 | 23 | query_parameters = map_nonempty(render_query_parameters(method.query_parameters.values)) { |s| '?' + s } 24 | js_content_type = map_nonempty(method.bodies.values.first) { |b| "xhr.setRequestHeader('Accept', '#{b.media_type}');\n" } || '' 25 | data = map_nonempty(method.bodies.values.first) { |b| (b.example || '').chomp } 26 | 27 | result = "var xhr = new XMLHttpRequest();\n" 28 | result += "xhr.open('#{method.method.upcase}', '#{resource.absolute_uri + query_parameters}', true);\n" 29 | result += "xhr.onreadystatechange = function () {\n" 30 | result += " if (xhr.readyState != 4 || xhr.status != 200) return;\n" 31 | result += " console.log('Success', xhr.responseText);\n" 32 | result += "};\n" 33 | result += js_content_type 34 | result += "xhr.send(\"#{data}\");" 35 | 36 | result 37 | end 38 | 39 | def ruby(resource, method_name) 40 | method = resource.methods[method_name] 41 | send_method = method.method.downcase == 'post' || method.method.downcase == 'put' 42 | query_parameters = map_nonempty(render_query_parameters(method.query_parameters.values)) { |s| '?' + s } || '' 43 | uri = "#{resource.absolute_uri}#{query_parameters}" 44 | 45 | data = map_nonempty(method.bodies.values.first) { |b| b.example.chomp } 46 | headers = { 'Accept' => map_nonempty(method.bodies.values.first) { |b| b.media_type } } 47 | headers = headers.delete_if {|key, value| value.nil? } 48 | 49 | result = "require 'net/http'\n" 50 | result += "require 'json'\n" if send_method 51 | result += "uri = URI.parse('#{uri}')\n" 52 | result += "headers = #{headers}\n" unless headers.empty? 53 | result += "data = #{data}\n\n" if send_method 54 | result += "req = Net::HTTP::#{method.method.capitalize}.new(uri.path" 55 | result += headers.empty? ? ")\n" : ", initheader = headers)\n" 56 | result += "req.body = data.to_json\n" if send_method 57 | result += "response = Net::HTTP.start(uri.hostname, uri.port) { |http| http.request(req) }\n" 58 | 59 | result 60 | end 61 | 62 | private 63 | def is_falsy(value) 64 | if value.is_a?(String) 65 | value.length == 0 66 | else 67 | value.nil? 68 | end 69 | end 70 | 71 | def map_nonempty(value, &code) 72 | if not is_falsy(value) 73 | code.call(value) 74 | else 75 | value 76 | end 77 | end 78 | 79 | def render_query_parameters(query_parameters) 80 | query_parameters 81 | .select { |q| q.required } 82 | .map { |q| "#{q.name}=#{q.example || 'value'}"} 83 | .join('&') 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /lib/raml_parser/version.rb: -------------------------------------------------------------------------------- 1 | module RamlParser 2 | VERSION = "0.2.5" 3 | end 4 | -------------------------------------------------------------------------------- /lib/raml_parser/yaml_helper.rb: -------------------------------------------------------------------------------- 1 | module RamlParser 2 | class YamlNode 3 | attr_reader :parent, :key, :value, :marks 4 | 5 | def initialize(parent, key, value) 6 | @parent = parent 7 | @key = key 8 | @value = value 9 | @marks = {} 10 | end 11 | 12 | def root 13 | if @parent != nil 14 | @parent.root 15 | else 16 | self 17 | end 18 | end 19 | 20 | def path 21 | if @parent != nil 22 | "#{@parent.path}.#{@key}" 23 | else 24 | @key 25 | end 26 | end 27 | 28 | def mark(what, p = path) 29 | if parent.nil? 30 | @marks[p] = what 31 | else 32 | @parent.mark(what, p) 33 | end 34 | self 35 | end 36 | 37 | def mark_all(what) 38 | mark(what) 39 | if @value.is_a? Hash 40 | hash_values { |n| n.mark_all(what) } 41 | elsif @value.is_a? Array 42 | array_values { |n| n.mark_all(what) } 43 | end 44 | self 45 | end 46 | 47 | def or_default(default) 48 | @value != nil ? self : YamlNode.new(@parent, @key, default) 49 | end 50 | 51 | def array(index) 52 | new_node = YamlNode.new(self, "[#{index}]", @value[index]) 53 | new_node.mark(:used) 54 | new_node 55 | end 56 | 57 | def array_values(&code) 58 | (@value || []).each_with_index.map { |_,i| code.call(array(i)) } 59 | end 60 | 61 | def hash(key) 62 | new_node = YamlNode.new(self, key, @value[key]) 63 | new_node.mark(:used) 64 | new_node 65 | end 66 | 67 | def hash_values(&code) 68 | Hash[(@value || {}).map { |k,v| [k, code.call(hash(k))] }] 69 | end 70 | 71 | def arrayhash(index) 72 | new_node = array(index) 73 | new_node.mark(:used) 74 | new_node2 = new_node.hash(new_node.value.first[0]) 75 | new_node2.mark(:used) 76 | new_node2 77 | end 78 | 79 | def arrayhash_values(&code) 80 | Hash[(@value || []).each_with_index.map { |_,i| 81 | node = arrayhash(i) 82 | [node.key, code.call(node)] 83 | }] 84 | end 85 | end 86 | 87 | class YamlHelper 88 | require 'yaml' 89 | 90 | def self.read_yaml(path) 91 | # add support for !include tags 92 | Psych.add_domain_type 'include', 'include' do |_, value| 93 | case value 94 | when /^https?:\/\// 95 | # TODO implement remote loading of included files 96 | '' 97 | else 98 | case value 99 | when /\.raml$/ 100 | read_yaml(value) 101 | when /\.ya?ml$/ 102 | read_yaml(value) 103 | else 104 | File.read(value) 105 | end 106 | end 107 | end 108 | 109 | # change working directory so that !include works properly 110 | pwd_old = Dir.pwd 111 | Dir.chdir(File.dirname(path)) 112 | raw = File.read(File.basename(path)) 113 | node = YAML.load(raw) 114 | Dir.chdir(pwd_old) 115 | node 116 | end 117 | 118 | def self.dump_yaml(yaml) 119 | YAML.dump(yaml) 120 | end 121 | end 122 | end 123 | -------------------------------------------------------------------------------- /raml_parser.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path('../lib', __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require 'raml_parser/version' 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "raml_parser" 8 | spec.version = RamlParser::VERSION 9 | spec.authors = ["Christian Hoffmeister"] 10 | spec.email = ["mail@choffmeister.de"] 11 | spec.summary = "A parser for the RAML API modeling language." 12 | spec.description = "" 13 | spec.homepage = "https://github.com/ePages-de/raml_parser" 14 | spec.license = "MIT" 15 | 16 | spec.files = `git ls-files -z`.split("\x0") 17 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } 18 | spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) 19 | spec.require_paths = ["lib"] 20 | 21 | spec.add_development_dependency "bundler", "~> 1.7" 22 | spec.add_development_dependency "rake", "~> 10.0" 23 | spec.add_development_dependency "rspec", "~> 3.2.0" 24 | 25 | spec.add_dependency "activesupport", ">= 4.0.0" 26 | end 27 | -------------------------------------------------------------------------------- /spec/examples/raml/baseuriparameters1.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000/{version} 5 | version: v4 6 | /a: 7 | -------------------------------------------------------------------------------- /spec/examples/raml/baseuriparameters2.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://{user}.domain.com/{version}/{language} 5 | version: v1 6 | baseUriParameters: 7 | user: 8 | description: The user 9 | /a: 10 | /b/{next}: 11 | uriParameters: 12 | next: 13 | /c: 14 | baseUriParameters: 15 | user: 16 | description: Changed 17 | -------------------------------------------------------------------------------- /spec/examples/raml/documentation.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | documentation: 6 | - title: Home 7 | content: | 8 | Content Home 9 | - title: FAQ 10 | content: | 11 | Content FAQ 12 | -------------------------------------------------------------------------------- /spec/examples/raml/external/bug.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | title: myapi 3 | baseUri: http://localhost:3000 4 | /resource: 5 | post: 6 | body: 7 | multipart/form-data: 8 | formParameters: 9 | bar: 10 | type: string 11 | foo: 12 | type: file 13 | -------------------------------------------------------------------------------- /spec/examples/raml/external/groups_and_nesting.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | title: "Groups and Nesting" 3 | resourceTypes: 4 | - someType: {} 5 | /non-resource-prefix/resource: 6 | get: 7 | /nested: 8 | get: 9 | /non-resource-prefix/another-resource: 10 | get: 11 | /resource-with-nesting: 12 | type: someType 13 | get: 14 | /nested: 15 | get: 16 | /another-nested: 17 | get: 18 | /resource-without-methods-with-nesting: 19 | /nested: 20 | get: 21 | /another-nested: 22 | get: 23 | -------------------------------------------------------------------------------- /spec/examples/raml/external/json_schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "title": "Example Schema", 3 | "type": "object", 4 | "properties": { 5 | "firstName": { 6 | "type": "string" 7 | }, 8 | "lastName": { 9 | "type": "string" 10 | }, 11 | "age": { 12 | "description": "Age in years", 13 | "type": "integer", 14 | "minimum": 0 15 | } 16 | }, 17 | "required": ["firstName", "lastName"] 18 | } 19 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-api.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Jukebox API 4 | baseUri: http://jukebox.api.com 5 | version: v1 6 | 7 | schemas: 8 | - song: !include jukebox-include-song.schema 9 | - artist: !include jukebox-include-artist.schema 10 | - album: !include jukebox-include-album.schema 11 | 12 | 13 | resourceTypes: 14 | - readOnlyCollection: 15 | description: Collection of available <> in Jukebox. 16 | get: 17 | description: Get a list of <>. 18 | responses: 19 | 200: 20 | body: 21 | application/json: 22 | example: | 23 | <> 24 | - collection: 25 | description: Collection of available <> in Jukebox. 26 | get: 27 | description: Get a list of <>. 28 | responses: 29 | 200: 30 | body: 31 | application/json: 32 | example: | 33 | <> 34 | post: 35 | description: | 36 | Add a new <> to Jukebox. 37 | queryParameters: 38 | access_token: 39 | description: "The access token provided by the authentication application" 40 | example: AABBCCDD 41 | required: true 42 | type: string 43 | body: 44 | application/json: 45 | schema: <> 46 | example: | 47 | <> 48 | responses: 49 | 200: 50 | body: 51 | application/json: 52 | example: | 53 | { "message": "The <> has been properly entered" } 54 | - collection-item: 55 | description: Entity representing a <> 56 | get: 57 | description: | 58 | Get the <> 59 | with <>Id = 60 | {<>Id} 61 | responses: 62 | 200: 63 | body: 64 | application/json: 65 | example: | 66 | <> 67 | 404: 68 | body: 69 | application/json: 70 | example: | 71 | {"message": "<> not found" } 72 | traits: 73 | - searchable: 74 | queryParameters: 75 | query: 76 | description: | 77 | JSON array [{"field1","value1","operator1"},{"field2","value2","operator2"},...,{"fieldN","valueN","operatorN"}] <> 78 | example: | 79 | <> 80 | - orderable: 81 | queryParameters: 82 | orderBy: 83 | description: | 84 | Order by field: <> 85 | type: string 86 | required: false 87 | order: 88 | description: Order 89 | enum: [desc, asc] 90 | default: desc 91 | required: false 92 | - pageable: 93 | queryParameters: 94 | offset: 95 | description: Skip over a number of elements by specifying an offset value for the query 96 | type: integer 97 | required: false 98 | example: 20 99 | default: 0 100 | limit: 101 | description: Limit the number of elements on the response 102 | type: integer 103 | required: false 104 | example: 80 105 | default: 10 106 | 107 | /songs: 108 | type: 109 | collection: 110 | exampleCollection: !include jukebox-include-songs.sample 111 | exampleItem: !include jukebox-include-song-new.sample 112 | get: 113 | is: [ 114 | searchable: {description: "with valid searchable fields: songTitle", example: "[\"songTitle\", \"Get L\", \"like\"]"}, 115 | orderable: {fieldsList: "songTitle"}, 116 | pageable 117 | ] 118 | /{songId}: 119 | type: 120 | collection-item: 121 | exampleItem: !include jukebox-include-song-retrieve.sample 122 | /file-content: 123 | description: The file to be reproduced by the client 124 | get: 125 | description: Get the file content 126 | responses: 127 | 200: 128 | body: 129 | binary/octet-stream: 130 | post: 131 | description: | 132 | Enters the file content for an existing song entity. 133 | 134 | The song needs to be created for the `/songs/{songId}/file-content` to exist. 135 | You can use this second resource to get and post the file to reproduce. 136 | 137 | Use the "binary/octet-stream" content type to specify the content from any consumer (excepting web-browsers). 138 | Use the "multipart-form/data" content type to upload a file which content will become the file-content 139 | body: 140 | binary/octet-stream: 141 | multipart/form-data: 142 | formParameters: 143 | file: 144 | description: The file to be uploaded 145 | required: true 146 | type: file 147 | /artists: 148 | type: 149 | collection: 150 | exampleCollection: !include jukebox-include-artists.sample 151 | exampleItem: !include jukebox-include-artist-new.sample 152 | get: 153 | is: [ 154 | searchable: {description: "with valid searchable fields: countryCode", example: "[\"countryCode\", \"FRA\", \"equals\"]"}, 155 | orderable: {fieldsList: "artistName, nationality"}, 156 | pageable 157 | ] 158 | /{artistId}: 159 | type: 160 | collection-item: 161 | exampleItem: !include jukebox-include-artist-retrieve.sample 162 | /albums: 163 | type: 164 | readOnlyCollection: 165 | exampleCollection: !include jukebox-include-artist-albums.sample 166 | description: Collection of albulms belonging to the artist 167 | get: 168 | description: Get a specific artist's albums list 169 | is: [orderable: {fieldsList: "albumName"}, pageable] 170 | /albums: 171 | type: 172 | collection: 173 | exampleCollection: !include jukebox-include-albums.sample 174 | exampleItem: !include jukebox-include-album-new.sample 175 | get: 176 | is: [ 177 | searchable: {description: "with valid searchable fields: genreCode", example: "[\"genreCode\", \"ELE\", \"equals\"]"}, 178 | orderable: {fieldsList: "albumName, genre"}, 179 | pageable 180 | ] 181 | /{albumId}: 182 | type: 183 | collection-item: 184 | exampleItem: !include jukebox-include-album-retrieve.sample 185 | /songs: 186 | type: 187 | readOnlyCollection: 188 | exampleCollection: !include jukebox-include-album-songs.sample 189 | get: 190 | is: [orderable: {fieldsList: "songTitle"}] 191 | description: Get the list of songs for the album with `albumId = {albumId}` 192 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-album-new.sample: -------------------------------------------------------------------------------- 1 | { 2 | "albumId": "183100e3-0e2b-4404-a716-66104d440550", 3 | "albumName": "Random Access Memories", 4 | "year": "2013", 5 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg", 6 | "genreCode": "ELE", 7 | "artistId": "110e8300-e32b-41d4-a716-664400445500" 8 | } 9 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-album-retrieve.sample: -------------------------------------------------------------------------------- 1 | { 2 | "albumId": "183100e3-0e2b-4404-a716-66104d440550", 3 | "albumName": "Random Access Memories", 4 | "year": "2013", 5 | "genre": "Electric Funk", 6 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg", 7 | "genre": { 8 | "countryCode": "ELE", 9 | "countryName": "Electronict" 10 | }, 11 | "songs": [ 12 | { 13 | "songId": "550e8400-e29b-41d4-a716-446655440000", 14 | "songTitle": "Get Lucky" 15 | }, 16 | { 17 | "songId": "550e8400-e29b-41d4-a716-446655440111", 18 | "songTitle": "Loose yourself to dance" 19 | }, 20 | { 21 | "songId": "550e8400-e29b-41d4-a716-446655440222", 22 | "songTitle": "Gio sorgio by Moroder" 23 | } 24 | ], 25 | "artist": { 26 | "artistId": "110e8300-e32b-41d4-a716-664400445500", 27 | "artistName": "Daft Punk", 28 | "imageURL": "http://travelhymns.com/wp-content/uploads/2013/06/random-access-memories1.jpg" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-album-songs.sample: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "songId": "550e8400-e29b-41d4-a716-446655440000", 4 | "songTitle": "Get Lucky" 5 | }, 6 | { 7 | "songId": "550e8400-e29b-41d4-a716-446655440111", 8 | "songTitle": "Loose yourself to dance" 9 | }, 10 | { 11 | "songId": "550e8400-e29b-41d4-a716-446655440222", 12 | "songTitle": "Gio sorgio by Moroder" 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-album.schema: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-03/schema", 4 | "id": "http://jsonschema.net", 5 | "required":false, 6 | "properties": { 7 | "albumId": { 8 | "type": "string", 9 | "required":true, 10 | "minLength": 36, 11 | "maxLength": 36 12 | }, 13 | "albumName": { 14 | "type": "string", 15 | "required": true 16 | }, 17 | "year": { 18 | "type": "string", 19 | "required": false 20 | }, 21 | "iamgeURL": { 22 | "type": "string", 23 | "required": false 24 | }, 25 | "genreCode": { 26 | "type": "string", 27 | "required": true 28 | }, 29 | "artistId": { 30 | "type": "string", 31 | "required":true, 32 | "minLength": 36, 33 | "maxLength": 36 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-albums.sample: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "albumId": "183100e3-0e2b-4404-a716-66104d440550", 4 | "albumName": "Random Access Memories", 5 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg", 6 | "artistId": "110e8300-e32b-41d4-a716-664400445500", 7 | "genre": { 8 | "countryCode": "ELE", 9 | "countryName": "Electronic" 10 | } 11 | }, 12 | { 13 | "albumId": "183100e3-0e2b-4404-3123-66111d4de520", 14 | "albumName": "OK Computer", 15 | "imageURL": "http://www.greenplastic.com/dev/wp-content/uploads/2010/12/ok-computer.jpg", 16 | "artistId": "11032be3-41d4-4455-a716-664400a71600", 17 | "genre": { 18 | "countryCode": "ALT", 19 | "countryName": "Alternative Rock" 20 | } 21 | }, 22 | { 23 | "albumId": "183100e3-cccc-4404-1111-63204d64coda", 24 | "albumName": "The Dark Side of the Moon", 25 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/3/3b/Dark_Side_of_the_Moon.png", 26 | "artistId": "110e8300-e32b-41d4-a716-229932554400", 27 | "genre": { 28 | "countryCode": "PRO", 29 | "countryName": "Progressive Rock" 30 | } 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-artist-albums.sample: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "albumId": "183100e3-0e2b-4404-a716-66104d440550", 4 | "albumName": "Random Access Memories", 5 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg" 6 | }, 7 | { 8 | "albumId": "183100e3-0e2b-4404-a716-66104d440551", 9 | "albumName": "TRON: Legacy R3CONF1GUR3D", 10 | "imageURL": "http://ecx.images-amazon.com/images/I/51Tvo-iArBL.jpg" 11 | }, 12 | { 13 | "albumId": "183100e3-0e2b-4404-a716-66104d440552", 14 | "albumName": "TRON: Legacy", 15 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/3/39/Tron_Legacy_Soundtrack.jpg" 16 | }, 17 | { 18 | "albumId": "183100e3-0e2b-4404-a716-66104d440553", 19 | "albumName": "Alive", 20 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/4/49/Daft_Punk_Alive_2007.JPG" 21 | }, 22 | { 23 | "albumId": "183100e3-0e2b-4404-a716-66104d440554", 24 | "albumName": "Musique Vol. 1", 25 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/ab/Musique_Vol._1_1993%E2%80%932005.png" 26 | }, 27 | { 28 | "albumId": "183100e3-0e2b-4404-a716-66104d440555", 29 | "albumName": "Human After All", 30 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/0/0d/Humanafterall.jpg" 31 | }, 32 | { 33 | "albumId": "183100e3-0e2b-4404-a716-66104d440556", 34 | "albumName": "Daft Club", 35 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/f/fc/Daftclub.jpg" 36 | }, 37 | { 38 | "albumId": "183100e3-0e2b-4404-a716-66104d440557", 39 | "albumName": "Discovery", 40 | "imageURL": "http://ecx.images-amazon.com/images/I/71bsHTr6idL._SL1500_.jpg" 41 | } 42 | ] 43 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-artist-new.sample: -------------------------------------------------------------------------------- 1 | { 2 | "artistName": "Daft Punk", 3 | "description": "French electronic music duo consisting of musicians Guy-Manuel de Homem-Christo and Thomas Bangalter", 4 | "imageURL": "http://travelhymns.com/wp-content/uploads/2013/06/random-access-memories1.jpg" 5 | } 6 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-artist-retrieve.sample: -------------------------------------------------------------------------------- 1 | { 2 | "artistId": "110e8300-e32b-41d4-a716-664400445500", 3 | "artistName": "Daft Punk", 4 | "description": "French electronic music duo consisting of musicians Guy-Manuel de Homem-Christo and Thomas Bangalter", 5 | "imageURL": "http://travelhymns.com/wp-content/uploads/2013/06/random-access-memories1.jpg", 6 | "nationality": { 7 | "countryCode": "FRA", 8 | "countryName": "France" 9 | }, 10 | "albums": [ 11 | { 12 | "albumId": "183100e3-0e2b-4404-a716-66104d440550", 13 | "albumName": "Random Access Memories", 14 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg" 15 | }, 16 | { 17 | "albumId": "183100e3-0e2b-4404-a716-66104d440551", 18 | "albumName": "TRON: Legacy R3CONF1GUR3D", 19 | "imageURL": "http://ecx.images-amazon.com/images/I/51Tvo-iArBL.jpg" 20 | }, 21 | { 22 | "albumId": "183100e3-0e2b-4404-a716-66104d440552", 23 | "albumName": "TRON: Legacy", 24 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/3/39/Tron_Legacy_Soundtrack.jpg" 25 | }, 26 | { 27 | "albumId": "183100e3-0e2b-4404-a716-66104d440553", 28 | "albumName": "Alive", 29 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/4/49/Daft_Punk_Alive_2007.JPG" 30 | }, 31 | { 32 | "albumId": "183100e3-0e2b-4404-a716-66104d440554", 33 | "albumName": "Musique Vol. 1", 34 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/ab/Musique_Vol._1_1993%E2%80%932005.png" 35 | }, 36 | { 37 | "albumId": "183100e3-0e2b-4404-a716-66104d440555", 38 | "albumName": "Human After All", 39 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/0/0d/Humanafterall.jpg" 40 | }, 41 | { 42 | "albumId": "183100e3-0e2b-4404-a716-66104d440556", 43 | "albumName": "Daft Club", 44 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/f/fc/Daftclub.jpg" 45 | }, 46 | { 47 | "albumId": "183100e3-0e2b-4404-a716-66104d440557", 48 | "albumName": "Discovery", 49 | "imageURL": "http://ecx.images-amazon.com/images/I/71bsHTr6idL._SL1500_.jpg" 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-artist.schema: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-03/schema", 4 | "id": "http://jsonschema.net", 5 | "required":false, 6 | "properties": { 7 | "artistName": { 8 | "type": "string", 9 | "required":true 10 | }, 11 | "description": { 12 | "type": "string", 13 | "required": false 14 | }, 15 | "imageURL": { 16 | "type": "string", 17 | "required": false 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-artists.sample: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "artistId": "110e8300-e32b-41d4-a716-664400445500", 4 | "artistName": "Daft Punk", 5 | "description": "French electronic music duo consisting of musicians Guy-Manuel de Homem-Christo and Thomas Bangalter", 6 | "imageURL": "http://travelhymns.com/wp-content/uploads/2013/06/random-access-memories1.jpg", 7 | "nationality": { 8 | "countryCode": "FRA", 9 | "countryName": "France" 10 | } 11 | }, 12 | { 13 | "artistId": "110e8300-e32b-41d4-a716-229932554400", 14 | "artistName": "Pink Floyd", 15 | "description": "English rock band that achieved international acclaim with their progressive and psychedelic music.", 16 | "imageURL": "http://www.billboard.com/files/styles/promo_650/public/stylus/1251869-pink-floyd-reunions-617-409.jpg", 17 | "nationality": { 18 | "countryCode": "ENG", 19 | "countryName": "England" 20 | } 21 | }, 22 | { 23 | "artistId": "11032be3-41d4-4455-a716-664400a71600", 24 | "artistName": "Radiohead", 25 | "description": " English rock band from Abingdon, Oxfordshire, formed in 1985", 26 | "imageURL": "http://www.wired.com/images_blogs/photos/uncategorized/2007/10/01/radiohead.jpg", 27 | "nationality": { 28 | "countryCode": "ENG", 29 | "countryName": "England" 30 | } 31 | } 32 | ] 33 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-song-new.sample: -------------------------------------------------------------------------------- 1 | { 2 | "songId": "550e8400-e29b-41d4-a716-446655440000", 3 | "songTitle": "Get Lucky", 4 | "albumId": "183100e3-0e2b-4404-a716-66104d440550" 5 | } 6 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-song-retrieve.sample: -------------------------------------------------------------------------------- 1 | { 2 | "songId": "550e8400-e29b-41d4-a716-446655440000", 3 | "songTitle": "Get Lucky", 4 | "duration": "6:07", 5 | "artist": { 6 | "artistId": "110e8300-e32b-41d4-a716-664400445500" 7 | "artistName": "Daft Punk", 8 | "imageURL": "http://travelhymns.com/wp-content/uploads/2013/06/random-access-memories1.jpg" 9 | }, 10 | "album": { 11 | "albumId": "183100e3-0e2b-4404-a716-66104d440550", 12 | "albumName": "Random Access Memories", 13 | "imageURL": "http://upload.wikimedia.org/wikipedia/en/a/a7/Random_Access_Memories.jpg" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-song.schema: -------------------------------------------------------------------------------- 1 | { 2 | "type": "object", 3 | "$schema": "http://json-schema.org/draft-03/schema", 4 | "id": "http://jsonschema.net", 5 | "required": true, 6 | "properties": { 7 | "songId": { 8 | "type": "string", 9 | "required": true, 10 | "minLength": 36, 11 | "maxLength": 36 12 | }, 13 | "songTitle": { 14 | "type": "string", 15 | "required": true 16 | }, 17 | "albumId": { 18 | "type": "string", 19 | "required": true, 20 | "minLength": 36, 21 | "maxLength": 36 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spec/examples/raml/external/jukebox-include-songs.sample: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "songId": "550e8400-e29b-41d4-a716-446655440000", 4 | "songTitle": "Get Lucky" 5 | }, 6 | { 7 | "songId": "550e8400-e29b-41d4-a716-446655440111", 8 | "songTitle": "Loose yourself to dance" 9 | }, 10 | { 11 | "songId": "550e8400-e29b-41d4-a716-446655440222", 12 | "songTitle": "Gio sorgio by Morodera" 13 | } 14 | ] 15 | -------------------------------------------------------------------------------- /spec/examples/raml/external/mule_sales_enablement.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: "Muse: Mule Sales Enablement API" 4 | version: v1 5 | baseUri: http://vast-coast-7974.herokuapp.com/muse 6 | schemas: 7 | - presentation: | 8 | { "$schema": "http://json-schema.org/draft-03/schema", 9 | "type": "object", 10 | "description": "A product presentation", 11 | "properties": { 12 | "id": { "type": "string" }, 13 | "title": { "type": "string" }, 14 | "description": { "type": "string" }, 15 | "fileUrl": { "type": "string" }, 16 | "productId": { "type": "string" } 17 | }, 18 | "required": [ "id", "title", "fileUrl", "productId" ] 19 | } 20 | - product: | 21 | { "$schema": "http://json-schema.org/draft-03/schema", 22 | "type": "object", 23 | "description": "A Product", 24 | "properties": { 25 | "id": { "type": "string" }, 26 | "name": { "type": "string" }, 27 | "description": { "type": "string" }, 28 | "imageUrl": { "type": "string" }, 29 | "region": { "type": "string" } 30 | }, 31 | "required": [ "id", "name", "region" ] 32 | } 33 | resourceTypes: 34 | - base: 35 | get?: 36 | responses: &standardResponses 37 | 200: 38 | description: OK 39 | put?: 40 | responses: *standardResponses 41 | patch?: 42 | responses: *standardResponses 43 | post?: 44 | responses: 45 | 201: 46 | description: Created 47 | delete?: 48 | responses: *standardResponses 49 | - collection: 50 | type: base 51 | get: 52 | is: [ paged ] 53 | post: 54 | - typedCollection: 55 | type: collection 56 | get: 57 | responses: 58 | 200: 59 | body: 60 | application/json: 61 | schema: <> 62 | post: 63 | body: 64 | application/json: 65 | schema: <> 66 | responses: 67 | 201: 68 | body: 69 | application/json: 70 | schema: <> 71 | - member: 72 | type: base 73 | get: 74 | put: 75 | patch: 76 | delete: 77 | - typedMember: 78 | type: member 79 | get: 80 | responses: 81 | 200: 82 | body: 83 | application/json: 84 | schema: <> 85 | put: 86 | body: 87 | application/json: 88 | schema: <> 89 | responses: 90 | 200: 91 | body: 92 | application/json: 93 | schema: <> 94 | patch: 95 | body: 96 | application/json: 97 | schema: <> 98 | responses: 99 | 200: 100 | body: 101 | application/json: 102 | schema: <> 103 | delete: 104 | traits: 105 | - paged: 106 | displayName: paged 107 | queryParameters: 108 | start: 109 | displayName: start 110 | description: The first page to return 111 | type: number 112 | pages: 113 | displayName: pages 114 | description: The number of pages to return 115 | type: number 116 | - secured: 117 | displayName: secured 118 | headers: 119 | Authorization: 120 | description: The auth token for this request 121 | responses: 122 | 401: 123 | description: Unauthorized 124 | /presentations: &presentations 125 | type: { typedCollection: { schema: presentation } } 126 | is: [ secured ] 127 | get: 128 | queryParameters: 129 | title: 130 | type: string 131 | displayName: title 132 | description: Filter by title 133 | /{presentationId}: 134 | type: { typedMember: { schema: presentation } } 135 | is: [ secured ] 136 | /products: 137 | type: { typedCollection: { schema: product } } 138 | is: [ secured ] 139 | get: 140 | queryParameters: 141 | region: 142 | type: string 143 | displayName: region 144 | description: Filter by region 145 | /{productId}: 146 | type: { typedMember: { schema: product } } 147 | is: [ secured ] 148 | /presentations: *presentations 149 | -------------------------------------------------------------------------------- /spec/examples/raml/external/multiple-methods.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: #{test_api_uri} 5 | /resource: 6 | get: !!null 7 | post: !!null 8 | /another-resource: 9 | get: !!null 10 | put: !!null 11 | delete: !!null 12 | -------------------------------------------------------------------------------- /spec/examples/raml/external/named_parameters.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://www.example.com/ 5 | /resource: 6 | get: 7 | queryParameters: 8 | displayNameAndTypeDefaults: 9 | displayNameProvided: 10 | displayName: 'Display Name Provided' 11 | withType: 12 | type: integer 13 | withPattern: 14 | pattern: /some regex/ 15 | minLength: 16 | minLength: 8 17 | maxLength: 18 | maxLength: 20 19 | minAndMaxLength: 20 | minLength: 8 21 | maxLength: 20 22 | minimum: 23 | type: number 24 | minimum: 8 25 | maximum: 26 | type: number 27 | maximum: 20 28 | minAndMaximum: 29 | type: number 30 | minimum: 8 31 | maximum: 20 32 | repeat: 33 | repeat: true 34 | simpleShortDescription: 35 | description: | 36 | This parameter is used to do something. 37 | simpleLongDescription: 38 | description: | 39 | Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do 40 | eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad 41 | minim veniam, quis nostrud exercitation ullamco laboris nisi ut 42 | aliquip ex ea commodo consequat. Duis aute irure dolor in 43 | reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla 44 | pariatur. Excepteur sint occaecat cupidatat non proident, sunt in 45 | culpa qui officia deserunt mollit anim id est laborum. 46 | markdownDescription: 47 | description: | 48 | An explanation of the purpose of this parameter, in **Markdown**! 49 | 50 | # We can have headers 51 | 52 | And text with *all* sorts of formatting. 53 | 54 | * Such 55 | * as 56 | * lists 57 | example: 58 | example: 1a79a4d60de6718e8e5b326e338ae533 59 | default: 60 | default: 0 61 | descriptionAndExample: 62 | description: This parameter is used to do something. 63 | example: 154 64 | required: 65 | required: true 66 | enum: 67 | enum: 68 | - one 69 | - two 70 | - three 71 | combo: 72 | required: true 73 | enum: 74 | - any 75 | - all 76 | - exact match 77 | description: This parameter does something 78 | anotherCombo: 79 | required: true 80 | type: integer 81 | minimum: 5 82 | maximum: 20 83 | default: 5 84 | example: 18 85 | description: This explains the function of this parameter. 86 | -------------------------------------------------------------------------------- /spec/examples/raml/external/requests-responses.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://example.com 5 | schemas: 6 | - an_xml_schema: !include xml_schema.xsd 7 | - a_json_schema: !include json_schema.json 8 | /resource: 9 | post: 10 | headers: 11 | X-Some-Header: 12 | description: This is an example header 13 | body: 14 | text/xml: 15 | schema: an_xml_schema 16 | example: !include xml_example.xml 17 | application/json: 18 | schema: a_json_schema 19 | example: | 20 | { "figure": true, 21 | "out": true, 22 | "your": true, 23 | "life": true 24 | } 25 | responses: 26 | 200: 27 | description: | 28 | *Success* description 29 | headers: 30 | X-Some-Header: 31 | type: integer 32 | example: 5 33 | description: A *description* of some header sent as part of the **response**. 34 | 404: 35 | description: | 36 | *Error* description 37 | body: 38 | text/xml: 39 | example: | 40 | Error 41 | schema: an_xml_schema 42 | 43 | /sub: 44 | description: hi 45 | get: 46 | description: | 47 | ### WHAT 48 | -------------------------------------------------------------------------------- /spec/examples/raml/external/resource_summary_spacing.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | # Use this RAML with a very narrow console to see how the resource summary wraps 3 | # given the length/presence/absence of various elements 4 | title: Example 5 | resourceTypes: 6 | - someType: {} 7 | traits: 8 | - someTrait: {} 9 | /resource/with/a/very/long/name/that/causes/it/to/wrap/which/looks/bad/because: 10 | /short: 11 | /short: 12 | /short: 13 | /the/line/height/is/small: 14 | get: 15 | /some/more/with/ascenders: 16 | type: someType 17 | /and/padaqalag/with/methods: 18 | type: someType 19 | is: [someTrait] 20 | get: 21 | post: 22 | put: 23 | delete: 24 | /and/padaqalaga/with/description: 25 | type: someType 26 | is: [someTrait] 27 | description: | 28 | Some text I wrote 29 | 30 | And some more text 31 | 32 | Plus suppose I wrote a bunch of long text that wraps and makes it look like a real paragraph 33 | get: 34 | post: 35 | put: 36 | delete: 37 | /short: 38 | type: someType 39 | /short-with-methods: 40 | type: someType 41 | get: 42 | post: 43 | -------------------------------------------------------------------------------- /spec/examples/raml/external/simple.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://example.com 5 | securitySchemes: 6 | - basic: 7 | type: Basic Authentication 8 | traits: 9 | - secured: 10 | description: Some requests require authentication 11 | - unsecured: 12 | description: This is not secured 13 | - catpictures: 14 | description: requires cat headers 15 | - anotherTrait: {} 16 | - andAnotherTrait: {} 17 | - andYetAnotherTrait: {} 18 | - aFinalTrait: {} 19 | - someParameterizedTrait: 20 | description: <> 21 | resourceTypes: 22 | - longCollectionName: 23 | description: | 24 | This is a description. It can be long. 25 | 26 | # Headers 27 | 28 | It can have headers, and _italic_, and **bold** text. 29 | 30 | # Length 31 | 32 | Because it is arbitrary markdown, it can be arbitrarily long. 33 | documentation: 34 | - title: Getting Started 35 | content: | 36 | # Header 37 | Content 38 | ## Subheader 39 | **Bolded content** 40 | /resource: 41 | displayName: First One 42 | is: [secured] 43 | options: 44 | responses: 45 | 200: 46 | connect: 47 | responses: 48 | 200: 49 | trace: 50 | responses: 51 | 200: 52 | patch: 53 | responses: 54 | 200: 55 | delete: 56 | responses: 57 | 200: 58 | 201: 59 | 203: 60 | put: 61 | responses: 62 | 200: 63 | 201: 64 | 203: 65 | get: 66 | description: get the first one 67 | headers: 68 | x-custom: 69 | responses: 70 | 200: 71 | /{resourceId}: 72 | description: This is a resource description *with* some _markdown_ embedded in it 73 | uriParameters: 74 | resourceId: 75 | required: true 76 | description: Which resoure would you like to view 77 | get: 78 | description: | 79 | Instagram’s API uses the [OAuth 2.0 protocol](http://tools.ietf.org/html/draft-ietf-oauth-v2-12) for simple, but effective authentication and authorization. OAuth 2.0 is much easier to use than previous schemes; developers can start using the Instagram API almost immediately. The one thing to keep in mind is that all requests to the API must be made over SSL (https:// not http://) 80 | 81 | ## Do you need to authenticate? 82 | 83 | For the most part, Instagram’s API only requires the use of a _client_id). A client_id simply associates your server, script, or program with a specific application. However, some requests require authentication - specifically requests made on behalf of a user. Authenticated requests require an _access_token_. These tokens are unique to a user and should be stored securely. Access tokens may expire at any time in the future. 84 | 85 | Note that in many situations, you may not need to authenticate users at all. For instance, you may request popular photos without authenticating (i.e. you do not need to provide an access_token; just use your client ID with your request). We only require authentication in cases where your application is making requests on behalf of a user (commenting, liking, browsing a user’s feed, etc.). 86 | 87 | ## Receiving an access_token 88 | queryParameters: 89 | filter: 90 | description: What to filter 91 | type: string 92 | responses: 93 | 200: 94 | post: 95 | body: 96 | application/json: 97 | application/x-www-form-urlencoded: 98 | formParameters: 99 | name: 100 | description: The name of the resource to create 101 | type: string 102 | example: Comment 103 | description: 104 | description: A description of the resource to create 105 | type: string 106 | example: User-generated content pertinent to the associated blog post 107 | multipart/form-data: 108 | formParameters: 109 | name: 110 | description: The name of the resource to create 111 | type: string 112 | example: Comment 113 | description: 114 | description: A description of the resource to create 115 | type: string 116 | example: User-generated content pertinent to the associated blog post 117 | responses: 118 | 200: 119 | 201: 120 | 203: 121 | 122 | /another/resource: 123 | displayName: Cats 124 | type: longCollectionName 125 | is: [secured, catpictures, anotherTrait, andAnotherTrait] 126 | connect: !!null 127 | head: 128 | responses: 129 | 200: 130 | 201: 131 | 203: 132 | get: 133 | queryParameters: 134 | chunk: 135 | displayName: page 136 | description: Which page to display 137 | type: integer 138 | example: 1 139 | minimum: 1 140 | maximum: 100 141 | required: true 142 | order: 143 | description: The sort order of resources 144 | type: string 145 | enum: ["oldest", "newest"] 146 | example: oldest 147 | minLength: 5 148 | maxLength: 7 149 | default: newest 150 | query: 151 | description: A query parameter 152 | repeat: true 153 | 154 | /resource-with-headers: 155 | displayName: Resource With headers 156 | get: 157 | headers: 158 | x-custom-header: 159 | displayName: Custom header 160 | description: This header is used to send data that... 161 | type: string 162 | pattern: ^\w+$ 163 | x-p-{*}: 164 | displayName: Parameterized header 165 | 166 | /secured-resource: 167 | displayName: SO SECURE 168 | get: 169 | securedBy: [basic] 170 | /resource-with-method-level-traits: 171 | displayName: First One 172 | is: [secured] 173 | get: 174 | is: [unsecured, catpictures, anotherTrait, andAnotherTrait, andYetAnotherTrait, aFinalTrait, {someParameterizedTrait: { someParameterName: someParameterValue }}] 175 | description: get the first one 176 | /resource-with-form-and-multipart-form-parameters: 177 | get: 178 | queryParameters: 179 | some_query_param: 180 | displayName: Some Query Param 181 | description: Your value for some thing. 182 | type: string 183 | required: true 184 | example: "my value" 185 | body: 186 | application/json: 187 | example: | 188 | { 189 | "api_key": "c4f820f0420a013ea143230c290fbf99", 190 | ... 191 | } 192 | application/x-www-form-urlencoded: 193 | formParameters: 194 | api_key: 195 | displayName: API Key 196 | description: Your license key for the application. Please contact developer@nzpost.co.nz for a license key 197 | type: string 198 | required: true 199 | example: "c4f820f0420a013ea143230c290fbf99" 200 | multipart/form-data: 201 | formParameters: 202 | api_key: 203 | displayName: API Key 204 | description: Your license key for the application. Please contact developer@nzpost.co.nz for a license key 205 | type: string 206 | required: true 207 | example: "c4f820f0420a013ea143230c290fbf99" 208 | /resource-with-repeatable-params: 209 | post: 210 | queryParameters: 211 | someParam: 212 | repeat: true 213 | notRepeatable: 214 | body: 215 | application/x-www-form-urlencoded: 216 | formParameters: 217 | someFormParam: 218 | repeat: true 219 | multipart/form-data: 220 | formParameters: 221 | someMultipartFormParam: 222 | type: file 223 | repeat: true 224 | someMultipartFormParamWithMultipleTypes: 225 | - type: file 226 | - type: string 227 | repeat: true 228 | headers: 229 | someHeader: 230 | repeat: true 231 | x-meta-{*}: 232 | repeat: true 233 | 234 | -------------------------------------------------------------------------------- /spec/examples/raml/external/test.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example 4 | baseUri: http://localhost:3000 5 | /whatever: 6 | get: 7 | description: <> 8 | responses: 9 | 200: 10 | body: 11 | text/xml: 12 | -------------------------------------------------------------------------------- /spec/examples/raml/external/xml_example.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1511685 4 | 1304030488000 5 | 6 | 229433 7 | Cloudera 8 | 9 | 10 | Technical Writer 11 | 12 | San Francisco Bay Area 13 | 14 | us 15 | 16 | 17 | 18 | San Francisco or Palo Alto, CA 19 | 20 | hQ4ruu3J2q 21 | Paul 22 | Battaglia 23 | Technical Writer at Cloudera 24 | 25 | 26 | -------------------------------------------------------------------------------- /spec/examples/raml/external/xml_schema.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /spec/examples/raml/formparameters.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | version: v123 6 | /a: 7 | post: 8 | body: 9 | application/x-www-form-urlencoded: 10 | formParameters: 11 | from: 12 | description: FROM1 13 | to: 14 | description: TO1 15 | /b: 16 | post: 17 | body: 18 | multipart/form-data: 19 | formParameters: 20 | from: 21 | description: FROM2 22 | to: 23 | description: TO2 24 | -------------------------------------------------------------------------------- /spec/examples/raml/headers.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /a: 6 | get: 7 | headers: 8 | X-Foobar-Ping: 9 | description: Ping 10 | responses: 11 | 200: 12 | headers: 13 | X-Foobar-Pong: 14 | displayName: PingPong 15 | description: Pong 16 | -------------------------------------------------------------------------------- /spec/examples/raml/issue2.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: GitHub API 4 | baseUri: https://api.github.com 5 | version: v3 6 | traits: 7 | - base: 8 | responses: 9 | 404: 10 | description: Not found 11 | body: 12 | application/json: 13 | example: | 14 | { 15 | "status_code": 404, 16 | "message": "Not found" 17 | } 18 | 500: 19 | description: Internal server error 20 | body: 21 | application/json: 22 | example: | 23 | { 24 | "status_code": 500, 25 | "message": "Internal server error" 26 | } 27 | resourceTypes: 28 | - collection: 29 | post: 30 | responses: 31 | 404: 32 | description: Not found 33 | body: 34 | application/json: 35 | example: | 36 | { 37 | "status_code": 404, 38 | "message": "Not found" 39 | } 40 | 500: 41 | description: Internal server error 42 | body: 43 | application/json: 44 | example: | 45 | { 46 | "status_code": 500, 47 | "message": "Internal server error" 48 | } 49 | /users/{username}: 50 | type: collection 51 | get: 52 | -------------------------------------------------------------------------------- /spec/examples/raml/methods.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /a: 6 | get: 7 | /b: 8 | get: 9 | description: This is /a/b 10 | -------------------------------------------------------------------------------- /spec/examples/raml/parameters.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | traits: 6 | - first: 7 | description: <> and <> and <> and <> 8 | queryParameters: 9 | <>: 10 | description: Applepie 11 | resourceTypes: 12 | - second: 13 | description: <> and <> and <> 14 | /first: 15 | get: 16 | is: [ first: { fooBar: Hello } ] 17 | /second: 18 | type: { second: { applePie: World } } 19 | /third: 20 | /fourth: 21 | type: { second: { applePie: Finish } } 22 | -------------------------------------------------------------------------------- /spec/examples/raml/parametersinflection.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | traits: 6 | - keep: 7 | description: Keep <> 8 | - sing: 9 | description: Sing <> 10 | - plu: 11 | description: Plu <> 12 | /userName: 13 | get: 14 | is: [ keep ] 15 | post: 16 | is: [ plu ] 17 | /passwords: 18 | get: 19 | is: [ keep ] 20 | post: 21 | is: [ sing ] 22 | -------------------------------------------------------------------------------- /spec/examples/raml/protocols1.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | /a: 5 | get: 6 | /b: 7 | get: 8 | protocols: [ HTTP, HTTPS ] 9 | /c: 10 | get: 11 | protocols: [ HTTP ] 12 | /d: 13 | get: 14 | protocols: [ HTTPS ] 15 | -------------------------------------------------------------------------------- /spec/examples/raml/protocols2.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /a: 6 | get: 7 | /b: 8 | get: 9 | protocols: [ HTTP, HTTPS ] 10 | /c: 11 | get: 12 | protocols: [ HTTP ] 13 | /d: 14 | get: 15 | protocols: [ HTTPS ] 16 | -------------------------------------------------------------------------------- /spec/examples/raml/protocols3.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | protocols: [ HTTP, HTTPS ] 6 | /a: 7 | get: 8 | /b: 9 | get: 10 | protocols: [ HTTP, HTTPS ] 11 | /c: 12 | get: 13 | protocols: [ HTTP ] 14 | /d: 15 | get: 16 | protocols: [ HTTPS ] 17 | -------------------------------------------------------------------------------- /spec/examples/raml/queryparameters.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /a: 6 | get: 7 | queryParameters: 8 | q1: 9 | /b: 10 | get: 11 | queryParameters: 12 | q2: 13 | displayName: This is the second query parameter 14 | type: integer 15 | -------------------------------------------------------------------------------- /spec/examples/raml/requestbodies.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /first: 6 | post: 7 | body: 8 | application/json: 9 | example: | 10 | { 11 | "foo": "bar" 12 | } 13 | text/xml: 14 | put: 15 | body: 16 | application/json: 17 | example: | 18 | { 19 | "status": "not found", 20 | "message: "Resource could not be found" 21 | } 22 | -------------------------------------------------------------------------------- /spec/examples/raml/required.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /a/{b}: 6 | /c/{d}: 7 | uriParameters: 8 | d: 9 | /e/{f}: 10 | uriParameters: 11 | f: 12 | required: false 13 | /g/{h}: 14 | uriParameters: 15 | h: 16 | required: true 17 | /i: 18 | get: 19 | queryParameters: 20 | j: 21 | k: 22 | required: false 23 | l: 24 | required: true 25 | -------------------------------------------------------------------------------- /spec/examples/raml/resources.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /first: 6 | get: 7 | /second: 8 | /third: 9 | displayName: This is the third 10 | /with: 11 | /{uri}: 12 | /{params}: 13 | uriParameters: 14 | params: 15 | displayName: This are the params 16 | -------------------------------------------------------------------------------- /spec/examples/raml/resourcetypes.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | resourceTypes: 6 | - collection: 7 | description: A collection 8 | get: 9 | description: Get all items 10 | post: 11 | description: Create a new item 12 | /a: 13 | type: collection 14 | put: 15 | post: 16 | description: Overriden 17 | -------------------------------------------------------------------------------- /spec/examples/raml/responses.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /first: 6 | get: 7 | responses: 8 | 200: 9 | displayName: The first 10 | description: This is the first 11 | body: 12 | application/json: 13 | example: | 14 | { 15 | "foo": "bar" 16 | } 17 | text/xml: 18 | 404: 19 | body: 20 | application/json: 21 | example: | 22 | { 23 | "status": "not found", 24 | "message: "Resource could not be found" 25 | } 26 | -------------------------------------------------------------------------------- /spec/examples/raml/schemas.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | schemas: 6 | - schema1: | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | - schema2: | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | /route: 29 | get: 30 | body: 31 | text/xml: 32 | schema: schema1 33 | post: 34 | body: 35 | text/xml: 36 | schema: schema2 37 | put: 38 | body: 39 | text/xml: 40 | schema: | 41 | 42 | -------------------------------------------------------------------------------- /spec/examples/raml/securedby1.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | securitySchemes: 6 | - oauth_1_0: 7 | type: OAuth 1.0 8 | - oauth_2_0: 9 | type: OAuth 2.0 10 | /first: 11 | get: 12 | securedBy: [oauth_1_0] 13 | post: 14 | securedBy: [oauth_2_0] 15 | /second: 16 | securedBy: [oauth_1_0] 17 | get: 18 | securedBy: [null, oauth_2_0] 19 | post: 20 | securedBy: [oauth_2_0] 21 | -------------------------------------------------------------------------------- /spec/examples/raml/securedby2.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | securedBy: [oauth_2_0] 6 | securitySchemes: 7 | - oauth_1_0: 8 | type: OAuth 1.0 9 | - oauth_2_0: 10 | type: OAuth 2.0 11 | /first: 12 | get: 13 | securedBy: [oauth_1_0] 14 | post: 15 | securedBy: [oauth_2_0] 16 | /second: 17 | securedBy: [oauth_1_0] 18 | get: 19 | securedBy: [null, oauth_2_0] 20 | post: 21 | securedBy: [oauth_2_0] 22 | -------------------------------------------------------------------------------- /spec/examples/raml/securityschemes.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | securitySchemes: 6 | - oauth_2_0: 7 | description: | 8 | Dropbox supports OAuth 2.0 for authenticating all API requests. 9 | type: OAuth 2.0 10 | describedBy: 11 | headers: 12 | Authorization: 13 | description: | 14 | Used to send a valid OAuth 2 access token. Do not use 15 | with the "access_token" query string parameter. 16 | type: string 17 | queryParameters: 18 | access_token: 19 | description: | 20 | Used to send a valid OAuth 2 access token. Do not use together with 21 | the "Authorization" header 22 | type: string 23 | responses: 24 | 401: 25 | description: | 26 | Bad or expired token. This can happen if the user or Dropbox 27 | revoked or expired an access token. To fix, you should re- 28 | authenticate the user. 29 | 403: 30 | description: | 31 | Bad OAuth request (wrong consumer key, bad nonce, expired 32 | timestamp...). Unfortunately, re-authenticating the user won't help here. 33 | settings: 34 | authorizationUri: https://www.dropbox.com/1/oauth2/authorize 35 | accessTokenUri: https://api.dropbox.com/1/oauth2/token 36 | authorizationGrants: [ code, token ] 37 | - oauth_1_0: 38 | description: | 39 | OAuth 1.0 continues to be supported for all API requests, but OAuth 2.0 is now preferred. 40 | type: OAuth 1.0 41 | settings: 42 | requestTokenUri: https://api.dropbox.com/1/oauth/request_token 43 | authorizationUri: https://www.dropbox.com/1/oauth/authorize 44 | tokenCredentialsUri: https://api.dropbox.com/1/oauth/access_token 45 | - customHeader: 46 | description: | 47 | A custom 48 | -------------------------------------------------------------------------------- /spec/examples/raml/simple.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | version: v123 6 | mediaType: application/json 7 | -------------------------------------------------------------------------------- /spec/examples/raml/traits.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | traits: 6 | - searchable: 7 | description: This is searchable 8 | queryParameters: 9 | q: 10 | - sortable: 11 | description: This is sortable 12 | queryParameters: 13 | key: 14 | order: 15 | /a: 16 | get: 17 | is: [ searchable, sortable ] 18 | /b: 19 | get: 20 | is: [ searchable, sortable ] 21 | description: This is resource /a/b 22 | queryParameters: 23 | sort: 24 | q: 25 | displayName: This is the search term 26 | /c: 27 | is: [ sortable ] 28 | get: 29 | /d: 30 | is: [ sortable ] 31 | get: 32 | is: [ searchable ] 33 | -------------------------------------------------------------------------------- /spec/examples/raml/uriparameters.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /a: 6 | /b/{first}: 7 | /{second}: 8 | uriParameters: 9 | second: 10 | displayName: This is the second uri parameter 11 | type: integer 12 | /c/{third}: 13 | uriParameters: 14 | third: 15 | displayName: This is the third uri parameter 16 | 17 | -------------------------------------------------------------------------------- /spec/examples/raml_bad/uriparameters.raml: -------------------------------------------------------------------------------- 1 | #%RAML 0.8 2 | --- 3 | title: Example API 4 | baseUri: http://localhost:3000 5 | /a: 6 | /b/{first}: 7 | /{second}: 8 | uriParameters: 9 | first: 10 | second: 11 | -------------------------------------------------------------------------------- /spec/examples/yaml/include1.yml: -------------------------------------------------------------------------------- 1 | foo: bar 2 | inner: !include include2.yml 3 | -------------------------------------------------------------------------------- /spec/examples/yaml/include2.yml: -------------------------------------------------------------------------------- 1 | apple: pie 2 | -------------------------------------------------------------------------------- /spec/examples/yaml/simple.yml: -------------------------------------------------------------------------------- 1 | foo: bar 2 | empty: 3 | -------------------------------------------------------------------------------- /spec/examples/yaml/traversing.yml: -------------------------------------------------------------------------------- 1 | string: 'foobar' 2 | integer: 10 3 | hash: 4 | apple: pie 5 | array: 6 | - foo 7 | - bar 8 | array_complex: 9 | - foo: 10 | - bar: 11 | sub: 'element' 12 | -------------------------------------------------------------------------------- /spec/lib/raml_parser/snippet_generator_spec.rb: -------------------------------------------------------------------------------- 1 | require 'raml_parser/snippet_generator' 2 | 3 | RSpec.describe RamlParser::SnippetGenerator do 4 | it 'generate curl snippets' do 5 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/resources.raml') 6 | gen1 = RamlParser::SnippetGenerator.new(raml1) 7 | res1 = gen1.curl(raml1.resources[0], 'get') 8 | expect(res1).to eq 'curl -XGET http://localhost:3000/first' 9 | 10 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/requestbodies.raml') 11 | gen2 = RamlParser::SnippetGenerator.new(raml2) 12 | res2 = gen2.curl(raml2.resources[0], 'post') 13 | expect(res2).to include '-XPOST' 14 | expect(res2).to include 'Accept: application/json' 15 | expect(res2).to include '"foo": "bar"' 16 | 17 | raml3 = RamlParser::Parser.parse_file('spec/examples/raml/required.raml') 18 | gen3 = RamlParser::SnippetGenerator.new(raml3) 19 | res3 = gen3.curl(raml3.resources[4], 'get') 20 | expect(res3).to eq 'curl -XGET http://localhost:3000/i?l=value' 21 | end 22 | 23 | it 'generate javascript vanilla snippets' do 24 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/resources.raml') 25 | gen1 = RamlParser::SnippetGenerator.new(raml1) 26 | res1 = gen1.javascript_vanilla(raml1.resources[0], 'get') 27 | expect(res1).to include 'GET' 28 | expect(res1).to include 'http://localhost:3000/first' 29 | 30 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/requestbodies.raml') 31 | gen2 = RamlParser::SnippetGenerator.new(raml2) 32 | res2 = gen2.javascript_vanilla(raml2.resources[0], 'post') 33 | expect(res2).to include 'Accept' 34 | expect(res2).to include 'application/json' 35 | expect(res2).to include '"foo": "bar"' 36 | 37 | raml3 = RamlParser::Parser.parse_file('spec/examples/raml/required.raml') 38 | gen3 = RamlParser::SnippetGenerator.new(raml3) 39 | res3 = gen3.javascript_vanilla(raml3.resources[4], 'get') 40 | expect(res3).to include 'GET' 41 | expect(res3).to include 'http://localhost:3000/i?l=value' 42 | end 43 | 44 | it 'generate ruby snippets' do 45 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/resources.raml') 46 | gen1 = RamlParser::SnippetGenerator.new(raml1) 47 | res1 = gen1.ruby(raml1.resources[0], 'get') 48 | expect(res1).to include 'Net::HTTP::Get' 49 | expect(res1).to include 'http://localhost:3000/first' 50 | 51 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/requestbodies.raml') 52 | gen2 = RamlParser::SnippetGenerator.new(raml2) 53 | res2 = gen2.ruby(raml2.resources[0], 'post') 54 | expect(res2).to include 'Net::HTTP::Post' 55 | expect(res2).to include 'Accept' 56 | expect(res2).to include 'application/json' 57 | expect(res2).to include '"foo": "bar"' 58 | 59 | raml3 = RamlParser::Parser.parse_file('spec/examples/raml/required.raml') 60 | gen3 = RamlParser::SnippetGenerator.new(raml3) 61 | res3 = gen3.ruby(raml3.resources[4], 'get') 62 | expect(res3).to include 'Net::HTTP::Get' 63 | expect(res3).to include 'http://localhost:3000/i?l=value' 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /spec/lib/raml_parser/yaml_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require 'raml_parser/yaml_helper' 2 | 3 | RSpec.describe RamlParser::YamlHelper do 4 | it 'reads simple YAML file' do 5 | yml = RamlParser::YamlHelper.read_yaml('spec/examples/yaml/simple.yml') 6 | expect(yml['foo']).to eq 'bar' 7 | end 8 | 9 | it 'works with include tags' do 10 | yml = RamlParser::YamlHelper.read_yaml('spec/examples/yaml/include1.yml') 11 | expect(yml['foo']).to eq 'bar' 12 | expect(yml['inner']['apple']).to eq 'pie' 13 | end 14 | end 15 | 16 | RSpec.describe RamlParser::YamlNode do 17 | it 'has a working array/array_map method' do 18 | yml = RamlParser::YamlHelper.read_yaml('spec/examples/yaml/traversing.yml') 19 | root = RamlParser::YamlNode.new(nil, 'root', yml) 20 | expect(root.hash('array').array(0).value).to eq 'foo' 21 | expect(root.hash('array').array_values { |n| n.key }).to eq ['[0]', '[1]'] 22 | end 23 | 24 | it 'has a working hash/hash_map method' do 25 | yml = RamlParser::YamlHelper.read_yaml('spec/examples/yaml/traversing.yml') 26 | root = RamlParser::YamlNode.new(nil, 'root', yml) 27 | expect(root.hash('integer').value).to eq 10 28 | expect(root.hash_values { |n| 0 }).to eq ({'string'=>0, 'integer'=>0, 'hash'=>0, 'array'=>0, 'array_complex'=>0}) 29 | end 30 | 31 | it 'has a working arrayhash/arrayhash_map method' do 32 | yml = RamlParser::YamlHelper.read_yaml('spec/examples/yaml/traversing.yml') 33 | root = RamlParser::YamlNode.new(nil, 'root', yml) 34 | expect(root.hash('array_complex').arrayhash(0).key).to eq 'foo' 35 | expect(root.hash('array_complex').arrayhash(0).value).to eq nil 36 | expect(root.hash('array_complex').arrayhash_values { |n| n.value }).to eq ({'foo'=>nil, 'bar'=>{'sub'=>'element'}}) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/lib/raml_parser_spec.rb: -------------------------------------------------------------------------------- 1 | require 'raml_parser' 2 | 3 | RSpec.describe RamlParser::Parser do 4 | it 'finds all resources' do 5 | raml = RamlParser::Parser.parse_file('spec/examples/raml/resources.raml') 6 | expect(raml.resources.map { |r| r.absolute_uri }).to eq [ 7 | 'http://localhost:3000/first', 8 | 'http://localhost:3000/first/second', 9 | 'http://localhost:3000/third', 10 | 'http://localhost:3000/with', 11 | 'http://localhost:3000/with/{uri}', 12 | 'http://localhost:3000/with/{uri}/{params}' 13 | ] 14 | end 15 | 16 | it 'parses basic globals' do 17 | raml = RamlParser::Parser.parse_file('spec/examples/raml/simple.raml') 18 | expect(raml.title).to eq 'Example API' 19 | expect(raml.base_uri).to eq 'http://localhost:3000' 20 | expect(raml.version).to eq 'v123' 21 | expect(raml.media_type).to eq 'application/json' 22 | end 23 | 24 | it 'parses URI parameters' do 25 | raml = RamlParser::Parser.parse_file('spec/examples/raml/uriparameters.raml') 26 | expect(raml.resources[0].uri_parameters.map { |_,param| param.name }).to eq [] 27 | expect(raml.resources[1].uri_parameters.map { |_,param| param.name }).to eq ['first'] 28 | expect(raml.resources[2].uri_parameters.map { |_,param| param.name }).to eq ['first', 'second'] 29 | expect(raml.resources[3].uri_parameters.map { |_,param| param.name }).to eq ['third'] 30 | expect(raml.resources[2].uri_parameters['first'].display_name).to eq 'first' 31 | expect(raml.resources[2].uri_parameters['first'].type).to eq 'string' 32 | expect(raml.resources[2].uri_parameters['second'].display_name).to eq 'This is the second uri parameter' 33 | expect(raml.resources[2].uri_parameters['second'].type).to eq 'integer' 34 | end 35 | 36 | it 'parses query parameters' do 37 | raml = RamlParser::Parser.parse_file('spec/examples/raml/queryparameters.raml') 38 | expect(raml.resources[0].methods['get'].query_parameters.map { |name,_| name }).to eq ['q1'] 39 | expect(raml.resources[0].methods['get'].query_parameters.map { |_,param| param.name }).to eq ['q1'] 40 | expect(raml.resources[1].methods['get'].query_parameters.map { |name,_| name }).to eq ['q2'] 41 | expect(raml.resources[1].methods['get'].query_parameters.map { |_,param| param.name }).to eq ['q2'] 42 | expect(raml.resources[0].methods['get'].query_parameters['q1'].display_name).to eq 'q1' 43 | expect(raml.resources[0].methods['get'].query_parameters['q1'].type).to eq 'string' 44 | expect(raml.resources[1].methods['get'].query_parameters['q2'].display_name).to eq 'This is the second query parameter' 45 | expect(raml.resources[1].methods['get'].query_parameters['q2'].type).to eq 'integer' 46 | end 47 | 48 | it 'parses methods' do 49 | raml = RamlParser::Parser.parse_file('spec/examples/raml/methods.raml') 50 | expect(raml.resources[0].methods['get'].method).to eq 'GET' 51 | expect(raml.resources[1].methods['get'].method).to eq 'GET' 52 | end 53 | 54 | it 'parses responses' do 55 | raml = RamlParser::Parser.parse_file('spec/examples/raml/responses.raml') 56 | expect(raml.resources[0].methods['get'].responses.map { |code,_| code }).to eq [200, 404] 57 | expect(raml.resources[0].methods['get'].responses.map { |_,res| res.status_code }).to eq [200, 404] 58 | end 59 | 60 | it 'parses bodies' do 61 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/responses.raml') 62 | expect(raml1.resources[0].methods['get'].responses[200].bodies.map { |type,_| type }).to eq ['application/json', 'text/xml'] 63 | expect(raml1.resources[0].methods['get'].responses[200].bodies['application/json'].example).to_not eq nil 64 | 65 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/requestbodies.raml') 66 | expect(raml2.resources[0].methods['post'].bodies.map { |type,_| type }).to eq ['application/json', 'text/xml'] 67 | expect(raml2.resources[0].methods['put'].bodies['application/json'].example).to_not eq nil 68 | end 69 | 70 | it 'parses headers' do 71 | raml = RamlParser::Parser.parse_file('spec/examples/raml/headers.raml') 72 | expect(raml.resources[0].methods['get'].headers['X-Foobar-Ping'].description).to eq 'Ping' 73 | expect(raml.resources[0].methods['get'].responses[200].headers['X-Foobar-Pong'].description).to eq 'Pong' 74 | end 75 | 76 | it 'parses form parameters' do 77 | raml = RamlParser::Parser.parse_file('spec/examples/raml/formparameters.raml') 78 | expect(raml.resources[0].methods['post'].bodies['application/x-www-form-urlencoded'].form_parameters['from'].description).to eq 'FROM1' 79 | expect(raml.resources[0].methods['post'].bodies['application/x-www-form-urlencoded'].form_parameters['to'].description).to eq 'TO1' 80 | expect(raml.resources[1].methods['post'].bodies['multipart/form-data'].form_parameters['from'].description).to eq 'FROM2' 81 | expect(raml.resources[1].methods['post'].bodies['multipart/form-data'].form_parameters['to'].description).to eq 'TO2' 82 | end 83 | 84 | it 'parses security schemes' do 85 | raml = RamlParser::Parser.parse_file('spec/examples/raml/securityschemes.raml') 86 | expect(raml.security_schemes.keys).to eq ['oauth_2_0', 'oauth_1_0', 'customHeader'] 87 | expect(raml.security_schemes['oauth_2_0'].type).to eq 'OAuth 2.0' 88 | expect(raml.security_schemes['oauth_1_0'].type).to eq 'OAuth 1.0' 89 | expect(raml.security_schemes['customHeader'].type).to eq nil 90 | 91 | expect(raml.security_schemes['oauth_2_0'].settings['authorizationUri']).to eq 'https://www.dropbox.com/1/oauth2/authorize' 92 | expect(raml.security_schemes['oauth_2_0'].described_by.headers['Authorization'].description).to start_with 'Used to send' 93 | end 94 | 95 | it 'parses documentation' do 96 | raml = RamlParser::Parser.parse_file('spec/examples/raml/documentation.raml') 97 | expect(raml.documentation[0].title).to eq 'Home' 98 | expect(raml.documentation[1].title).to eq 'FAQ' 99 | end 100 | 101 | it 'parses schemas' do 102 | raml = RamlParser::Parser.parse_file('spec/examples/raml/schemas.raml') 103 | expect(raml.schemas['schema1']).to start_with '' 104 | expect(raml.schemas['schema2']).to start_with '' 105 | expect(raml.resources[0].methods['get'].bodies['text/xml'].schema).to start_with '' 106 | expect(raml.resources[0].methods['post'].bodies['text/xml'].schema).to start_with '' 107 | expect(raml.resources[0].methods['put'].bodies['text/xml'].schema).to start_with '' 108 | end 109 | 110 | it 'parses base URI parameters' do 111 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/baseuriparameters1.raml') 112 | expect(raml1.resources[0].absolute_uri).to eq 'http://localhost:3000/v4/a' 113 | expect(raml1.base_uri_parameters).to eq ({}) 114 | 115 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/baseuriparameters2.raml') 116 | expect(raml2.base_uri_parameters.map { |_,p| p.name }).to eq ['user', 'language'] 117 | expect(raml2.resources[0].uri_parameters.map { |_,p| p.name }).to eq [] 118 | expect(raml2.resources[0].base_uri_parameters.map { |_,p| p.name }).to eq ['user', 'language'] 119 | expect(raml2.resources[1].uri_parameters.map { |_,p| p.name }).to eq ['next'] 120 | expect(raml2.resources[1].base_uri_parameters.map { |_,p| p.name }).to eq ['user', 'language'] 121 | expect(raml2.resources[2].uri_parameters.map { |_,p| p.name }).to eq ['next'] 122 | expect(raml2.resources[2].base_uri_parameters.map { |_,p| p.name }).to eq ['user', 'language'] 123 | expect(raml2.resources[0].base_uri_parameters['user'].description).to eq 'The user' 124 | expect(raml2.resources[1].base_uri_parameters['user'].description).to eq 'The user' 125 | expect(raml2.resources[2].base_uri_parameters['user'].description).to eq 'Changed' 126 | end 127 | 128 | it 'parses protocols' do 129 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/protocols1.raml') 130 | expect(raml1.protocols).to eq [] 131 | expect(raml1.resources[0].methods['get'].protocols).to eq %w() 132 | expect(raml1.resources[1].methods['get'].protocols).to eq %w(HTTP HTTPS) 133 | expect(raml1.resources[2].methods['get'].protocols).to eq %w(HTTP) 134 | expect(raml1.resources[3].methods['get'].protocols).to eq %w(HTTPS) 135 | 136 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/protocols2.raml') 137 | expect(raml2.protocols).to eq ['HTTP'] 138 | expect(raml2.resources[0].methods['get'].protocols).to eq %w(HTTP) 139 | expect(raml2.resources[1].methods['get'].protocols).to eq %w(HTTP HTTPS) 140 | expect(raml2.resources[2].methods['get'].protocols).to eq %w(HTTP) 141 | expect(raml2.resources[3].methods['get'].protocols).to eq %w(HTTPS) 142 | 143 | raml3 = RamlParser::Parser.parse_file('spec/examples/raml/protocols3.raml') 144 | expect(raml3.protocols).to eq ['HTTP', 'HTTPS'] 145 | expect(raml3.resources[0].methods['get'].protocols).to eq %w(HTTP HTTPS) 146 | expect(raml3.resources[1].methods['get'].protocols).to eq %w(HTTP HTTPS) 147 | expect(raml3.resources[2].methods['get'].protocols).to eq %w(HTTP) 148 | expect(raml3.resources[3].methods['get'].protocols).to eq %w(HTTPS) 149 | end 150 | 151 | it 'handle secured by' do 152 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/securedby1.raml') 153 | expect(raml1.resources[0].methods['get'].secured_by).to eq ['oauth_1_0'] 154 | expect(raml1.resources[0].methods['post'].secured_by).to eq ['oauth_2_0'] 155 | expect(raml1.resources[1].methods['get'].secured_by).to eq ['oauth_1_0', nil, 'oauth_2_0'] 156 | expect(raml1.resources[1].methods['post'].secured_by).to eq ['oauth_1_0', 'oauth_2_0'] 157 | 158 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/securedby2.raml') 159 | expect(raml2.resources[0].methods['get'].secured_by).to eq ['oauth_2_0', 'oauth_1_0'] 160 | expect(raml2.resources[0].methods['post'].secured_by).to eq ['oauth_2_0'] 161 | expect(raml2.resources[1].methods['get'].secured_by).to eq ['oauth_2_0', 'oauth_1_0', nil] 162 | expect(raml2.resources[1].methods['post'].secured_by).to eq ['oauth_2_0', 'oauth_1_0'] 163 | end 164 | 165 | it 'mixes in traits' do 166 | raml = RamlParser::Parser.parse_file('spec/examples/raml/traits.raml') 167 | expect(raml.resources[0].methods['get'].query_parameters.map { |name,_| name }).to eq ['q', 'key', 'order'] 168 | expect(raml.resources[0].methods['get'].description).to eq 'This is sortable' 169 | expect(raml.resources[1].methods['get'].query_parameters.map { |name,_| name }).to eq ['q', 'key', 'order', 'sort'] 170 | expect(raml.resources[1].methods['get'].description).to eq 'This is resource /a/b' 171 | expect(raml.resources[2].methods['get'].query_parameters.map { |name,_| name }).to eq ['key', 'order'] 172 | expect(raml.resources[3].methods['get'].query_parameters.map { |name,_| name }).to eq ['key', 'order', 'q'] 173 | end 174 | 175 | it 'mixes in resource types' do 176 | raml = RamlParser::Parser.parse_file('spec/examples/raml/resourcetypes.raml') 177 | expect(raml.resources[0].methods.keys).to eq ['get', 'post', 'put'] 178 | expect(raml.resources[0].methods['get'].description).to eq 'Get all items' 179 | expect(raml.resources[0].methods['post'].description).to eq 'Overriden' 180 | expect(raml.resources[0].methods['put'].description).to eq nil 181 | end 182 | 183 | it 'falls back to default display name' do 184 | raml1 = RamlParser::Parser.parse_file('spec/examples/raml/resources.raml') 185 | expect(raml1.resources[1].display_name).to eq '/first/second' 186 | expect(raml1.resources[2].display_name).to eq 'This is the third' 187 | 188 | raml2 = RamlParser::Parser.parse_file('spec/examples/raml/resources.raml') 189 | expect(raml2.resources[5].uri_parameters['uri'].display_name).to eq 'uri' 190 | expect(raml2.resources[5].uri_parameters['params'].display_name).to eq 'This are the params' 191 | 192 | raml3 = RamlParser::Parser.parse_file('spec/examples/raml/queryparameters.raml') 193 | expect(raml3.resources[0].methods['get'].query_parameters['q1'].display_name).to eq 'q1' 194 | expect(raml3.resources[1].methods['get'].query_parameters['q2'].display_name).to eq 'This is the second query parameter' 195 | 196 | raml4 = RamlParser::Parser.parse_file('spec/examples/raml/headers.raml') 197 | expect(raml4.resources[0].methods['get'].headers['X-Foobar-Ping'].display_name).to eq 'X-Foobar-Ping' 198 | expect(raml4.resources[0].methods['get'].responses[200].headers['X-Foobar-Pong'].display_name).to eq 'PingPong' 199 | end 200 | 201 | it 'properly sets required property' do 202 | raml = RamlParser::Parser.parse_file('spec/examples/raml/required.raml') 203 | expect(raml.resources[0].uri_parameters['b'].required).to eq true 204 | expect(raml.resources[1].uri_parameters['d'].required).to eq true 205 | expect(raml.resources[2].uri_parameters['f'].required).to eq false 206 | expect(raml.resources[3].uri_parameters['h'].required).to eq true 207 | expect(raml.resources[4].methods['get'].query_parameters['j'].required).to eq false 208 | expect(raml.resources[4].methods['get'].query_parameters['k'].required).to eq false 209 | expect(raml.resources[4].methods['get'].query_parameters['l'].required).to eq true 210 | end 211 | 212 | it 'fixed issue #2' do 213 | raml = RamlParser::Parser.parse_file('spec/examples/raml/issue2.raml') 214 | expect(raml.resources[0].methods.keys).to eq ['post', 'get'] 215 | expect(raml.resources[0].methods['get'].method).to eq 'GET' 216 | expect(raml.resources[0].methods['post'].method).to eq 'POST' 217 | end 218 | 219 | it 'handles parametrization of traits and resource types' do 220 | raml = RamlParser::Parser.parse_file('spec/examples/raml/parameters.raml') 221 | expect(raml.resources[0].methods['get'].description).to eq '/first and first and get and Hello' 222 | expect(raml.resources[0].methods['get'].query_parameters['get'].description).to eq 'Applepie' 223 | expect(raml.resources[1].description).to eq '/second and second and World' 224 | expect(raml.resources[3].description).to eq '/third/fourth and fourth and Finish' 225 | end 226 | 227 | it 'handles singularization/pluralization of parametrization' do 228 | raml = RamlParser::Parser.parse_file('spec/examples/raml/parametersinflection.raml') 229 | 230 | expect(raml.resources[0].methods['get'].description).to eq 'Keep userName' 231 | expect(raml.resources[0].methods['post'].description).to eq 'Plu userNames' 232 | expect(raml.resources[1].methods['get'].description).to eq 'Keep passwords' 233 | expect(raml.resources[1].methods['post'].description).to eq 'Sing password' 234 | end 235 | 236 | it 'does not fail on any example RAML file' do 237 | files = Dir.glob('spec/examples/raml/**/*.raml') 238 | files.each { |f| 239 | result = RamlParser::Parser.parse_file_with_marks(f) 240 | 241 | expect(result[:marks]).to all(satisfy do |p,m| 242 | m == :used or m == :unsupported 243 | end) 244 | } 245 | end 246 | 247 | it 'fail on any bad example RAML file' do 248 | files = Dir.glob('spec/examples/raml_bad/**/*.raml') 249 | files.each { |f| 250 | expect { RamlParser::Parser.parse_file_with_marks(f) }.to raise_error 251 | } 252 | end 253 | end 254 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | RSpec.configure do |config| 20 | # rspec-expectations config goes here. You can use an alternate 21 | # assertion/expectation library such as wrong or the stdlib/minitest 22 | # assertions if you prefer. 23 | config.expect_with :rspec do |expectations| 24 | # This option will default to `true` in RSpec 4. It makes the `description` 25 | # and `failure_message` of custom matchers include text for helper methods 26 | # defined using `chain`, e.g.: 27 | # be_bigger_than(2).and_smaller_than(4).description 28 | # # => "be bigger than 2 and smaller than 4" 29 | # ...rather than: 30 | # # => "be bigger than 2" 31 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 32 | end 33 | 34 | # rspec-mocks config goes here. You can use an alternate test double 35 | # library (such as bogus or mocha) by changing the `mock_with` option here. 36 | config.mock_with :rspec do |mocks| 37 | # Prevents you from mocking or stubbing a method that does not exist on 38 | # a real object. This is generally recommended, and will default to 39 | # `true` in RSpec 4. 40 | mocks.verify_partial_doubles = true 41 | end 42 | 43 | # The settings below are suggested to provide a good initial experience 44 | # with RSpec, but feel free to customize to your heart's content. 45 | =begin 46 | # These two settings work together to allow you to limit a spec run 47 | # to individual examples or groups you care about by tagging them with 48 | # `:focus` metadata. When nothing is tagged with `:focus`, all examples 49 | # get run. 50 | config.filter_run :focus 51 | config.run_all_when_everything_filtered = true 52 | 53 | # Limits the available syntax to the non-monkey patched syntax that is 54 | # recommended. For more details, see: 55 | # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax 56 | # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 57 | # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching 58 | config.disable_monkey_patching! 59 | 60 | # This setting enables warnings. It's recommended, but in some cases may 61 | # be too noisy due to issues in dependencies. 62 | config.warnings = true 63 | 64 | # Many RSpec users commonly either run the entire suite or an individual 65 | # file, and it's useful to allow more verbose output when running an 66 | # individual spec file. 67 | if config.files_to_run.one? 68 | # Use the documentation formatter for detailed output, 69 | # unless a formatter has already been configured 70 | # (e.g. via a command-line flag). 71 | config.default_formatter = 'doc' 72 | end 73 | 74 | # Print the 10 slowest examples and example groups at the 75 | # end of the spec run, to help surface which specs are running 76 | # particularly slow. 77 | config.profile_examples = 10 78 | 79 | # Run specs in random order to surface order dependencies. If you find an 80 | # order dependency and want to debug it, you can fix the order by providing 81 | # the seed, which is printed after each run. 82 | # --seed 1234 83 | config.order = :random 84 | 85 | # Seed global randomization in this process using the `--seed` CLI option. 86 | # Setting this allows you to use `--seed` to deterministically reproduce 87 | # test failures related to randomization by passing the same `--seed` value 88 | # as the one that triggered the failure. 89 | Kernel.srand config.seed 90 | =end 91 | end 92 | --------------------------------------------------------------------------------