├── .gitignore
├── .gitmodules
├── .rubocop.yml
├── .travis.yml
├── Gemfile
├── Guardfile
├── Installation.md
├── LICENSE
├── README.md
├── Rakefile
├── Vagrantfile
├── lib
├── redsnow.rb
└── redsnow
│ ├── binding.rb
│ ├── blueprint.rb
│ ├── object.rb
│ ├── parseresult.rb
│ ├── sourcemap.rb
│ └── version.rb
├── provisioning.sh
├── redsnow.gemspec
└── test
├── .rubocop.yml
├── _helper.rb
├── example.rb
├── fixtures
├── sample-api-ast.json
├── sample-api-sourcemap.json
└── sample-api.apib
├── redsnow_binding_test.rb
├── redsnow_options_test.rb
├── redsnow_parseresult_test.rb
├── redsnow_sourcemap_test.rb
└── redsnow_test.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | *.gem
2 | *.rbc
3 | /.config
4 | /coverage/
5 | /InstalledFiles
6 | /pkg/
7 | /spec/reports/
8 | /test/tmp/
9 | /test/version_tmp/
10 | /tmp/
11 |
12 | ## Specific to RubyMotion:
13 | .dat*
14 | .repl_history
15 | build/
16 |
17 | ## Documentation cache and generated files:
18 | /.yardoc/
19 | /_yardoc/
20 | /doc/
21 | /rdoc/
22 |
23 | ## Environment normalisation:
24 | /.bundle/
25 | /lib/bundler/man/
26 |
27 | # for a library or gem, you might want to ignore these files since the code is
28 | # intended to run in multiple environments; otherwise, check them in:
29 | Gemfile.lock
30 | .ruby-version
31 | .ruby-gemset
32 |
33 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34 | .rvmrc
35 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "ext/drafter"]
2 | path = ext/drafter
3 | url = https://github.com/apiaryio/drafter.git
4 |
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | Exclude:
3 | - 'ext/**/*'
4 | - 'test/fixtures/**/*'
5 |
6 | # Offense count: 283
7 | # Configuration parameters: AllowURI, URISchemes.
8 | Metrics/LineLength:
9 | Max: 300
10 |
11 | # Offense count: 17
12 | # Configuration parameters: CountComments.
13 | Metrics/MethodLength:
14 | Max: 36
15 |
16 | Metrics/AbcSize:
17 | Max: 42
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: ruby
3 | rvm:
4 | - 1.9.3
5 | - 2.1.0
6 | - '2.2'
7 | script:
8 | - rake test
9 | - rake install
10 | - bundle exec rubocop lib test Rakefile redsnow.gemspec
11 | before_install:
12 | - git submodule update --init --recursive
13 | - gem update bundler
14 | - bundle
15 | env: RECOMPILE=true
16 | notifications:
17 | email:
18 | recipients:
19 | - ladislav@apiary.io
20 | on_success: change
21 | on_failure: always
22 | hipchat:
23 | rooms:
24 | secure: d1RJSY+QU7y01wmqvxpgYsAyRzNzbAK2r1Ut9X87s5UOFB+5yxUgRocs4sKpGSpm0UVtD0u/G3H1wqbmWpxnYpmQYilTw9Qa04GDwJJ2/IraHU1fpGG+gZCsOMid7NjNl6jErbY8cfoUmCQdrUiYBgLowtxh5i8HbPT//Cb7mhU=
25 | template:
26 | - ! '%{repository}#%{build_number} (%{branch} - %{commit} : %{author}): %{message}
27 | (Details/Change view)'
28 | format: html
29 | deploy:
30 | provider: rubygems
31 | api_key:
32 | secure: H1fybpyLM915pBlPwn6rtrZ+IBzRumTasMC/vuwcJarBTiqIvYW4XXveA4Y2FNkuo7amCjjwujLDKUImhBM0Yyk0d2WTDC6izSUyP9/+MraicnKYX/biTzW13re/sUPr/FXI4HupvZsjBcKYA6F/eidPf4QM4O2LZ89FpW23/GY=
33 | gem: redsnow
34 | on:
35 | all_branches: true
36 | tags: true
37 | rvm: 1.9.3
38 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in red_snow.gemspec
4 | gemspec
5 |
6 | # Lock dependencies for TravisCI builds using older Ruby versions
7 | gem 'activesupport', '< 5'
8 | gem 'listen', '< 3.1'
9 | gem 'rubocop', '~> 0.32.1'
10 |
--------------------------------------------------------------------------------
/Guardfile:
--------------------------------------------------------------------------------
1 | # A sample Guardfile
2 | # More info at https://github.com/guard/guard#readme
3 |
4 | guard :rubocop do
5 | watch(/.+\.rb$/)
6 | watch(/(?:.+\/)?\.rubocop\.yml$/) { |m| File.dirname(m[0]) }
7 | end
8 |
--------------------------------------------------------------------------------
/Installation.md:
--------------------------------------------------------------------------------
1 |
2 | # RedSnow Installation
3 |
4 | For installing Ruby we recommend use [RVM](https://rvm.io/rvm/install).
5 |
6 | ## Ubuntu 14.04 ([Vagrantfile](Vagrantfile))
7 |
8 | Install ruby and prerequisites
9 |
10 | # GCC 4.7
11 | sudo apt-get update -y
12 | sudo apt-get install -y python-software-properties
13 | sudo apt-get update -y
14 | sudo apt-get install -y build-essential git-core curl
15 |
16 | # RVM gpg key
17 | gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
18 |
19 | # RVM Install
20 | curl -sSL https://get.rvm.io | bash -s stable
21 | source ~/.rvm/scripts/rvm
22 |
23 | # Installing required packages: libreadline6-dev, zlib1g-dev, libssl-dev, libyaml-dev, libsqlite3-dev, sqlite3, autoconf, libgdbm-dev, libncurses5-dev, automake, libtool, bison, pkg-config, libffi-dev
24 | rvm install 2.1.5
25 | rvm ruby-2.1.5
26 | gem install bundler
27 |
28 | Install RedSnow
29 |
30 | gem install redsnow
31 |
32 | Run example for test
33 |
34 | $ ruby test/example.rb
35 |
36 | This should be the output
37 |
38 | My API
39 | 0
40 | 8
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Apiary
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.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | # RedSnow [](https://travis-ci.org/apiaryio/redsnow)
4 | ### API Blueprint Parser for Ruby
5 |
6 | **NOTE**: *This library is deprecated and unmaintained. We recommend using [drafter](https://github.com/apiaryio/drafter) directly via [libffi](https://github.com/ffi/ffi/wiki) [for example](https://github.com/apiaryio/drafter/pull/356#issuecomment-229733343).*
7 |
8 | Ruby binding for the [Snow Crash](https://github.com/apiaryio/snowcrash) library, also a thermonuclear weapon.
9 |
10 | API Blueprint is Web API documentation language. You can find API Blueprint documentation on the [API Blueprint site](http://apiblueprint.org).
11 |
12 | ## Install
13 | The best way to install RedSnow is by using its [GEM package](https://rubygems.org/gems/redsnow).
14 |
15 | gem install redsnow
16 |
17 | Installation instructions for Ubuntu 14.04 are described in detail [here](Installation.md).
18 |
19 | ## Documentation
20 |
21 | - [Documentation at rubydoc](http://rubydoc.info/gems/redsnow/)
22 |
23 | ## Getting started
24 |
25 | ```ruby
26 | require 'redsnow'
27 |
28 | result = RedSnow.parse('# My API', exportSourcemap: true)
29 | puts result.ast.name
30 | puts result.sourcemap.name
31 | ```
32 |
33 | ## Parsing options
34 |
35 | Options can be number or hash. We support `:requireBlueprintName` and `:exportSourcemap` option.
36 |
37 | ```ruby
38 | require 'redsnow'
39 |
40 | result = RedSnow::parse('# My API', { :exportSourcemap => true })
41 | puts result.ast.name
42 | puts result.sourcemap.name
43 | ```
44 |
45 | ## Hacking Redsnow
46 | You are welcome to contribute. Use following steps to build & test Redsnow.
47 |
48 | ### Build
49 |
50 |
51 | 1. If needed, install bundler:
52 |
53 | ```sh
54 | $ gem install bundler
55 | ```
56 |
57 | 2. Clone the repo + fetch the submodules:
58 |
59 | ```sh
60 | $ git clone git://github.com/apiaryio/redsnow.git
61 | $ cd redsnow
62 | $ git submodule update --init --recursive
63 | ```
64 |
65 | 3. Build:
66 |
67 | ```sh
68 | $ rake
69 | ```
70 |
71 | ### Test
72 | Inside the redsnow repository run:
73 |
74 | ```sh
75 | $ bundle install
76 | $ rake test
77 | ```
78 |
79 | ### Release
80 | Use `rake install` to test locally released version.
81 |
82 | ```sh
83 | $ rake release
84 | ```
85 |
86 | ### Contribute
87 | Fork & Pull Request.
88 |
89 | ## License
90 | MIT License. See the [LICENSE](https://github.com/apiaryio/protagonist/blob/master/LICENSE) file.
91 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | begin
2 | require 'bundler/gem_tasks'
3 | rescue LoadError
4 | puts 'Cannot load bundler/gem_tasks'
5 | end
6 |
7 | begin
8 | require 'rake/testtask'
9 | rescue LoadError
10 | puts 'Cannot load rake/testtask'
11 | end
12 |
13 | begin
14 | require 'ffi'
15 | rescue LoadError
16 | puts 'Cannot load ffi'
17 | end
18 |
19 | begin
20 | require 'yard'
21 | rescue LoadError
22 | puts 'Cannot load yard'
23 | end
24 |
25 | task default: :compile
26 |
27 | desc 'Compile extension'
28 | task :compile do
29 | prefix = FFI::Platform.mac? ? '' : 'lib.target/'
30 | # Path to compiled drafter library
31 | path = File.expand_path("ext/drafter/build/out/Release/#{prefix}libdrafter.#{FFI::Platform::LIBSUFFIX}", File.dirname(__FILE__))
32 | puts "Path to library #{path}"
33 | if !File.exist?(path) || ENV['RECOMPILE']
34 | unless File.directory?(File.expand_path('ext/drafter/src'))
35 | puts 'Initializing submodules (if required)...'
36 | `git submodule update --init --recursive 2>/dev/null`
37 | end
38 | puts 'Compiling extension...'
39 | `cd #{File.expand_path('ext/drafter/')} && ./configure --shared && make`
40 | status = $CHILD_STATUS.to_i
41 | if status == 0
42 | puts 'Compiling done.'
43 | else
44 | puts 'Compiling error, exiting.'
45 | next # If i'm using exit, abort I have some errors in rake install but gem can be installed
46 | end
47 | else
48 | puts 'Extension already compiled. To recompile set env variable RECOMPILE=true.'
49 | end
50 | end
51 |
52 | desc 'Run tests'
53 | Rake::TestTask.new(:test) do |test|
54 | Rake::Task['compile'].invoke
55 |
56 | test.libs << 'lib' << 'test'
57 | test.test_files = FileList['test/*_test.rb']
58 | test.verbose = true
59 | end
60 |
61 | # ----- Documentation tasks ---------------------------------------------------
62 |
63 | YARD::Rake::YardocTask.new(:doc) do |t|
64 | t.options = %w( --embed-mixins --markup=markdown )
65 | end
66 |
--------------------------------------------------------------------------------
/Vagrantfile:
--------------------------------------------------------------------------------
1 | Vagrant.configure('2') do |config|
2 | config.vm.box = 'ubuntu/trusty64'
3 |
4 | # VirtualBox
5 | config.vm.provider :virtualbox do |vb|
6 | vb.customize ['modifyvm', :id, '--memory', '2048']
7 | vb.customize ['modifyvm', :id, '--cpus', '1']
8 | end
9 |
10 | # VMWare Fusion
11 | config.vm.provider :vmware_fusion do |vb|
12 | vb.vmx['memsize'] = '4096'
13 | vb.vmx['numvcpus'] = '1'
14 | end
15 |
16 | config.vm.network :private_network, ip: '10.3.3.3'
17 | config.vm.synced_folder '.', '/vagrant', id: 'vagrant-root'
18 | config.vm.provision :shell, path: './provisioning.sh'
19 | end
20 |
--------------------------------------------------------------------------------
/lib/redsnow.rb:
--------------------------------------------------------------------------------
1 | require 'redsnow/version'
2 | require 'redsnow/binding'
3 | require 'redsnow/blueprint'
4 | require 'redsnow/sourcemap'
5 | require 'redsnow/parseresult'
6 | require 'ffi'
7 |
8 | # RedSnow
9 | module RedSnow
10 | include Binding
11 |
12 | # Options
13 | EXPORT_SOURCEMAP_OPTION_KEY = :exportSourcemap
14 | REQUIRE_BLUEPRINT_NAME_OPTION_KEY = :requireBlueprintName
15 |
16 | # Parse options
17 | attr_accessor :options
18 | def self.parse_options(options)
19 | # Parse Options
20 | if options.is_a?(Numeric)
21 | return options
22 | else
23 | opt = 0
24 | opt |= (1 << 1) if options[REQUIRE_BLUEPRINT_NAME_OPTION_KEY]
25 | opt |= (1 << 2) if options[EXPORT_SOURCEMAP_OPTION_KEY]
26 | return opt
27 | end
28 | end
29 |
30 | # parse
31 | # parsing API Blueprint into Ruby objects
32 | # @param raw_blueprint [String] API Blueprint
33 | # @param options [Number] Parsing Options
34 | #
35 | # @return [ParseResult]
36 | def self.parse(raw_blueprint, options = 0)
37 | fail ArgumentError, 'Expected string value' unless raw_blueprint.is_a?(String)
38 |
39 | blueprint_options = parse_options(options)
40 |
41 | parse_result = FFI::MemoryPointer.new :pointer
42 |
43 | RedSnow::Binding.drafter_c_parse(raw_blueprint, blueprint_options, parse_result)
44 |
45 | parse_result = parse_result.get_pointer(0)
46 |
47 | ParseResult.new(parse_result.null? ? nil : parse_result.read_string)
48 | ensure
49 |
50 | RedSnow::Memory.free(parse_result)
51 | end
52 | end
53 |
--------------------------------------------------------------------------------
/lib/redsnow/binding.rb:
--------------------------------------------------------------------------------
1 | require 'ffi'
2 |
3 | module RedSnow
4 | # expose function free() to allow release memory allocated by C-interface
5 | module Memory
6 | extend FFI::Library
7 | ffi_lib FFI::Library::LIBC
8 |
9 | attach_function :free, [:pointer], :void
10 | end
11 |
12 | # C-binding with Snow Crash Library using [FFI](https://github.com/ffi/ffi)
13 | # @see https://github.com/apiaryio/drafter/blob/master/src/cdrafter.h
14 | module Binding
15 | extend FFI::Library
16 |
17 | prefix = FFI::Platform.mac? ? '' : 'lib.target/'
18 |
19 | ffi_lib File.expand_path("../../../ext/drafter/build/out/Release/#{prefix}libdrafter.#{FFI::Platform::LIBSUFFIX}", __FILE__)
20 | enum :option, [
21 | :render_descriptions_option,
22 | :require_blueprint_name_option,
23 | :export_sourcemap_option
24 | ]
25 |
26 | attach_function('drafter_c_parse', 'drafter_c_parse', [:string, :option, :pointer], :int)
27 | end
28 | end
29 |
--------------------------------------------------------------------------------
/lib/redsnow/blueprint.rb:
--------------------------------------------------------------------------------
1 | require 'redsnow/object'
2 |
3 | # The classes in this module should be 1:1 with the Snow Crash AST
4 | # counterparts (https://github.com/apiaryio/snowcrash/blob/master/src/Blueprint.h).
5 | module RedSnow
6 | # Blueprint AST node
7 | # Base class for API Blueprint AST nodes
8 | #
9 | # @abstract
10 | class BlueprintNode
11 | end
12 |
13 | # Blueprint AST Reference node
14 | #
15 | # @attr id [String] identifier of the reference
16 | #
17 | # @abstract
18 | class ReferenceNode < BlueprintNode
19 | attr_accessor :id
20 |
21 | def initialize(id)
22 | @id = id
23 | end
24 | end
25 |
26 | # Blueprint AST node with name && description associated
27 | #
28 | # @attr name [String] name of the node
29 | # @attr description [String] description of the node
30 | #
31 | # @abstract
32 | class NamedBlueprintNode < BlueprintNode
33 | attr_accessor :name
34 | attr_accessor :description
35 |
36 | # Ensure the input string buffer ends with two newlines.
37 | #
38 | # @param buffer [String] a buffer to check
39 | # If the buffer does not ends with two newlines the newlines are added.
40 | def ensure_description_newlines(buffer)
41 | return if description.empty?
42 |
43 | if description[-1, 1] != '\n'
44 | buffer << '\n\n'
45 | elsif description.length > 1 && description[-2, 1] != '\n'
46 | buffer << '\n'
47 | end
48 | end
49 | end
50 |
51 | # Blueprint AST node for key-value collections
52 | #
53 | # @abstract
54 | # @attr collection [Array] array of key value hashes
55 | class KeyValueCollection < BlueprintNode
56 | attr_accessor :collection
57 |
58 | # Retrieves the value of the collection item by its key
59 | #
60 | # @param key [String] Name of the item key to retrieve
61 | # @return [NilClass] if the collection does not have an item with the key
62 | # @return [String] if the collection has an item with the key
63 | def [](key)
64 | return nil if @collection.nil?
65 | return_item_value key
66 | end
67 | # Filter collection keys
68 | #
69 | # @return [Array] collection without ignored keys
70 | def filter_collection(ignore_keys)
71 | return @collection if ignore_keys.blank?
72 | @collection.select { |kv_item| !ignore_keys.include?(kv_item.keys.first) }
73 | end
74 |
75 | private
76 |
77 | def return_item_value(key)
78 | item = get_item(key.to_s)
79 | item && item[:value]
80 | end
81 |
82 | def get_item(key)
83 | @collection.find { |item| item[:name].downcase == key.downcase }
84 | end
85 | end
86 |
87 | # Metadata collection Blueprint AST node
88 | # represents 'metadata section'
89 | class Metadata < KeyValueCollection
90 | # @param metadata [json]
91 | def initialize(metadata)
92 | return if metadata.nil?
93 |
94 | @collection = []
95 | metadata.each do |item|
96 | @collection << Hash[name: item['name'], value: item.fetch('value', nil)]
97 | end
98 | end
99 | end
100 |
101 | # Headers collection Blueprint AST node
102 | # represents 'headers section'
103 | class Headers < KeyValueCollection
104 | # HTTP 'Content-Type' header
105 | CONTENT_TYPE_HEADER_KEY = :'Content-Type'
106 |
107 | # @return [String] the value of 'Content-type' header if present or nil
108 | def content_type
109 | content_type_header = @collection.find { |header| header.key?(CONTENT_TYPE_HEADER_KEY) }
110 | (content_type_header.nil?) ? nil : content_type_header[CONTENT_TYPE_HEADER_KEY]
111 | end
112 |
113 | # @param headers [json]
114 | def initialize(headers)
115 | @collection = []
116 |
117 | return if headers.nil?
118 |
119 | headers.each do |item|
120 | @collection << Hash[name: item['name'], value: item['value']]
121 | end
122 | end
123 | end
124 |
125 | # URI parameter Blueprint AST node
126 | # represents one 'parameters section' parameter
127 | #
128 | # @attr type [String] an arbitrary type of the parameter or nil
129 | # @attr use [Symbol] parameter necessity flag, `:required`, `:optional` or :undefined
130 | # Where `:undefined` implies `:required` according to the API Blueprint Specification
131 | # @attr default_value [String] default value of the parameter or nil
132 | # This is a value used when the parameter is ommited in the request.
133 | # @attr example_value [String] example value of the parameter or nil
134 | # @attr values [Array] an enumeration of possible parameter values
135 | class Parameter < NamedBlueprintNode
136 | attr_accessor :type
137 | attr_accessor :use
138 | attr_accessor :default_value
139 | attr_accessor :example_value
140 | attr_accessor :values
141 |
142 | # @param parameter [json]
143 | def initialize(parameter)
144 | @name = parameter.fetch('name', '')
145 | @description = parameter.fetch('description', '')
146 | @type = parameter.fetch('type', '')
147 |
148 | case parameter['required']
149 | when true
150 | @use = :required
151 | when false
152 | @use = :optional
153 | else
154 | @use = :undefined
155 | end
156 |
157 | @default_value = parameter.fetch('default', nil)
158 | @example_value = parameter.fetch('example', nil)
159 |
160 | @values = []
161 | parameter.key?('values') && parameter['values'].each do |value|
162 | @values << value['value']
163 | end
164 | end
165 | end
166 |
167 | # Collection of URI parameters Blueprint AST node
168 | # represents 'parameters section'
169 | #
170 | # @attr collection [Array] an array of URI parameters
171 | class Parameters < BlueprintNode
172 | attr_accessor :collection
173 |
174 | # @param parameters [json]
175 | def initialize(parameters)
176 | @collection = []
177 |
178 | return if parameters.nil?
179 |
180 | parameters.each do |item|
181 | @collection << Parameter.new(item)
182 | end
183 | end
184 | end
185 |
186 | # HTTP message payload Blueprint AST node
187 | # base class for 'payload sections'
188 | #
189 | # @abstract
190 | # @attr parameters [Array] ignored
191 | # @attr headers [Headers] array of HTTP header fields of the message or nil
192 | # @attr body [String] HTTP-message body or nil
193 | # @attr schema [String] HTTP-message body validation schema or nil
194 | # @attr reference [Hash] Symbol Reference if the payload is a reference
195 | class Payload < NamedBlueprintNode
196 | attr_accessor :headers
197 | attr_accessor :body
198 | attr_accessor :schema
199 | attr_accessor :reference
200 |
201 | # @param payload [json]
202 | def initialize(payload)
203 | @name = payload.fetch('name', '')
204 | @description = payload.fetch('description', '')
205 | @body = payload.fetch('body', '')
206 | @schema = payload.fetch('schema', '')
207 |
208 | if payload.key?('reference') && payload['reference'].key?('id')
209 | @reference = ReferenceNode.new(payload['reference']['id'])
210 | end
211 |
212 | @headers = Headers.new(payload.fetch('headers', nil))
213 | end
214 | end
215 |
216 | # Transaction example Blueprint AST node
217 | #
218 | # @attr requests [Array] example request payloads
219 | # @attr response [Array] example response payloads
220 | class TransactionExample < NamedBlueprintNode
221 | attr_accessor :requests
222 | attr_accessor :responses
223 |
224 | # @param example [json]
225 | def initialize(example)
226 | @name = example.fetch('name', '')
227 | @description = example.fetch('description', '')
228 |
229 | @requests = []
230 | example.key?('requests') && example['requests'].each do |request|
231 | @requests << Payload.new(request).tap do |inst|
232 | example_instance = self
233 | inst.define_singleton_method(:example) { example_instance }
234 | end
235 | end
236 |
237 | @responses = []
238 | example.key?('responses') && example['responses'].each do |response|
239 | @responses << Payload.new(response).tap do |inst|
240 | example_instance = self
241 | inst.define_singleton_method(:example) { example_instance }
242 | end
243 | end
244 | end
245 | end
246 |
247 | # Action Blueprint AST node
248 | # represetns 'action sction'
249 | #
250 | # @attr method [String] HTTP request method or nil
251 | # @attr parameters [Parameters] action-specific URI parameters or nil
252 | # @attr examples [Array] action transaction examples
253 | # @attr relation [String] action relation attribute
254 | # @attr uri_template [String] action uri template attribute
255 | class Action < NamedBlueprintNode
256 | attr_accessor :method
257 | attr_accessor :parameters
258 | attr_accessor :examples
259 | attr_accessor :relation
260 | attr_accessor :uri_template
261 |
262 | # @param action [json]
263 | def initialize(action)
264 | @name = action.fetch('name', '')
265 | @description = action.fetch('description', '')
266 |
267 | @method = action.fetch('method', '')
268 |
269 | @parameters = Parameters.new(action.fetch('parameters', nil))
270 |
271 | if action.key?('attributes')
272 | @relation = action['attributes'].fetch('relation', '')
273 | @uri_template = action['attributes'].fetch('uriTemplate', '')
274 | end
275 |
276 | @examples = []
277 | action.key?('examples') && action['examples'].each do |example|
278 | @examples << TransactionExample.new(example).tap do |inst|
279 | action_instance = self
280 | inst.define_singleton_method(:action) { action_instance }
281 | end
282 | end
283 | end
284 | end
285 |
286 | # Resource Blueprint AST node
287 | # represents 'resource section'
288 | #
289 | # @attr uri_template [String] RFC 6570 URI template
290 | # @attr model [Model] model payload for the resource or nil
291 | # @attr parameters [Parameters] action-specific URI parameters or nil
292 | # @attr actions [Array] array of resource actions or nil
293 | class Resource < NamedBlueprintNode
294 | attr_accessor :uri_template
295 | attr_accessor :model
296 | attr_accessor :parameters
297 | attr_accessor :actions
298 |
299 | # @param resource [json]
300 | def initialize(resource)
301 | @name = resource.fetch('name', '')
302 | @description = resource.fetch('description', '')
303 | @uri_template = resource.fetch('uriTemplate', '')
304 |
305 | @model = Payload.new(resource.fetch('model', nil))
306 |
307 | @parameters = Parameters.new(resource.fetch('parameters', nil))
308 |
309 | @actions = []
310 | resource.key?('actions') && resource['actions'].each do |action|
311 | @actions << Action.new(action).tap do |inst|
312 | resource_instance = self
313 | inst.define_singleton_method(:resource) { resource_instance }
314 | end
315 | end
316 | end
317 | end
318 |
319 | # Resource group Blueprint AST node
320 | # represents 'resource group section'
321 | #
322 | # @attr resources [Array] array of resources in the group
323 | class ResourceGroup < NamedBlueprintNode
324 | attr_accessor :resources
325 |
326 | # @param resource_group [json]
327 | def initialize(resource_group)
328 | @name = resource_group.fetch('name', '')
329 | @description = resource_group.fetch('description', '')
330 |
331 | @resources = []
332 | resource_group.key?('resources') && resource_group['resources'].each do |resource|
333 | @resources << Resource.new(resource).tap do |inst|
334 | resource_group_instance = self
335 | inst.define_singleton_method(:resource_group) { resource_group_instance }
336 | end
337 | end
338 | end
339 | end
340 |
341 | # Top-level Blueprint AST node
342 | # represents 'blueprint section'
343 | #
344 | # @attr metadata [Metadata] tool-specific metadata collection or nil
345 | # @attr resource_groups [Array] array of blueprint resource groups
346 | class Blueprint < NamedBlueprintNode
347 | attr_accessor :metadata
348 | attr_accessor :resource_groups
349 |
350 | # Version key
351 | VERSION_KEY = :_version
352 |
353 | # Supported version of Api Blueprint
354 | SUPPORTED_VERSIONS = ['3.0']
355 |
356 | # @param ast [json]
357 | def initialize(ast)
358 | @name = ast.fetch('name', '')
359 | @description = ast.fetch('description', '')
360 | @metadata = Metadata.new(ast.fetch('metadata', nil))
361 |
362 | @resource_groups = []
363 | ast.key?('resourceGroups') && ast['resourceGroups'].each do |resource_group|
364 | @resource_groups << ResourceGroup.new(resource_group)
365 | end
366 | end
367 | end
368 | end
369 |
--------------------------------------------------------------------------------
/lib/redsnow/object.rb:
--------------------------------------------------------------------------------
1 | # Essential extensions to base object class
2 | module RedSnow
3 | # Class from MatterCompiler as ascendant
4 | class Object
5 | # Symbolizes keys of a hash
6 | def deep_symbolize_keys
7 | return each_with_object({}) { |memo, (k, v)| memo[k.to_sym] = v.deep_symbolize_keys } if self.is_a?(Hash)
8 | return each_with_object([]) { |memo, v| memo << v.deep_symbolize_keys } if self.is_a?(Array)
9 | self
10 | end
11 |
12 | # Returns true if object is nil or empty, false otherwise
13 | def blank?
14 | respond_to?(:empty?) ? empty? : !self
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/redsnow/parseresult.rb:
--------------------------------------------------------------------------------
1 | require 'json'
2 |
3 | module RedSnow
4 | # Parse Result
5 | # @see https://github.com/apiaryio/api-blueprint-ast/blob/master/Parse%20Result.md
6 | # @param ast [Blueprint]
7 | # @param error [Hash] Description of a parsing error as occurred during parsing. If this field is present && code different from 0 then the content of ast field should be ignored.
8 | # @param warnings [Array] Ordered array of parser warnings as occurred during the parsing.
9 | # @param sourcemap [BlueprintSourcemap]
10 | class ParseResult
11 | attr_accessor :ast
12 | attr_accessor :error
13 | attr_accessor :warnings
14 | attr_accessor :sourcemap
15 |
16 | # Version key
17 | VERSION_KEY = :_version
18 |
19 | # Supported version of Api Blueprint
20 | SUPPORTED_VERSIONS = ['2.1']
21 |
22 | # @param parse_result [ string or nil ]
23 | def initialize(parse_result)
24 | parse_result = JSON.parse(parse_result)
25 |
26 | @ast = Blueprint.new(parse_result['ast'])
27 | @sourcemap = RedSnow::Sourcemap::Blueprint.new(parse_result['sourcemap'])
28 |
29 | @warnings = []
30 | parse_result.key?('warnings') && parse_result['warnings'].each do |warning|
31 | @warnings << source_annotation(warning)
32 | end
33 |
34 | @error = source_annotation(parse_result['error'])
35 | end
36 |
37 | protected
38 |
39 | def source_annotation(json)
40 | annotation = {}
41 |
42 | annotation[:message] = json['message']
43 | annotation[:code] = json['code']
44 | annotation[:ok] = json.fetch('code', 0)
45 |
46 | annotation[:location] = []
47 | json.key?('location') && json['location'].each do |location|
48 | annotation[:location] << Location.new(location)
49 | end
50 |
51 | annotation
52 | end
53 | end
54 |
55 | # Array of possibly non-continuous blocks of the source API Blueprint.
56 | # @param index [Number] Zero-based index of the character where warning has occurred.
57 | # @param length [Number] Number of the characters from index where warning has occurred.
58 | class Location
59 | attr_accessor :index
60 | attr_accessor :length
61 |
62 | # @param location [json]
63 | def initialize(location)
64 | @length = location['length']
65 | @index = location['index']
66 | end
67 | end
68 |
69 | # Warnning Codes
70 | # @see https://github.com/apiaryio/snowcrash/blob/master/src/SourceAnnotation.h#L128
71 | class WarningCodes
72 | NO_WARNING = 0
73 | API_NAME_WARNING = 1
74 | DUPLICATE_WARNING = 2
75 | FORMATTING_WARNING = 3
76 | REDEFINITION_WARNING = 4
77 | IGNORING_WARNING = 5
78 | EMPTY_DEFINITION_WARNING = 6
79 | NOT_EMPTY_DEFINITION_WARNING = 7
80 | LOGICAL_ERROR_WARNING = 8
81 | DEPRECATED_WARNING = 9
82 | INDENTATION_WARNING = 10
83 | AMBIGUITY_WARNING = 11
84 | URI_WARNING = 12
85 | end
86 | # Error Codes
87 | # @see https://github.com/apiaryio/snowcrash/blob/master/src/SourceAnnotation.h#L113
88 | class ErrorCodes
89 | NO_ERROR = 0
90 | APPLICATION_ERROR = 1
91 | BUSINESS_ERROR = 2
92 | SYMBOL_ERROR = 3
93 | end
94 | end
95 |
--------------------------------------------------------------------------------
/lib/redsnow/sourcemap.rb:
--------------------------------------------------------------------------------
1 | require 'redsnow/object'
2 |
3 | # The classes in this module should be 1:1 with the Snow Crash Blueprint sourcemap
4 | # counterparts (https://github.com/apiaryio/snowcrash/blob/master/src/BlueprintSourcemap.h).
5 | module RedSnow
6 | module Sourcemap
7 | # Base Node - holds @collection
8 | class Node
9 | attr_accessor :collection
10 |
11 | # @param sourcemap [json array or nil]
12 | def initialize(sourcemap)
13 | @collection = []
14 |
15 | return if sourcemap.nil?
16 |
17 | sourcemap.each do |sm|
18 | @collection << SourceMap.new(sm)
19 | end
20 | end
21 | end
22 |
23 | # SourceMap
24 | class SourceMap < Array
25 | # @param sourcemap [json array or nil]
26 | def initialize(sourcemap)
27 | return if sourcemap.nil?
28 |
29 | sourcemap.each do |position|
30 | self << position
31 | end
32 | end
33 | end
34 |
35 | # Blueprint sourcemap node with name && description associated
36 | #
37 | # @attr name [SourceMap] name of the node
38 | # @attr description [SourceMap] description of the node
39 | #
40 | # @abstract
41 | class NamedNode < Node
42 | attr_accessor :name
43 | attr_accessor :description
44 |
45 | # @param sourcemap [json array or nil]
46 | def initialize(sourcemap)
47 | return if sourcemap.nil?
48 |
49 | @name = SourceMap.new(sourcemap['name'])
50 | @description = SourceMap.new(sourcemap['description'])
51 | end
52 | end
53 |
54 | # Metadata source map collection node
55 | class Metadata < Node
56 | # @param sourcemap [json]
57 | def initialize(sourcemap)
58 | super(sourcemap)
59 | end
60 | end
61 |
62 | # Headers source map collection node
63 | class Headers < Node
64 | # @param sourcemap [json]
65 | def initialize(sourcemap)
66 | super(sourcemap)
67 | end
68 | end
69 |
70 | # Parameter source map node
71 | #
72 | # @attr type [Sourcemap] an arbitrary type of the parameter or nil
73 | # @attr use [Sourcemap] parameter necessity flag, `:required` or `:optional`
74 | # @attr default_value [Sourcemap] default value of the parameter or nil
75 | # @attr example_value [Sourcemap] example value of the parameter or nil
76 | # @attr values [Array] an enumeration of possible parameter values
77 | class Parameter < NamedNode
78 | attr_accessor :type
79 | attr_accessor :use
80 | attr_accessor :default_value
81 | attr_accessor :example_value
82 | attr_accessor :values
83 |
84 | # @param sourcemap [json]
85 | def initialize(sourcemap)
86 | super(sourcemap)
87 |
88 | @type = SourceMap.new(sourcemap['type'])
89 | @use = SourceMap.new(sourcemap['required'])
90 | @default_value = SourceMap.new(sourcemap['default'])
91 | @example_value = SourceMap.new(sourcemap['example'])
92 | @values = []
93 |
94 | sourcemap.key?('values') && sourcemap['values'].each do |value|
95 | @values << SourceMap.new(value['value'])
96 | end
97 | end
98 | end
99 |
100 | # Parameters source map collection node
101 | #
102 | # @attr collection [Array] an array of URI parameters
103 | class Parameters < Node
104 | # @param sourcemap [json]
105 | def initialize(sourcemap)
106 | @collection = []
107 |
108 | return if sourcemap.nil?
109 |
110 | sourcemap.each do |parameter|
111 | @collection << Parameter.new(parameter)
112 | end
113 | end
114 | end
115 |
116 | # Payload source map node
117 | #
118 | # @abstract
119 | # @attr parameters [Parameters] ignored
120 | # @attr headers [Headers] array of HTTP header fields of the message or nil
121 | # @attr body [Sourcemap] HTTP-message body or nil
122 | # @attr schema [Sourcemap] HTTP-message body validation schema or nil
123 | # @attr reference [Sourcemap] Symbol Reference sourcemap if the payload is a reference
124 | class Payload < NamedNode
125 | attr_accessor :headers
126 | attr_accessor :body
127 | attr_accessor :schema
128 | attr_accessor :reference
129 |
130 | # @param sourcemap [json]
131 | def initialize(sourcemap)
132 | return if sourcemap.nil?
133 |
134 | super(sourcemap)
135 |
136 | @body = SourceMap.new(sourcemap['body'])
137 | @schema = SourceMap.new(sourcemap['schema'])
138 | @reference = SourceMap.new(sourcemap['reference'])
139 | @headers = Headers.new(sourcemap['headers'])
140 | end
141 | end
142 |
143 | # Transaction example source map node
144 | #
145 | # @attr requests [Array] example request payloads
146 | # @attr response [Array] example response payloads
147 | class TransactionExample < NamedNode
148 | attr_accessor :requests
149 | attr_accessor :responses
150 |
151 | # @param sourcemap [json]
152 | def initialize(sourcemap)
153 | super(sourcemap)
154 |
155 | @requests = []
156 | sourcemap.key?('requests') && sourcemap['requests'].each do |request|
157 | @requests << Payload.new(request)
158 | end
159 |
160 | @responses = []
161 | sourcemap.key?('responses') && sourcemap['responses'].each do |response|
162 | @responses << Payload.new(response)
163 | end
164 | end
165 | end
166 |
167 | # Action source map node
168 | #
169 | # @attr method [Sourcemap] HTTP request method or nil
170 | # @attr parameters [Parameters] action-specific URI parameters or nil
171 | # @attr examples [Array] action transaction examples
172 | class Action < NamedNode
173 | attr_accessor :method
174 | attr_accessor :parameters
175 | attr_accessor :examples
176 |
177 | # @param sourcemap [json]
178 | def initialize(sourcemap)
179 | return if sourcemap.nil?
180 |
181 | super(sourcemap)
182 |
183 | @method = SourceMap.new(sourcemap['method'])
184 | @parameters = Parameters.new(sourcemap['parameters'])
185 |
186 | @examples = []
187 | sourcemap.key?('examples') && sourcemap['examples'].each do |example|
188 | @examples << TransactionExample.new(example)
189 | end
190 | end
191 | end
192 |
193 | # Resource source map node
194 | #
195 | # @attr uri_template [Sourcemap] RFC 6570 URI template
196 | # @attr model [Payload] model payload for the resource or nil
197 | # @attr parameters [Parameters] action-specific URI parameters or nil
198 | # @attr actions [Array] array of resource actions or nil
199 | class Resource < NamedNode
200 | attr_accessor :uri_template
201 | attr_accessor :model
202 | attr_accessor :parameters
203 | attr_accessor :actions
204 |
205 | # @param sourcemap [json]
206 | def initialize(sourcemap)
207 | return if sourcemap.nil?
208 |
209 | super(sourcemap)
210 | @uri_template = SourceMap.new(sourcemap['uriTemplate'])
211 | @model = Payload.new(sourcemap['model'])
212 | @parameters = Parameters.new(sourcemap['parameters'])
213 |
214 | @actions = []
215 | sourcemap.key?('actions') && sourcemap['actions'].each do |action|
216 | @actions << Action.new(action)
217 | end
218 | end
219 | end
220 |
221 | # Resource group source map node
222 | #
223 | # @attr resources [Array] array of resources in the group
224 | class ResourceGroup < NamedNode
225 | attr_accessor :resources
226 |
227 | # @param sourcemap [json]
228 | def initialize(sourcemap)
229 | super(sourcemap)
230 |
231 | @resources = []
232 | sourcemap.key?('resources') && sourcemap['resources'].each do |resource|
233 | @resources << Resource.new(resource)
234 | end
235 | end
236 | end
237 |
238 | # Blueprint source map node
239 | #
240 | # @attr metadata [Metadata] tool-specific metadata collection or nil
241 | # @attr resource_groups [Array] array of resource groups
242 | class Blueprint < NamedNode
243 | attr_accessor :metadata
244 | attr_accessor :resource_groups
245 |
246 | # @param sourcemap [json]
247 | def initialize(sourcemap)
248 | return if sourcemap.nil?
249 |
250 | super(sourcemap)
251 |
252 | @metadata = Metadata.new(sourcemap['metadata'])
253 | @resource_groups = []
254 |
255 | sourcemap.key?('resourceGroups') && sourcemap['resourceGroups'].each do |resource_group|
256 | @resource_groups << ResourceGroup.new(resource_group)
257 | end
258 | end
259 | end
260 | end
261 | end
262 |
--------------------------------------------------------------------------------
/lib/redsnow/version.rb:
--------------------------------------------------------------------------------
1 | # Module RedSnow
2 | module RedSnow
3 | # Gem version
4 | VERSION = '0.4.4'
5 | end
6 |
--------------------------------------------------------------------------------
/provisioning.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # GCC 4.7
4 | sudo apt-get update -y
5 | sudo apt-get install -y python-software-properties
6 | sudo apt-get update -y
7 | sudo apt-get install -y build-essential git-core curl mc vim
8 |
9 | # Ruby
10 | gem install bundler
11 | # RVM gpg key
12 | gpg --keyserver hkp://keys.gnupg.net --recv-keys D39DC0E3
13 | # RVM Install
14 | curl -sSL https://get.rvm.io | bash -s stable
15 | source /home/vagrant/.rvm/scripts/rvm
16 | # Installing required packages: libreadline6-dev, zlib1g-dev, libssl-dev, libyaml-dev, libsqlite3-dev, sqlite3, autoconf, libgdbm-dev, libncurses5-dev, automake, libtool, bison, pkg-config, libffi-dev
17 | rvm install 2.1.5
18 | rvm ruby-2.1.5
19 | gem install bundler
20 |
--------------------------------------------------------------------------------
/redsnow.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'redsnow/version'
5 |
6 | Gem::Specification.new do |gem|
7 | gem.name = 'redsnow'
8 | gem.version = RedSnow::VERSION
9 | gem.authors = ['Ladislav Prskavec']
10 | gem.email = ['ladislav@apiary.io']
11 | gem.description = 'Ruby bindings for Snow Crash'
12 | gem.summary = 'Ruby bindings for Snow Crash'
13 | gem.homepage = 'https://github.com/apiaryio/redsnow'
14 | gem.license = 'MIT'
15 | gem.files = Dir['lib/**/*']
16 | gem.files << Dir['*']
17 | gem.files << Dir['ext/drafter/**/*'].reject { |f| f =~ /cmdline|test|features|README*|LICENSE|Gemfile*|\.xcode*/ }
18 | gem.executables = gem.files.grep(/^bin/).map { |f| File.basename(f) }
19 | gem.test_files = gem.files.grep(/^(test|spec|features)/)
20 | gem.require_paths = %w(lib ext)
21 |
22 | gem.required_ruby_version = '>= 1.9.3'
23 |
24 | gem.extensions = %w(Rakefile)
25 |
26 | gem.add_dependency 'ffi', '~> 1.9.3'
27 | gem.add_dependency 'rake', '>= 10.3.2'
28 | gem.add_dependency 'bundler', '>= 1.7.0'
29 | gem.add_dependency 'yard', '~> 0.9.5'
30 |
31 | gem.add_development_dependency 'minitest'
32 | gem.add_development_dependency 'shoulda'
33 | gem.add_development_dependency 'mocha'
34 | gem.add_development_dependency 'unindent'
35 | gem.add_development_dependency 'rubocop'
36 | gem.add_development_dependency 'guard-rubocop'
37 | end
38 |
--------------------------------------------------------------------------------
/test/.rubocop.yml:
--------------------------------------------------------------------------------
1 | inherit_from: ../.rubocop.yml
2 |
3 | Lint/AmbiguousRegexpLiteral:
4 | Enabled: false
5 |
6 | # The documentation check doesn't make sense for test code
7 | Style/Documentation:
8 | Enabled: false
9 |
10 | # Configuration parameters: CountComments.
11 | Metrics/ClassLength:
12 | Max: 300
13 |
--------------------------------------------------------------------------------
/test/_helper.rb:
--------------------------------------------------------------------------------
1 | require 'bundler/setup'
2 |
3 | require 'minitest/autorun'
4 | require 'shoulda'
5 | require 'mocha'
6 | require File.join(File.expand_path('../../lib/redsnow.rb', __FILE__))
7 | # Test Helper
8 | class TestHelper < Minitest::Test
9 | def fixture_file(name)
10 | File.read File.expand_path("../fixtures/#{name}", __FILE__)
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/test/example.rb:
--------------------------------------------------------------------------------
1 | require 'redsnow'
2 |
3 | result = RedSnow.parse('# My API', exportSourcemap: true)
4 | puts result.ast.name
5 | puts result.sourcemap.name
6 |
--------------------------------------------------------------------------------
/test/fixtures/sample-api-ast.json:
--------------------------------------------------------------------------------
1 | {
2 | "_version": "3.0",
3 | "metadata": [
4 | {
5 | "name": "FORMAT",
6 | "value": "1A"
7 | }
8 | ],
9 | "name": "",
10 | "description": "",
11 | "resourceGroups": [
12 | {
13 | "name": "Messages",
14 | "description": "",
15 | "resources": [
16 | {
17 | "name": "Message",
18 | "description": "",
19 | "uriTemplate": "/messages/{id}",
20 | "model": {},
21 | "parameters": [
22 | {
23 | "name": "id",
24 | "description": "Id of a message",
25 | "type": "",
26 | "required": true,
27 | "default": "",
28 | "example": "",
29 | "values": [
30 | {
31 | "value": "1"
32 | },
33 | {
34 | "value": "2"
35 | },
36 | {
37 | "value": "3"
38 | }
39 | ]
40 | }
41 | ],
42 | "actions": [
43 | {
44 | "name": "Retrieve Message",
45 | "description": "",
46 | "method": "GET",
47 | "parameters": [],
48 | "examples": [
49 | {
50 | "name": "",
51 | "description": "",
52 | "requests": [],
53 | "responses": [
54 | {
55 | "name": "200",
56 | "description": "",
57 | "headers": [
58 | {
59 | "name": "Content-Type",
60 | "value": "text/plain"
61 | }
62 | ],
63 | "body": "Hello World!\n",
64 | "schema": ""
65 | }
66 | ]
67 | }
68 | ]
69 | },
70 | {
71 | "name": "Delete Message",
72 | "description": "",
73 | "method": "DELETE",
74 | "parameters": [],
75 | "examples": [
76 | {
77 | "name": "",
78 | "description": "",
79 | "requests": [],
80 | "responses": [
81 | {
82 | "name": "204",
83 | "description": "",
84 | "headers": [],
85 | "body": "",
86 | "schema": ""
87 | }
88 | ]
89 | }
90 | ]
91 | }
92 | ]
93 | }
94 | ]
95 | }
96 | ]
97 | }
98 |
--------------------------------------------------------------------------------
/test/fixtures/sample-api-sourcemap.json:
--------------------------------------------------------------------------------
1 | {
2 | "metadata": [
3 | [
4 | [
5 | 0,
6 | 12
7 | ]
8 | ]
9 | ],
10 | "name": [],
11 | "description": [],
12 | "resourceGroups": [
13 | {
14 | "name": [
15 | [
16 | 12,
17 | 18
18 | ]
19 | ],
20 | "description": [],
21 | "resources": [
22 | {
23 | "name": [
24 | [
25 | 30,
26 | 28
27 | ]
28 | ],
29 | "description": [],
30 | "uriTemplate": [
31 | [
32 | 30,
33 | 28
34 | ]
35 | ],
36 | "model": {},
37 | "parameters": [
38 | {
39 | "name": [
40 | [
41 | 77,
42 | 23
43 | ]
44 | ],
45 | "description": [
46 | [
47 | 77,
48 | 23
49 | ]
50 | ],
51 | "type": [],
52 | "required": [],
53 | "default": [],
54 | "example": [],
55 | "values": [
56 | [
57 | [
58 | 129,
59 | 6
60 | ]
61 | ],
62 | [
63 | [
64 | 147,
65 | 6
66 | ]
67 | ],
68 | [
69 | [
70 | 165,
71 | 6
72 | ]
73 | ]
74 | ]
75 | }
76 | ],
77 | "actions": [
78 | {
79 | "name": [
80 | [
81 | 172,
82 | 26
83 | ]
84 | ],
85 | "description": [],
86 | "method": [
87 | [
88 | 172,
89 | 26
90 | ]
91 | ],
92 | "parameters": [],
93 | "examples": [
94 | {
95 | "name": [],
96 | "description": [],
97 | "requests": [],
98 | "responses": [
99 | {
100 | "name": [
101 | [
102 | 200,
103 | 27
104 | ]
105 | ],
106 | "description": [],
107 | "headers": [
108 | [
109 | [
110 | 200,
111 | 27
112 | ]
113 | ]
114 | ],
115 | "body": [
116 | [
117 | 231,
118 | 17
119 | ]
120 | ],
121 | "schema": []
122 | }
123 | ]
124 | }
125 | ]
126 | },
127 | {
128 | "name": [
129 | [
130 | 249,
131 | 27
132 | ]
133 | ],
134 | "description": [],
135 | "method": [
136 | [
137 | 249,
138 | 27
139 | ]
140 | ],
141 | "parameters": [],
142 | "examples": [
143 | {
144 | "name": [],
145 | "description": [],
146 | "requests": [],
147 | "responses": [
148 | {
149 | "name": [
150 | [
151 | 278,
152 | 13
153 | ]
154 | ],
155 | "description": [],
156 | "headers": [],
157 | "body": [],
158 | "schema": []
159 | }
160 | ]
161 | }
162 | ]
163 | }
164 | ]
165 | }
166 | ]
167 | }
168 | ]
169 | }
170 |
--------------------------------------------------------------------------------
/test/fixtures/sample-api.apib:
--------------------------------------------------------------------------------
1 | FORMAT: 1A
2 |
3 | # Group Messages
4 |
5 | # Message [/messages/{id}]
6 |
7 | + Parameters
8 | + id ... Id of a message
9 | + Values
10 | + `1`
11 | + `2`
12 | + `3`
13 |
14 | ## Retrieve Message [GET]
15 | + Response 200 (text/plain)
16 |
17 | Hello World!
18 |
19 | ## Delete Message [DELETE]
20 | + Response 204
21 |
--------------------------------------------------------------------------------
/test/redsnow_binding_test.rb:
--------------------------------------------------------------------------------
1 | require '_helper'
2 | require 'json'
3 | # RedSnowBindingTest
4 | class RedSnowBindingTest < Minitest::Test
5 | context 'RedSnow Binding' do
6 | should 'convert API Blueprint to AST' do
7 | parse_result = FFI::MemoryPointer.new :pointer
8 |
9 | RedSnow::Binding.drafter_c_parse("meta: data\nfoo:bar\n#XXXX\ndescription for it", 4, parse_result)
10 |
11 | parse_result = parse_result.get_pointer(0)
12 | assert !parse_result.null?
13 |
14 | parse_result_as_string = parse_result.null? ? nil : parse_result.read_string
15 | refute_nil parse_result_as_string
16 |
17 | parsed = JSON.parse(parse_result_as_string)
18 |
19 | assert_equal 'XXXX', parsed['ast']['name']
20 | assert_equal 'description for it', parsed['ast']['description']
21 | end
22 | end
23 | end
24 |
--------------------------------------------------------------------------------
/test/redsnow_options_test.rb:
--------------------------------------------------------------------------------
1 | require '_helper'
2 | # RedSnowOptionsTest
3 | class RedSnowOptionsTest < Minitest::Test
4 | context 'Test arguments' do
5 | context 'Arguments' do
6 | should "raise error if first parameter isn't String" do
7 | exception = assert_raises(ArgumentError) { RedSnow.parse(1) }
8 | assert_equal('Expected string value', exception.message)
9 | end
10 |
11 | should 'get option for sourcemaps' do
12 | options = RedSnow.parse_options(exportSourcemap: true)
13 | assert_equal 4, options
14 | end
15 |
16 | should 'get option for required Blueprint name' do
17 | options = RedSnow.parse_options(requireBlueprintName: true)
18 | assert_equal 2, options
19 | end
20 |
21 | should 'get option for required Blueprint name and sourcemaps' do
22 | options = RedSnow.parse_options(requireBlueprintName: true, exportSourcemap: true)
23 | assert_equal 6, options
24 | end
25 |
26 | should 'get option for required Blueprint name and not sourcemaps' do
27 | options = RedSnow.parse_options(requireBlueprintName: true, exportSourcemap: false)
28 | assert_equal 2, options
29 | end
30 |
31 | should 'no options' do
32 | options = RedSnow.parse_options(0)
33 | assert_equal 0, options
34 | end
35 | end
36 | end
37 | end
38 |
--------------------------------------------------------------------------------
/test/redsnow_parseresult_test.rb:
--------------------------------------------------------------------------------
1 | require '_helper'
2 | require 'unindent'
3 | # RedSnowParseResultTest
4 | class RedSnowParseResultTest < Minitest::Test
5 | context 'Simple API' do
6 | setup do
7 | @result = RedSnow.parse('# My API', 4)
8 | end
9 |
10 | should 'have name' do
11 | assert_equal 'My API', @result.ast.name
12 | end
13 |
14 | should "don't have error" do
15 | assert_equal 0, @result.error[:ok]
16 | end
17 | end
18 |
19 | context 'Simple API with warning' do
20 | setup do
21 | @source = <<-STR
22 | FORMAT: 1A
23 | # My API
24 | ## GET /
25 | STR
26 | @result = RedSnow.parse(@source.unindent, 4)
27 | end
28 |
29 | should 'have name' do
30 | assert_equal 'My API', @result.ast.name
31 | end
32 |
33 | should "don't have error" do
34 | assert_equal 0, @result.error[:code]
35 | end
36 |
37 | should 'have source map for api name' do
38 | assert_equal [[11, 9]], @result.sourcemap.name
39 | end
40 |
41 | should 'have some warning' do
42 | assert_equal RedSnow::WarningCodes::EMPTY_DEFINITION_WARNING, @result.warnings[0][:code]
43 | assert_equal 'action is missing a response', @result.warnings[0][:message]
44 |
45 | assert_equal 20, @result.warnings[0][:location][0].index
46 | assert_equal 9, @result.warnings[0][:location][0].length
47 |
48 | assert_equal "## GET /\n", @source.unindent[20..@source.unindent.length]
49 | # Line in blueprint
50 | assert_equal 3, @source.unindent[0..20].lines.count
51 | end
52 | end
53 | end
54 |
--------------------------------------------------------------------------------
/test/redsnow_sourcemap_test.rb:
--------------------------------------------------------------------------------
1 | require '_helper'
2 | require 'unindent'
3 | # RedSnowParsingTest
4 | class RedSnowSourcemapTest < Minitest::Test
5 | context 'API Blueprint parser' do
6 | context 'API' do
7 | setup do
8 | @result = RedSnow.parse('# My API', 4)
9 | end
10 |
11 | should 'have name' do
12 | assert_equal [[0, 8]], @result.sourcemap.name
13 | end
14 | end
15 |
16 | context 'API' do
17 | setup do
18 | source = <<-STR
19 | **description**
20 | STR
21 |
22 | @result = RedSnow.parse(source.unindent, 4)
23 | end
24 |
25 | should 'have description' do
26 | assert_equal [], @result.sourcemap.name
27 | assert_equal [[0, 16]], @result.sourcemap.description
28 | end
29 | end
30 |
31 | context 'Group' do
32 | setup do
33 | source = <<-STR
34 | # Group Name
35 | _description_
36 |
37 | ## My Resource [/resource]
38 | Resource description
39 |
40 | ## My Alternative Resource [/alternative_resource]
41 | Alternative resource description
42 |
43 | STR
44 |
45 | @result = RedSnow.parse(source.unindent, 4)
46 | @sourcemap = @result.sourcemap
47 | end
48 |
49 | should 'have resource group' do
50 | assert_equal 1, @sourcemap.resource_groups.count
51 | assert_equal 2, @sourcemap.resource_groups[0].resources.count
52 | end
53 |
54 | should 'have resource' do
55 | assert_equal [[28, 27]], @sourcemap.resource_groups[0].resources[0].name
56 | assert_equal [[28, 27]], @sourcemap.resource_groups[0].resources[0].uri_template
57 | end
58 |
59 | should 'have alternative resource' do
60 | assert_equal [[77, 51]], @sourcemap.resource_groups[0].resources[1].name
61 | end
62 | end
63 |
64 | context 'Resource' do
65 | setup do
66 | source = <<-STR
67 | # My Resource [/resource]
68 | Resource description
69 |
70 | + Model (text/plain)
71 |
72 | Hello World
73 |
74 | ## Retrieve Resource [GET]
75 | Method description
76 |
77 | + Response 200 (text/plain)
78 |
79 | Response description
80 |
81 | + Headers
82 |
83 | X-Response-Header: Fighter
84 |
85 | + Body
86 |
87 | Y.T.
88 |
89 | + Schema
90 |
91 | Kourier
92 |
93 | ## Delete Resource [DELETE]
94 |
95 | + Response 200
96 |
97 | [My Resource][]
98 |
99 | STR
100 |
101 | @result = RedSnow.parse(source.unindent, 4)
102 | @resource_group = @result.sourcemap.resource_groups[0]
103 | @resource = @resource_group.resources[0]
104 | end
105 |
106 | should 'have resource group' do
107 | assert_equal [], @resource_group.name
108 | assert_equal 1, @resource_group.resources.count
109 | end
110 |
111 | should 'have resource' do
112 | assert_equal [[0, 26]], @resource.uri_template
113 | assert_equal [[26, 22]], @resource.description
114 | end
115 |
116 | should 'have resource model' do
117 | assert_equal [[0, 26]], @resource.model.name
118 | assert_equal [[74, 16]], @resource.model.body
119 | assert_equal 1, @resource.model.headers.collection.count
120 | assert_equal [[50, 20]], @resource.model.headers.collection[0]
121 | end
122 |
123 | should 'have actions' do
124 | assert_equal 2, @resource.actions.count
125 | assert_equal [[91, 27]], @resource.actions[0].method
126 | assert_equal [[91, 27]], @resource.actions[0].name
127 | end
128 |
129 | should 'have reference' do
130 | assert_equal [[354, 16]], @resource.actions[1].examples[0].responses[0].reference
131 | end
132 | end
133 |
134 | context 'Action' do
135 | setup do
136 | source = <<-STR
137 | # My Resource [/resource]
138 | Resource description
139 |
140 | ## Retrieve Resource [GET]
141 | Method description
142 |
143 | + Parameters
144 | + limit = `20` (optional, number, `42`) ... This is a limit
145 |
146 | + Response 202
147 |
148 | + Response 203
149 |
150 | STR
151 |
152 | @result = RedSnow.parse(source.unindent, 4)
153 | @resource_group = @result.sourcemap.resource_groups[0]
154 | @resource = @resource_group.resources[0]
155 | @action = @resource.actions[0]
156 | @parameter = @action.parameters.collection.first
157 | end
158 |
159 | should 'have 1 example' do
160 | assert_equal 1, @action.examples.count
161 | end
162 |
163 | should 'have the right parameters' do
164 | assert_equal [[118, 58]], @parameter.name
165 | assert_equal [[118, 58]], @parameter.type
166 | assert_equal [[118, 58]], @parameter.use
167 | assert_equal [[118, 58]], @parameter.default_value
168 | assert_equal [[118, 58]], @parameter.example_value
169 | assert_equal 0, @parameter.values.count
170 | end
171 | end
172 |
173 | context 'parses resource parameters' do
174 | setup do
175 | source = <<-STR
176 | # /machine{?limit}
177 |
178 | + Parameters
179 | + limit = `20` (optional, number, `42`) ... This is a limit
180 | + Values
181 | + `20`
182 | + `42`
183 | + `53`
184 |
185 | ## GET
186 |
187 | + Response 204
188 | STR
189 |
190 | @result = RedSnow.parse(source.unindent, 4)
191 | @resource_group = @result.sourcemap.resource_groups[0]
192 | @resource = @resource_group.resources[0]
193 | @parameter = @resource.parameters.collection[0]
194 | @values = @parameter.values
195 | end
196 |
197 | should 'have parameters' do
198 | assert_equal [[39, 58]], @parameter.name
199 | assert_equal [[39, 58]], @parameter.description
200 | assert_equal [[39, 58]], @parameter.type
201 | assert_equal [[39, 58]], @parameter.use
202 | assert_equal [[39, 58]], @parameter.default_value
203 | assert_equal [[39, 58]], @parameter.example_value
204 | assert_equal 3, @parameter.values.count
205 |
206 | assert_equal [[122, 7]], @values[0]
207 | assert_equal [[139, 7]], @values[1]
208 | assert_equal [[156, 7]], @values[2]
209 | end
210 | end
211 |
212 | context 'parses multiple transactions' do
213 | setup do
214 | source = <<-STR
215 | ## Notes Collection [/notes?id={id}&testingGroup={testtingGroup}&collection={collection}]
216 | ### Create a Note [POST]
217 | + Request Create a note
218 |
219 | + Headers
220 |
221 | Content-Type: application/json
222 | Prefer: creating
223 |
224 | + Body
225 |
226 | { "title": "Buy cheese and bread for breakfast." }
227 |
228 | + Response 201 (application/json)
229 |
230 | { "id": 3, "title": "Buy cheese and bread for breakfast." }
231 |
232 | + Request Unable to create note
233 |
234 | + Headers
235 |
236 | Content-Type: application/json
237 | Prefer: testing
238 |
239 |
240 | + Body
241 |
242 | { "ti": "Buy cheese and bread for breakfast." }
243 |
244 | + Response 500
245 |
246 | { "error": "can't create record" }
247 | STR
248 | @result = RedSnow.parse(source.unindent, 4)
249 | @resource_group = @result.sourcemap.resource_groups[0]
250 | @action = @resource_group.resources[0].actions[0]
251 | @examples = @action.examples
252 | end
253 |
254 | should 'have multiple requests and responses' do
255 | assert_equal 2, @examples.count
256 | assert_equal 1, @examples[0].requests.count
257 | assert_equal 1, @examples[0].responses.count
258 |
259 | assert_equal 2, @examples[0].requests[0].headers.collection.count
260 |
261 | assert_equal 1, @examples[1].requests.count
262 | assert_equal [[549, 52]], @examples[1].requests[0].body
263 | end
264 | end
265 | end
266 | end
267 |
--------------------------------------------------------------------------------
/test/redsnow_test.rb:
--------------------------------------------------------------------------------
1 | require '_helper'
2 | require 'unindent'
3 |
4 | # RedSnowParsingTest
5 | class RedSnowParsingAPITest < Minitest::Test
6 | # https://github.com/apiaryio/protagonist/blob/master/test/parser-test.coffee
7 | context 'API' do
8 | setup do
9 | @result = RedSnow.parse('# My API')
10 | end
11 |
12 | should 'have name' do
13 | assert_equal 'My API', @result.ast.name
14 | end
15 | end
16 |
17 | context 'API' do
18 | setup do
19 | source = <<-STR
20 | **description**
21 | STR
22 |
23 | @result = RedSnow.parse(source.unindent)
24 | end
25 |
26 | should 'have description' do
27 | assert_equal '', @result.ast.name
28 | assert_equal "**description**\n", @result.ast.description
29 | end
30 | end
31 | end
32 |
33 | class RedSnowParsingGroupTest < Minitest::Test
34 | context 'Group' do
35 | setup do
36 | source = <<-STR
37 | # Group Name
38 | _description_
39 |
40 | ## My Resource [/resource]
41 | Resource description
42 |
43 | ## My Alternative Resource [/alternative_resource]
44 | Alternative resource description
45 |
46 | STR
47 |
48 | @result = RedSnow.parse(source.unindent)
49 | @resource_group = @result.ast.resource_groups[0]
50 | @resource = @resource_group.resources[0]
51 | @alternative_resource = @resource_group.resources[1]
52 | end
53 |
54 | should 'have resource group' do
55 | assert_equal 1, @result.ast.resource_groups.count
56 | assert_equal 'Name', @resource_group.name
57 | assert_equal "_description_\n\n", @resource_group.description
58 | end
59 |
60 | should 'have resource' do
61 | assert_equal '/resource', @resource.uri_template
62 | assert_equal 'My Resource', @resource.name
63 | assert_equal "Resource description\n\n", @resource.description
64 | end
65 |
66 | should 'have alternative resource' do
67 | assert_equal '/alternative_resource', @alternative_resource.uri_template
68 | assert_equal 'My Alternative Resource', @alternative_resource.name
69 | assert_equal "Alternative resource description\n\n", @alternative_resource.description
70 | end
71 | end
72 | end
73 |
74 | class RedSnowParsingResourceTest < Minitest::Test
75 | context 'Resource' do
76 | setup do
77 | source = <<-STR
78 | # My Resource [/resource]
79 | Resource description
80 |
81 | + Model (text/plain)
82 |
83 | Hello World
84 |
85 | ## Retrieve Resource [GET]
86 | Method description
87 |
88 | + Response 200 (text/plain)
89 |
90 | Response description
91 |
92 | + Headers
93 |
94 | X-Response-Header: Fighter
95 |
96 | + Body
97 |
98 | Y.T.
99 |
100 | + Schema
101 |
102 | Kourier
103 |
104 | ## Delete Resource [DELETE]
105 |
106 | + Response 200
107 |
108 | [My Resource][]
109 |
110 | STR
111 |
112 | @result = RedSnow.parse(source.unindent)
113 | @resource_group = @result.ast.resource_groups[0]
114 | @resource = @resource_group.resources[0]
115 | @action = @resource.actions[0]
116 | @response = @resource.actions[1].examples[0].responses[0]
117 | end
118 |
119 | should 'have resource group' do
120 | assert_equal 1, @result.ast.resource_groups.count
121 | assert_equal '', @resource_group.name
122 | assert_equal '', @resource_group.description
123 | assert_equal 1, @resource_group.resources.count
124 | end
125 |
126 | should 'have resource' do
127 | assert_equal '/resource', @resource.uri_template
128 | assert_equal 'My Resource', @resource.name
129 | assert_equal "Resource description\n\n", @resource.description
130 | end
131 |
132 | should 'have resource model' do
133 | assert_equal 'My Resource', @resource.model.name
134 | assert_equal '', @resource.model.description
135 | assert_equal "Hello World\n", @resource.model.body
136 | assert_equal 1, @resource.model.headers.collection.count
137 | assert_equal 'Content-Type', @resource.model.headers.collection[0][:name]
138 | assert_equal 'text/plain', @resource.model.headers['content-type']
139 | assert_equal 'text/plain', @resource.model.headers['Content-Type']
140 | end
141 |
142 | should 'have a getter to its resource_group' do
143 | assert_equal @resource.resource_group, @resource_group
144 | end
145 |
146 | should 'have actions' do
147 | assert_equal 2, @resource.actions.count
148 | assert_equal 'GET', @action.method
149 | assert_equal 'Retrieve Resource', @action.name
150 | assert_equal "Method description\n\n", @action.description
151 | end
152 |
153 | should 'have reference' do
154 | assert_equal 'My Resource', @response.reference.id
155 | end
156 | end
157 | end
158 |
159 | class RedSnowParsingActionTest < Minitest::Test
160 | context 'Action' do
161 | setup do
162 | source = <<-STR
163 | # My Resource [/resource]
164 | Resource description
165 |
166 | ## Retrieve Resource [GET]
167 | Method description
168 |
169 | + Parameters
170 | + limit = `20` (optional, number, `42`) ... This is a limit
171 |
172 | + Response 202
173 |
174 | + Response 203
175 |
176 | STR
177 |
178 | @result = RedSnow.parse(source.unindent)
179 | @resource_group = @result.ast.resource_groups[0]
180 | @resource = @resource_group.resources[0]
181 | @action = @resource.actions[0]
182 | @parameter = @action.parameters.collection.first
183 | end
184 |
185 | should 'have a name' do
186 | assert_equal 'Retrieve Resource', @action.name
187 | end
188 |
189 | should 'have a description' do
190 | assert_equal "Method description\n\n", @action.description
191 | end
192 |
193 | should 'have a method' do
194 | assert_equal 'GET', @action.method
195 | end
196 |
197 | should 'have 1 example' do
198 | assert_equal 1, @action.examples.count
199 | end
200 |
201 | should 'have the right parameters' do
202 | assert_equal 'limit', @parameter.name
203 | end
204 |
205 | should 'have a resource' do
206 | assert_equal @resource, @action.resource
207 | end
208 | end
209 | end
210 |
211 | class RedSnowParsingMetadataTest < Minitest::Test
212 | context 'parses blueprint metadata' do
213 | setup do
214 | source = <<-STR
215 | FORMAT: 1A
216 | A: 1
217 | B: 2
218 | C: 3
219 |
220 | # API Name
221 | STR
222 |
223 | @result = RedSnow.parse(source.unindent)
224 | @metadata = @result.ast.metadata
225 | @collection = @metadata.collection
226 | end
227 |
228 | should 'have metadata' do
229 | assert_equal 'FORMAT', @collection[0][:name]
230 | assert_equal '1A', @collection[0][:value]
231 |
232 | assert_equal 'A', @collection[1][:name]
233 | assert_equal '1', @collection[1][:value]
234 |
235 | assert_equal 'B', @collection[2][:name]
236 | assert_equal '2', @collection[2][:value]
237 |
238 | assert_equal 'C', @collection[3][:name]
239 | assert_equal '3', @collection[3][:value]
240 | end
241 |
242 | should 'return metadata values by element reference method' do
243 | assert_equal '1A', @metadata['FORMAT']
244 | assert_equal '1', @metadata['A']
245 | assert_equal '2', @metadata['B']
246 | assert_equal '3', @metadata['C']
247 | assert_equal nil, @metadata['D']
248 | end
249 | end
250 | end
251 |
252 | class RedSnowParsingParametersTest < Minitest::Test
253 | context 'parses action attributes' do
254 | setup do
255 | source = <<-STR
256 | # /machine{?limit}
257 |
258 | + Parameters
259 | + limit = `20` (optional, number, `42`) ... This is a limit
260 | + Values
261 | + `20`
262 | + `42`
263 | + `53`
264 |
265 | ## GET
266 |
267 | + Response 204
268 | STR
269 |
270 | @result = RedSnow.parse(source.unindent)
271 | @resource_group = @result.ast.resource_groups[0]
272 | @resource = @resource_group.resources[0]
273 | @parameter = @resource.parameters.collection[0]
274 | @values = @parameter.values
275 | end
276 |
277 | should 'have parameters' do
278 | assert_equal 'limit', @parameter.name
279 | assert_equal 'This is a limit', @parameter.description
280 | assert_equal 'number', @parameter.type
281 | assert_equal :optional, @parameter.use
282 | assert_equal '20', @parameter.default_value
283 | assert_equal '42', @parameter.example_value
284 | assert_equal 3, @parameter.values.count
285 |
286 | assert_equal '20', @values[0]
287 | assert_equal '42', @values[1]
288 | assert_equal '53', @values[2]
289 | end
290 | end
291 |
292 | context 'parses action parameters and attributes' do
293 | setup do
294 | source = <<-STR
295 | # GET /coupons/{id}
296 |
297 | + Relation: coupon
298 |
299 | + Parameters
300 | + id (number, `1001`) ... Id of coupon
301 |
302 | + Response 204
303 | STR
304 |
305 | @result = RedSnow.parse(source.unindent)
306 | @resource_group = @result.ast.resource_groups[0]
307 | @resource = @resource_group.resources[0]
308 | @action = @resource.actions[0]
309 | @parameter = @action.parameters.collection[0]
310 | end
311 |
312 | should 'have parameters' do
313 | assert_equal 'id', @parameter.name
314 | assert_equal :required, @parameter.use
315 | assert_equal 'coupon', @action.relation
316 | assert_equal '', @action.uri_template
317 | end
318 | end
319 |
320 | context 'parses resource parameters' do
321 | setup do
322 | source = <<-STR
323 | # /machine{?limit}
324 |
325 | + Parameters
326 | + limit = `20` (optional, number, `42`) ... This is a limit
327 | + Values
328 | + `20`
329 | + `42`
330 | + `53`
331 |
332 | ## GET
333 |
334 | + Response 204
335 | STR
336 |
337 | @result = RedSnow.parse(source.unindent)
338 | @resource_group = @result.ast.resource_groups[0]
339 | @resource = @resource_group.resources[0]
340 | @parameter = @resource.parameters.collection[0]
341 | @values = @parameter.values
342 | end
343 |
344 | should 'have parameters' do
345 | assert_equal 'limit', @parameter.name
346 | assert_equal 'This is a limit', @parameter.description
347 | assert_equal 'number', @parameter.type
348 | assert_equal :optional, @parameter.use
349 | assert_equal '20', @parameter.default_value
350 | assert_equal '42', @parameter.example_value
351 | assert_equal 3, @parameter.values.count
352 |
353 | assert_equal '20', @values[0]
354 | assert_equal '42', @values[1]
355 | assert_equal '53', @values[2]
356 | end
357 | end
358 |
359 | context 'parses action parameters' do
360 | setup do
361 | source = <<-STR
362 | # GET /coupons/{id}
363 |
364 | + Parameters
365 | + id (number, `1001`) ... Id of coupon
366 |
367 | + Response 204
368 | STR
369 |
370 | @result = RedSnow.parse(source.unindent)
371 | @resource_group = @result.ast.resource_groups[0]
372 | @resource = @resource_group.resources[0]
373 | @action = @resource.actions[0]
374 | @parameter = @action.parameters.collection[0]
375 | end
376 |
377 | should 'have parameters' do
378 | assert_equal 'id', @parameter.name
379 | assert_equal :required, @parameter.use
380 | end
381 | end
382 | end
383 |
384 | class RedSnowParsingMultipleTransactionsTest < Minitest::Test
385 | context 'parses multiple transactions' do
386 | setup do
387 | source = <<-STR
388 | ## Notes Collection [/notes?id={id}&testingGroup={testtingGroup}&collection={collection}]
389 | ### Create a Note [POST]
390 | + Request Create a note
391 |
392 | + Headers
393 |
394 | Content-Type: application/json
395 | Prefer: creating
396 |
397 | + Body
398 |
399 | { "title": "Buy cheese and bread for breakfast." }
400 |
401 | + Response 201 (application/json)
402 |
403 | { "id": 3, "title": "Buy cheese and bread for breakfast." }
404 |
405 | + Request Unable to create note
406 |
407 | + Headers
408 |
409 | Content-Type: application/json
410 | Prefer: testing
411 |
412 |
413 | + Body
414 |
415 | { "ti": "Buy cheese and bread for breakfast." }
416 |
417 | + Response 500
418 |
419 | { "error": "can't create record" }
420 | STR
421 | @result = RedSnow.parse(source.unindent)
422 | @resource_group = @result.ast.resource_groups[0]
423 | @action = @resource_group.resources[0].actions[0]
424 | @examples = @action.examples
425 | end
426 |
427 | should 'have multiple requests and responses' do
428 | assert_equal 2, @examples.count
429 | assert_equal 1, @examples[0].requests.count
430 | assert_equal 1, @examples[0].responses.count
431 | assert_equal '', @examples[0].name
432 | assert_equal '', @examples[0].description
433 | assert_equal 'Create a note', @examples[0].requests[0].name
434 | assert_equal 'Content-Type', @examples[0].requests[0].headers.collection[0][:name]
435 | assert_equal 'application/json', @examples[0].requests[0].headers.collection[0][:value]
436 | assert_equal 'Prefer', @examples[0].requests[0].headers.collection[1][:name]
437 | assert_equal 'creating', @examples[0].requests[0].headers.collection[1][:value]
438 | assert_equal '201', @examples[0].responses[0].name
439 | assert_equal @action, @examples[0].action
440 | assert_equal @examples[0], @examples[0].responses[0].example
441 | assert_equal @examples[0], @examples[0].requests[0].example
442 | assert_equal 'Unable to create note', @examples[1].requests[0].name
443 | assert_equal 'Content-Type', @examples[1].requests[0].headers.collection[0][:name]
444 | assert_equal 'application/json', @examples[1].requests[0].headers['content-type']
445 | assert_equal 'Prefer', @examples[1].requests[0].headers.collection[1][:name]
446 | assert_equal 'testing', @examples[1].requests[0].headers['prefer']
447 | assert_equal '{ "ti": "Buy cheese and bread for breakfast." }' + "\n", @examples[1].requests[0].body
448 | assert_equal '500', @examples[1].responses[0].name
449 | assert_equal @action, @examples[1].action
450 | assert_equal @examples[1], @examples[1].responses[0].example
451 | assert_equal @examples[1], @examples[1].requests[0].example
452 | end
453 | end
454 | end
455 |
--------------------------------------------------------------------------------