jekyll-ramler 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +9 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +103 -0
- data/LICENSE.md +27 -0
- data/README.md +53 -0
- data/jekyll-ramler.gemspec +17 -0
- data/lib/jekyll-ramler.rb +2 -0
- data/lib/raml-generate.rb +266 -0
- data/lib/utils.rb +135 -0
- data/spec/reference_page_generator_spec.rb +125 -0
- data/spec/spec_assets/json/schema/allOf_schema.schema.json +23 -0
- data/spec/spec_assets/json/schema/foo.include.schema.json +7 -0
- data/spec/spec_assets/json/schema/ref_schema.schema.json +9 -0
- data/spec/spec_assets/json/schema/simple_schema.schema.json +17 -0
- data/spec/spec_assets/raml/simple.raml +73 -0
- data/spec/spec_helper.rb +109 -0
- data/spec/utils_json_schema_compiler_spec.rb +87 -0
- data/spec/utils_raml_schema_generator_spec.rb +91 -0
- metadata +76 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc0e1f14109050ea1f296cc5ed5b3fb1ac100510
|
4
|
+
data.tar.gz: fdf5cee7e082ea0397f460233683b1b13aa5e034
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 54959f41e90effd0ab0af92512cc49780771e6bfb5cf204e5b725f81d24f70834396eff93c9f384ee49738cfd689eb0a34959e986ef36a6a6809476d36aa9a43
|
7
|
+
data.tar.gz: 4fe17f77d424cba1df92d33ce9ecb08d31179f25dfcdb03e3443f7aac569021897e50b7ce21c7d5783de3e54fad03dd7cdac006a076591eed616312faadfd87e
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
jekyll-ramler (0.0.1)
|
5
|
+
jekyll
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
blankslate (2.1.2.4)
|
11
|
+
celluloid (0.16.0)
|
12
|
+
timers (~> 4.0.0)
|
13
|
+
classifier-reborn (2.0.3)
|
14
|
+
fast-stemmer (~> 1.0)
|
15
|
+
coderay (1.1.0)
|
16
|
+
coffee-script (2.3.0)
|
17
|
+
coffee-script-source
|
18
|
+
execjs
|
19
|
+
coffee-script-source (1.9.0)
|
20
|
+
colorator (0.1)
|
21
|
+
diff-lcs (1.2.5)
|
22
|
+
execjs (2.3.0)
|
23
|
+
fakefs (0.6.5)
|
24
|
+
fast-stemmer (1.0.2)
|
25
|
+
ffi (1.9.6)
|
26
|
+
hitimes (1.2.2)
|
27
|
+
jekyll (2.5.3)
|
28
|
+
classifier-reborn (~> 2.0)
|
29
|
+
colorator (~> 0.1)
|
30
|
+
jekyll-coffeescript (~> 1.0)
|
31
|
+
jekyll-gist (~> 1.0)
|
32
|
+
jekyll-paginate (~> 1.0)
|
33
|
+
jekyll-sass-converter (~> 1.0)
|
34
|
+
jekyll-watch (~> 1.1)
|
35
|
+
kramdown (~> 1.3)
|
36
|
+
liquid (~> 2.6.1)
|
37
|
+
mercenary (~> 0.3.3)
|
38
|
+
pygments.rb (~> 0.6.0)
|
39
|
+
redcarpet (~> 3.1)
|
40
|
+
safe_yaml (~> 1.0)
|
41
|
+
toml (~> 0.1.0)
|
42
|
+
jekyll-coffeescript (1.0.1)
|
43
|
+
coffee-script (~> 2.2)
|
44
|
+
jekyll-gist (1.1.0)
|
45
|
+
jekyll-paginate (1.1.0)
|
46
|
+
jekyll-sass-converter (1.3.0)
|
47
|
+
sass (~> 3.2)
|
48
|
+
jekyll-watch (1.2.1)
|
49
|
+
listen (~> 2.7)
|
50
|
+
kramdown (1.5.0)
|
51
|
+
liquid (2.6.2)
|
52
|
+
listen (2.8.5)
|
53
|
+
celluloid (>= 0.15.2)
|
54
|
+
rb-fsevent (>= 0.9.3)
|
55
|
+
rb-inotify (>= 0.9)
|
56
|
+
mercenary (0.3.5)
|
57
|
+
method_source (0.8.2)
|
58
|
+
parslet (1.5.0)
|
59
|
+
blankslate (~> 2.0)
|
60
|
+
posix-spawn (0.3.9)
|
61
|
+
pry (0.10.1)
|
62
|
+
coderay (~> 1.1.0)
|
63
|
+
method_source (~> 0.8.1)
|
64
|
+
slop (~> 3.4)
|
65
|
+
pygments.rb (0.6.2)
|
66
|
+
posix-spawn (~> 0.3.6)
|
67
|
+
yajl-ruby (~> 1.2.0)
|
68
|
+
rb-fsevent (0.9.4)
|
69
|
+
rb-inotify (0.9.5)
|
70
|
+
ffi (>= 0.5.0)
|
71
|
+
redcarpet (3.2.2)
|
72
|
+
rspec (3.2.0)
|
73
|
+
rspec-core (~> 3.2.0)
|
74
|
+
rspec-expectations (~> 3.2.0)
|
75
|
+
rspec-mocks (~> 3.2.0)
|
76
|
+
rspec-core (3.2.0)
|
77
|
+
rspec-support (~> 3.2.0)
|
78
|
+
rspec-expectations (3.2.0)
|
79
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
80
|
+
rspec-support (~> 3.2.0)
|
81
|
+
rspec-mocks (3.2.0)
|
82
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
83
|
+
rspec-support (~> 3.2.0)
|
84
|
+
rspec-support (3.2.1)
|
85
|
+
ruby_deep_clone (0.6.0)
|
86
|
+
safe_yaml (1.0.4)
|
87
|
+
sass (3.4.11)
|
88
|
+
slop (3.6.0)
|
89
|
+
timers (4.0.1)
|
90
|
+
hitimes
|
91
|
+
toml (0.1.2)
|
92
|
+
parslet (~> 1.5.0)
|
93
|
+
yajl-ruby (1.2.1)
|
94
|
+
|
95
|
+
PLATFORMS
|
96
|
+
ruby
|
97
|
+
|
98
|
+
DEPENDENCIES
|
99
|
+
fakefs
|
100
|
+
jekyll-ramler!
|
101
|
+
pry
|
102
|
+
rspec
|
103
|
+
ruby_deep_clone
|
data/LICENSE.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) 2015, GovDelivery, Inc.
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
11
|
+
this list of conditions and the following disclaimer in the documentation
|
12
|
+
and/or other materials provided with the distribution.
|
13
|
+
|
14
|
+
3. Neither the name of GovDelivery nor the names of its contributors may be
|
15
|
+
used to endorse or promote products derived from this software without
|
16
|
+
specific prior written permission.
|
17
|
+
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
22
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
23
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
24
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
25
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
26
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
[![Build Status](https://travis-ci.org/govdelivery/jekyll-ramler.svg?branch=master)](https://travis-ci.org/govdelivery/jekyll-ramler)
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/jekyll-ramler.svg)](http://badge.fury.io/rb/jekyll-ramler)
|
3
|
+
|
4
|
+
jekyll-ramler
|
5
|
+
=============
|
6
|
+
|
7
|
+
Generates Jekyll pages for overview, security, and resource documentation
|
8
|
+
specificed in a RAML file.
|
9
|
+
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
### Dependencies
|
14
|
+
|
15
|
+
jekyll-ramler relies on [raml-cop](https://www.npmjs.com/package/raml-cop), a
|
16
|
+
Node.js script, to actually parse a RAML file. Thus, you will need to install
|
17
|
+
[Node.js](http://nodejs.org/), then perform `npm install raml-cop`.
|
18
|
+
|
19
|
+
|
20
|
+
## JSON Schema Support
|
21
|
+
|
22
|
+
jekyll-ramler includes a few features for JSON Schema.
|
23
|
+
|
24
|
+
### Table and Raw views of JSON Schema
|
25
|
+
|
26
|
+
If a resource method defines `body:application/json:schema` item, then
|
27
|
+
jekyll-ramler will generate two views of the schema. One view will be an
|
28
|
+
easily copyable display of the raw schema. The other view will be a table-like
|
29
|
+
display of parameters, similar to what is generated for
|
30
|
+
`body:application/x-www-form-urlencoded:formParamters`.
|
31
|
+
|
32
|
+
### Generated JSON Schema from formParameters
|
33
|
+
|
34
|
+
Recognizing that Content-Type does not impact functionality or content for some
|
35
|
+
APIs, jekyll-ramler will generate JSON Schema for resource methods that include
|
36
|
+
a defined `body:application/x-www-form-urlencoded:formParamters` list, as well
|
37
|
+
as a `body:application/json` item **without** a `schema` item. If a resource
|
38
|
+
includes a `body:application/json/schema` item, then nothing will happen.
|
39
|
+
|
40
|
+
## $ref and allOf
|
41
|
+
|
42
|
+
[JSON Schema provides a semantic construct for schema inheritance](http://spacetelescope.github.io/understanding-json-schema/reference/combining.html)
|
43
|
+
via the **$ref** and **allOf** keywords. jekyll-ramler uses this construct to
|
44
|
+
allow for schema referred to in RAML to inherit from other schema, which allows
|
45
|
+
you to DRY your JSON schema bit.
|
46
|
+
|
47
|
+
When jekyll-ramler encounteres **$ref** and/or **allOf**, it pulls in and
|
48
|
+
merges the referred to schemas, creating a single schema object that includes
|
49
|
+
all attributes. Thus, while you might store your JSON schema across multiple
|
50
|
+
files, users of your site will only see fully de-referenced and merged JSON
|
51
|
+
schema on your site. This de-referencing and merging also allows jekyll-ramler
|
52
|
+
to generate complete, table-like views of your JSON schema, even if your schema
|
53
|
+
inherits from other schema.
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'jekyll-ramler'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.date = '2015-02-09'
|
5
|
+
s.authors = ['GovDelivery']
|
6
|
+
s.email = 'support@govdelivery.com'
|
7
|
+
s.homepage = 'https://github.com/govdelivery/jekyll-ramler'
|
8
|
+
s.license = 'BSD-3-Cluase'
|
9
|
+
s.summary = 'Jekyll plugin that generates API documentation pages based on RAML'
|
10
|
+
s.description = %q{Generates Jekyll pages for overview, security, and
|
11
|
+
resource documentation specificed in a RAML file.}
|
12
|
+
|
13
|
+
s.add_runtime_dependency 'jekyll'
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split($\)
|
16
|
+
s.require_paths = ['lib']
|
17
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
class RawFile<StaticFile
|
5
|
+
def initialize(site, base, dir, name, content)
|
6
|
+
@content = content
|
7
|
+
super(site, base, dir, name)
|
8
|
+
end
|
9
|
+
|
10
|
+
def write(dest)
|
11
|
+
dest_path = File.join(dest, @dir, @name)
|
12
|
+
FileUtils.mkdir_p(File.dirname(dest_path))
|
13
|
+
File.open(dest_path, 'w') do |f|
|
14
|
+
f.write(@content)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class GeneratedPage<Page
|
20
|
+
def initialize(site, base, dir, item, layout=nil)
|
21
|
+
@site = site
|
22
|
+
@base = base
|
23
|
+
@dir = dir.gsub(/{(\w*)}/, '--\1--').gsub(/\s/, '_')
|
24
|
+
@name = 'index.html'
|
25
|
+
|
26
|
+
layout = get_layout(dir) if layout.nil?
|
27
|
+
layout << '.html' if not layout.end_with?('.html')
|
28
|
+
|
29
|
+
self.process(@name)
|
30
|
+
self.read_yaml(File.join(base, '_layouts'), layout)
|
31
|
+
|
32
|
+
self.data['title'] = "#{item['title']}"
|
33
|
+
if item.include?('description')
|
34
|
+
self.data['description'] = transform_md(item['description'])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def transform_md(output)
|
40
|
+
# Use the existing Jekyll Markdown converters
|
41
|
+
md_converters = site.converters.select{|c| c.matches('.md')}
|
42
|
+
md_converters.reduce(output) do |output, converter|
|
43
|
+
converter.convert output
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_layout(dir, site=@site)
|
48
|
+
default = site.config.fetch('defaults', {}).detect do |default|
|
49
|
+
default['scope']['path'] == dir.split('/').first
|
50
|
+
end
|
51
|
+
|
52
|
+
default = {"values" => {}} if default.nil?
|
53
|
+
|
54
|
+
default['values'].fetch('layout', 'default')
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
class SecuritySchemePage<GeneratedPage
|
60
|
+
def initialize(site, base, dir, securityScheme)
|
61
|
+
super(site, base, dir, securityScheme, layout=get_layout('resource', site))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class DocumentationPage<GeneratedPage
|
66
|
+
def initialize(site, base, dir, documentation)
|
67
|
+
super(site, base, dir, documentation)
|
68
|
+
|
69
|
+
output = documentation['content']
|
70
|
+
self.data['body'] = transform_md(output)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class ResourcePage<GeneratedPage
|
75
|
+
def initialize(site, base, dir, resource, traits, securitySchemes)
|
76
|
+
# Sandbox our resource, and do nothing with child resources
|
77
|
+
resource = resource.dup
|
78
|
+
resource.delete('resources')
|
79
|
+
|
80
|
+
resource['title'] = dir.sub('resource', '') if not resource.include?('title')
|
81
|
+
super(site, base, dir, resource)
|
82
|
+
|
83
|
+
# Add security data to the resource
|
84
|
+
resource.fetch('methods', []).each do |method|
|
85
|
+
if method.include?('securedBy')
|
86
|
+
for scheme in method['securedBy']
|
87
|
+
for attr in ['headers', 'queryParameters', 'responses']
|
88
|
+
method[attr] = {} if not method.include?(attr)
|
89
|
+
method[attr].merge!(securitySchemes[scheme].fetch('describedBy', {}).fetch(attr, {}))
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Add trait data to the resource
|
96
|
+
resource.fetch('methods', []).each do |method|
|
97
|
+
if method.include?('is')
|
98
|
+
for trait in method['is']
|
99
|
+
merge_method_trait(method, traits[trait])
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Compile JSON Schema that use $ref references
|
105
|
+
jsc = Jekyll::JsonSchemaCompiler.new
|
106
|
+
jsc.compile(resource['methods'])
|
107
|
+
|
108
|
+
# Generate new schema based on existing formParameters
|
109
|
+
schema_generator = Jekyll::RamlSchemaGenerator.new(site, resource['title'])
|
110
|
+
schema_generator.insert_schemas(resource['methods'])
|
111
|
+
|
112
|
+
# Transform descriptions via Markdown
|
113
|
+
resource = transform_resource_descriptions(resource)
|
114
|
+
self.data['methods'] = add_schema_hashes(resource['methods'])
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def merge_method_trait(method, trait)
|
119
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
120
|
+
method.merge(trait, &merger)
|
121
|
+
|
122
|
+
if method.include?('description')
|
123
|
+
method['description'] = "#{method['description']}\n\n#{trait['description']}"
|
124
|
+
else
|
125
|
+
method['description'] = trait['description']
|
126
|
+
end
|
127
|
+
method
|
128
|
+
end
|
129
|
+
|
130
|
+
def transform_resource_descriptions(resource)
|
131
|
+
if resource.include?('description')
|
132
|
+
resource['description'] = transform_md(resource['description'])
|
133
|
+
end
|
134
|
+
|
135
|
+
resource.each_key do |key|
|
136
|
+
if resource[key].is_a?(Hash)
|
137
|
+
resource[key] = transform_resource_descriptions(resource[key])
|
138
|
+
end
|
139
|
+
if resource[key].is_a?(Array)
|
140
|
+
resource[key].map!{|h| h.is_a?(Hash) ? transform_resource_descriptions(h) : h }
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
resource
|
145
|
+
end
|
146
|
+
|
147
|
+
# Adds a 'schema_hash' attribute to bodies with 'schema', which allows for the generation of schema table views
|
148
|
+
def add_schema_hashes(obj, key=nil)
|
149
|
+
if obj.is_a?(Array)
|
150
|
+
obj.map! { |method| add_schema_hashes(method) }
|
151
|
+
elsif obj.is_a?(Hash)
|
152
|
+
obj.each { |k, v| obj[k] = add_schema_hashes(v, k)}
|
153
|
+
|
154
|
+
if obj.include?("schema")
|
155
|
+
|
156
|
+
case key
|
157
|
+
when 'application/json'
|
158
|
+
obj['schema_hash'] = JSON.parse(obj['schema'])
|
159
|
+
|
160
|
+
refactor_object = lambda do |obj|
|
161
|
+
obj['properties'].each do |name, param|
|
162
|
+
param['displayName'] = name
|
163
|
+
param['required'] = true if obj.fetch('required', []).include?(name)
|
164
|
+
|
165
|
+
if param.include?('example') and ['object', 'array'].include?(param['type'])
|
166
|
+
param['example'] = JSON.pretty_generate(JSON.parse(param['example']))
|
167
|
+
elsif param.include?('properties')
|
168
|
+
param['example'] = JSON.pretty_generate(param['properties'])
|
169
|
+
elsif param.include?('items')
|
170
|
+
param['example'] = JSON.pretty_generate(param['items'])
|
171
|
+
end
|
172
|
+
|
173
|
+
obj['properties'][name] = param
|
174
|
+
end
|
175
|
+
obj
|
176
|
+
end
|
177
|
+
|
178
|
+
if obj['schema_hash'].include?('properties')
|
179
|
+
obj['schema_hash'] = refactor_object.call(obj['schema_hash'])
|
180
|
+
end
|
181
|
+
|
182
|
+
if obj['schema_hash'].include?('items') and obj['schema_hash']['items'].include?('properties')
|
183
|
+
obj['schema_hash']['items'] = refactor_object.call(obj['schema_hash']['items'])
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
obj
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class ReferencePageGenerator < Generator
|
194
|
+
safe true
|
195
|
+
|
196
|
+
def generate(site)
|
197
|
+
@site = site
|
198
|
+
|
199
|
+
raml_js_path = site.config.fetch('raml_json_file_path', 'api.json')
|
200
|
+
raml_js = File.open(raml_js_path).read
|
201
|
+
raml_hash = JSON.parse(raml_js)
|
202
|
+
|
203
|
+
# BETTER THE DATASTRUCTURES!
|
204
|
+
@traits = {}
|
205
|
+
@securitySchemes = {}
|
206
|
+
if raml_hash.has_key?('traits')
|
207
|
+
raml_hash['traits'].each {|obj| obj.each_pair {|k, v| @traits[k] = v}}
|
208
|
+
end
|
209
|
+
if raml_hash.has_key?('securitySchemes')
|
210
|
+
raml_hash['securitySchemes'].each do |obj|
|
211
|
+
obj.each_pair do |k, v|
|
212
|
+
v.fetch('describedBy', {}).fetch('headers', {}).each_pair{ |hn, hv| hv['displayName'] = hn if not hv.nil?}
|
213
|
+
v.fetch('describedBy', {}).fetch('queryParameters', {}).each_pair{ |hn, hv| hv['displayName'] = hn if not hv.nil?}
|
214
|
+
@securitySchemes[k] = v
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# Create a page for each resource
|
220
|
+
if raml_hash.has_key?('resources')
|
221
|
+
generate_resource_pages(raml_hash['resources'])
|
222
|
+
end
|
223
|
+
|
224
|
+
dir = Jekyll::get_dir('overview', @site.config)
|
225
|
+
@securitySchemes.each do |scheme_name, scheme|
|
226
|
+
scheme_dir = File.join(dir, scheme_name)
|
227
|
+
scheme['title'] = scheme_name
|
228
|
+
@site.pages << SecuritySchemePage.new(@site, @site.source, scheme_dir, scheme)
|
229
|
+
end
|
230
|
+
|
231
|
+
raml_hash.fetch('documentation', []).each do |documentation|
|
232
|
+
documentation_dir = File.join(dir, documentation['title'])
|
233
|
+
@site.pages << DocumentationPage.new(@site, @site.source, documentation_dir, documentation)
|
234
|
+
end
|
235
|
+
|
236
|
+
# Allow users to download the RAML, which may be modified since it was read
|
237
|
+
raml_download_path = site.config.fetch('raml_download_path', 'api.raml').split('/')
|
238
|
+
raml_download_filename = raml_download_path.delete_at(-1)
|
239
|
+
raml_download_path = raml_download_path.join('/') + '/'
|
240
|
+
|
241
|
+
raml_yaml = raml_hash.to_yaml
|
242
|
+
raml_yaml.sub!('---', '#%RAML 0.8')
|
243
|
+
@site.static_files << RawFile.new(@site, @site.source, raml_download_path, raml_download_filename, raml_yaml)
|
244
|
+
end
|
245
|
+
|
246
|
+
private
|
247
|
+
def generate_resource_pages(resources, parent_dir=nil)
|
248
|
+
|
249
|
+
if parent_dir
|
250
|
+
dir = parent_dir
|
251
|
+
else
|
252
|
+
dir = Jekyll::get_dir('resource', @site.config)
|
253
|
+
end
|
254
|
+
|
255
|
+
resources.each do |resource|
|
256
|
+
resource_name = resource["relativeUri"]
|
257
|
+
resource_dir = File.join(dir, resource_name)
|
258
|
+
@site.pages << ResourcePage.new(@site, @site.source, resource_dir, resource, @traits, @securitySchemes)
|
259
|
+
if resource.has_key?('resources')
|
260
|
+
generate_resource_pages(resource['resources'], resource_dir)
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
data/lib/utils.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Jekyll
|
4
|
+
def self.get_dir(page_type, config)
|
5
|
+
config.fetch('page_dirs', {}).fetch(page_type, page_type)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.sanatize_json_string(s)
|
9
|
+
strip_newlines(s)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.strip_newlines(s)
|
13
|
+
# Assuming Markdown, so do NOT remove consecutive newlines
|
14
|
+
regex = /([^\n])\n([^\n])/
|
15
|
+
s.gsub(regex, '\1 \2').strip
|
16
|
+
end
|
17
|
+
|
18
|
+
# Utility class for creating schema (current JSON, perhaps XML someday) based
|
19
|
+
# on existing RAML formParameters
|
20
|
+
class RamlSchemaGenerator
|
21
|
+
|
22
|
+
|
23
|
+
def initialize(site, title=nil)
|
24
|
+
@site = site
|
25
|
+
@title = title
|
26
|
+
@current_method = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
# Creates a schema attribute sibling of any formParameter attribute found,
|
30
|
+
# based on the found formParameters attribute.
|
31
|
+
#
|
32
|
+
# Existing schema siblings of formParameter attributes are not modified.
|
33
|
+
#
|
34
|
+
# Modifys obj, and returns the modified obj
|
35
|
+
def insert_schemas(obj)
|
36
|
+
if obj.is_a?(Array)
|
37
|
+
obj.map!{|method| insert_schemas(method)}
|
38
|
+
elsif obj.is_a?(Hash)
|
39
|
+
@current_method = obj['method'] if obj.include?('method')
|
40
|
+
|
41
|
+
obj.each { |k, v| obj[k] = insert_schemas(v)}
|
42
|
+
|
43
|
+
if obj.include?('body')
|
44
|
+
if obj['body'].fetch('application/x-www-form-urlencoded', {}).include?('formParameters')
|
45
|
+
if obj['body'].include?('application/json') && !(obj['body']['application/json'].include?('schema'))
|
46
|
+
insert_json_schema(obj, generate_json_schema(obj))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
obj
|
53
|
+
end
|
54
|
+
|
55
|
+
# Inserts provided JSON Schema into obj['body']['application/json']['schema']
|
56
|
+
def insert_json_schema(obj, schema)
|
57
|
+
obj['body']['application/json']['schema'] = schema
|
58
|
+
end
|
59
|
+
|
60
|
+
# Creates JSON Schema - as a string - based on obj['body']['application/x-www-form-urlencoded']['formParameters']
|
61
|
+
def generate_json_schema(obj)
|
62
|
+
|
63
|
+
# JSON Schema spec: http://json-schema.org/latest/json-schema-validation.html
|
64
|
+
schema_hash = {}
|
65
|
+
schema_hash['$schema'] = @site.config['json_schema_schema_uri']
|
66
|
+
schema_hash['title'] = @title if @title
|
67
|
+
schema_hash['description'] = Jekyll::sanatize_json_string(obj['description']) if obj.include?('description')
|
68
|
+
schema_hash['type'] = 'object'
|
69
|
+
|
70
|
+
required_properties = []
|
71
|
+
schema_hash['properties'] = obj['body']['application/x-www-form-urlencoded']['formParameters'].dup
|
72
|
+
schema_hash['properties'].each do |name, param|
|
73
|
+
if param.include?('required')
|
74
|
+
required_properties << name if param['required'] == true
|
75
|
+
param.delete('required')
|
76
|
+
end
|
77
|
+
|
78
|
+
if param.include?('description')
|
79
|
+
param['description'] = Jekyll::sanatize_json_string(param['description'])
|
80
|
+
end
|
81
|
+
|
82
|
+
# Repeat is not a supported keyword in JSON Schema
|
83
|
+
if param.include?('repeat')
|
84
|
+
param.delete('repeat')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
schema_hash['required'] = required_properties if not required_properties.empty?
|
88
|
+
|
89
|
+
JSON.pretty_generate(schema_hash)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class JsonSchemaCompiler
|
94
|
+
def compile(obj, obj_name=nil)
|
95
|
+
if obj.is_a?(Array)
|
96
|
+
obj.map!{|method| compile(method)}
|
97
|
+
elsif obj.is_a?(Hash)
|
98
|
+
if obj.include?('schema') and obj_name == 'application/json'
|
99
|
+
schema_hash = JSON.parse(obj['schema'])
|
100
|
+
schema_hash = traverse_and_compile_schema(schema_hash)
|
101
|
+
obj['schema'] = JSON.pretty_generate(schema_hash)
|
102
|
+
end
|
103
|
+
|
104
|
+
obj.each { |k, v| obj[k] = compile(v, k)}
|
105
|
+
end
|
106
|
+
|
107
|
+
obj
|
108
|
+
end
|
109
|
+
|
110
|
+
private
|
111
|
+
def traverse_and_compile_schema(schema_hash)
|
112
|
+
if schema_hash.is_a?(Array)
|
113
|
+
schema_hash.map!{|method| traverse_and_compile_schema(method)}
|
114
|
+
elsif schema_hash.is_a?(Hash)
|
115
|
+
if schema_hash.include?('$ref')
|
116
|
+
refed = JSON.parse(File.read(schema_hash['$ref']))
|
117
|
+
schema_hash.delete('$ref')
|
118
|
+
schema_hash.merge!(refed)
|
119
|
+
end
|
120
|
+
|
121
|
+
schema_hash.each { |k, v| schema_hash[k] = traverse_and_compile_schema(v)}
|
122
|
+
|
123
|
+
# Merge allOfs into the parent object
|
124
|
+
if schema_hash.include?('allOf')
|
125
|
+
for item in schema_hash['allOf']
|
126
|
+
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
127
|
+
schema_hash.merge!(item, &merger)
|
128
|
+
end
|
129
|
+
schema_hash.delete('allOf')
|
130
|
+
end
|
131
|
+
end
|
132
|
+
schema_hash
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require_relative './spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'ReferencePageGenerator', fakefs:true do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@site = Jekyll::Site.new(Jekyll.configuration({
|
7
|
+
"skip_config_files" => true,
|
8
|
+
"json_schema_schema_uri" => "http://json-schema.org/draft-04/schema#"
|
9
|
+
}))
|
10
|
+
@rpg = Jekyll::ReferencePageGenerator.new
|
11
|
+
|
12
|
+
FakeFS.activate!
|
13
|
+
Pry.config.history.should_save = false;
|
14
|
+
Pry.config.history.should_load = false;
|
15
|
+
|
16
|
+
FileUtils.mkdir_p('_layouts')
|
17
|
+
File.open('_layouts/default.html', 'w') { |f| f << "{{ content }}" }
|
18
|
+
|
19
|
+
File.open('api.json', 'w') do |f|
|
20
|
+
f << JSON.pretty_generate(load_simple_raml)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
after(:each) do
|
26
|
+
FakeFS.deactivate!
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'Generating Pages from RAML' do
|
30
|
+
|
31
|
+
it 'generates a resource page for each resource in the RAML' do
|
32
|
+
@rpg.generate(@site)
|
33
|
+
|
34
|
+
raml_hash = load_simple_raml
|
35
|
+
passed = recursive_resource_search(raml_hash, @site)
|
36
|
+
|
37
|
+
expect(passed).to be true
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'generates an overview page for each security or documentation item in the RAML' do
|
41
|
+
@rpg.generate(@site)
|
42
|
+
raml_hash = load_simple_raml
|
43
|
+
|
44
|
+
passed = documentation_search(raml_hash, @site)
|
45
|
+
expect(passed).to be true
|
46
|
+
|
47
|
+
|
48
|
+
passed = security_search(raml_hash, @site)
|
49
|
+
expect(passed).to be true
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'transforms descriptions via Markdown' do
|
54
|
+
@rpg.generate(@site)
|
55
|
+
super_security = @site.pages.select {|p| p.data['title'] == 'Super Security'}.first
|
56
|
+
|
57
|
+
expect(super_security.data['description']).to match /<p>.*<\/p>/m
|
58
|
+
expect(super_security.data['description']).to include "<em>secured</em>"
|
59
|
+
|
60
|
+
test_doc = @site.pages.select {|p| p.data['title'] == 'Some Test Content'}.first
|
61
|
+
expect(test_doc.data['body']).to match /<p>.*<\/p>\n\n<h1.*>.*<\/h1>\n\n<p>.*<\/p>/m
|
62
|
+
expect(test_doc.data['body']).to include "<strong>Hello</strong>"
|
63
|
+
|
64
|
+
test_resource = @site.pages.select {|p| p.data['title'] == '/test_resource'}.first
|
65
|
+
test_post = test_resource.data['methods'].select {|r| r['method'] == 'post' }.first
|
66
|
+
test_post['body']['application/x-www-form-urlencoded']['formParameters'].each do |param|
|
67
|
+
expect(param[1]['description']).to match /<p>.*<\/p>/m
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'inserts trait properties into resources that have traits' do
|
72
|
+
@rpg.generate(@site)
|
73
|
+
|
74
|
+
test_resource = @site.pages.select {|p| p.data['title'] == '/test_resource'}.first
|
75
|
+
test_post = test_resource.data['methods'].select {|r| r['method'] == 'post' }.first
|
76
|
+
expect(test_post['responses']).to include "418"
|
77
|
+
expect(test_post['responses']['418']['body']['application/json']['example']).to include "I'm a teapot"
|
78
|
+
|
79
|
+
# Should not insert a traits properties into pages that do not have that trait
|
80
|
+
@site.pages.delete(test_resource)
|
81
|
+
@site.pages.each do |page|
|
82
|
+
expect(page.data.to_s).not_to include "418"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'inserts security properties into resoruces that are secured' do
|
88
|
+
@rpg.generate(@site)
|
89
|
+
|
90
|
+
test_resource = @site.pages.select {|p| p.data['title'] == '/test_resource'}.first
|
91
|
+
test_post = test_resource.data['methods'].select {|r| r['method'] == 'post' }.first
|
92
|
+
|
93
|
+
expect(test_post['responses']).to include "401"
|
94
|
+
expect(test_post['responses']['401']['body']['application/json']['example']).to include "Whatcha doin' here?"
|
95
|
+
expect(test_post).to include "queryParameters"
|
96
|
+
expect(test_post['queryParameters']).to include "auth"
|
97
|
+
expect(test_post).to include "headers"
|
98
|
+
expect(test_post['headers']).to include "X-SUPER-SECURE"
|
99
|
+
expect(test_post['headers']['X-SUPER-SECURE']).to include "displayName"
|
100
|
+
expect(test_post['headers']['X-SUPER-SECURE']).to include "type"
|
101
|
+
expect(test_post['headers']['X-SUPER-SECURE']).to include "description"
|
102
|
+
expect(test_post['headers']['X-SUPER-SECURE']).to include "required"
|
103
|
+
|
104
|
+
|
105
|
+
# Should not insert security properties into pages that are not secured
|
106
|
+
@site.pages.delete(test_resource)
|
107
|
+
@site.pages.each do |page|
|
108
|
+
expect(page.data.to_s).not_to include "401"
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'creates downloadable RAML and JSON of the API descriptor' do
|
114
|
+
@rpg.generate(@site)
|
115
|
+
@site.process
|
116
|
+
expect(File.file?(File.join('_site', 'api.raml'))).to be true
|
117
|
+
api_raml = File.read(File.join('_site', 'api.raml'))
|
118
|
+
expect(api_raml).to start_with "#%RAML 0.8"
|
119
|
+
|
120
|
+
expect(File.file?(File.join('_site', 'api.json'))).to be true
|
121
|
+
api_json = File.read(File.join('_site', 'api.json'))
|
122
|
+
expect{JSON.parse(api_json)}.not_to raise_error
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "allOf Schema",
|
4
|
+
"description": "An example of a schema with an allOf property",
|
5
|
+
"type": "object",
|
6
|
+
"allOf": [
|
7
|
+
{ "properties": {
|
8
|
+
"foo": {
|
9
|
+
"description": "Fooing",
|
10
|
+
"type": "string",
|
11
|
+
"example": "Foo"
|
12
|
+
}
|
13
|
+
}
|
14
|
+
},
|
15
|
+
{ "properties": {
|
16
|
+
"bar": {
|
17
|
+
"description": "A place to get a drink",
|
18
|
+
"type": "object"
|
19
|
+
}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
]
|
23
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"$schema": "http://json-schema.org/draft-04/schema#",
|
3
|
+
"title": "Simple Schema",
|
4
|
+
"description": "An example of a schema with no inheritance",
|
5
|
+
"type": "object",
|
6
|
+
"properties": {
|
7
|
+
"foo": {
|
8
|
+
"description": "Fooing",
|
9
|
+
"type": "string",
|
10
|
+
"example": "Foo"
|
11
|
+
},
|
12
|
+
"bar": {
|
13
|
+
"description": "A place to get a drink",
|
14
|
+
"type": "object"
|
15
|
+
}
|
16
|
+
}
|
17
|
+
}
|
@@ -0,0 +1,73 @@
|
|
1
|
+
#%RAML 0.8
|
2
|
+
title: Test!
|
3
|
+
documentation:
|
4
|
+
- title: Some Test Content
|
5
|
+
content: |
|
6
|
+
**Hello** and welcome to test content!
|
7
|
+
|
8
|
+
# Very Important Heading
|
9
|
+
|
10
|
+
This is all great!
|
11
|
+
securitySchemes:
|
12
|
+
- "Super Security":
|
13
|
+
description: |
|
14
|
+
This is a description of the super secure system we employ to secure our
|
15
|
+
*secured* api.
|
16
|
+
type: x-{other}
|
17
|
+
describedBy:
|
18
|
+
headers:
|
19
|
+
X-SUPER-SECURE:
|
20
|
+
description: Super Secure auth token
|
21
|
+
type: string
|
22
|
+
required: true
|
23
|
+
queryParameters:
|
24
|
+
auth:
|
25
|
+
responses:
|
26
|
+
401:
|
27
|
+
body:
|
28
|
+
application/json:
|
29
|
+
example: |
|
30
|
+
{
|
31
|
+
"error": "Whatcha doin' here?"
|
32
|
+
}
|
33
|
+
- "More Security":
|
34
|
+
description: |
|
35
|
+
This is a description of some more security we employ to secure our
|
36
|
+
*secured* api.
|
37
|
+
type: x-{other}
|
38
|
+
traits:
|
39
|
+
- teapot:
|
40
|
+
usage: Used to test traits
|
41
|
+
responses:
|
42
|
+
418:
|
43
|
+
body:
|
44
|
+
application/json:
|
45
|
+
example: |
|
46
|
+
{
|
47
|
+
"status": "I'm a teapot"
|
48
|
+
}
|
49
|
+
/test_resource:
|
50
|
+
post:
|
51
|
+
description: An example of a schema with no inheritance
|
52
|
+
is: [teapot]
|
53
|
+
securedBy: ["Super Security"]
|
54
|
+
body:
|
55
|
+
application/x-www-form-urlencoded:
|
56
|
+
formParameters:
|
57
|
+
foo:
|
58
|
+
description: Fooing
|
59
|
+
type: string
|
60
|
+
example: Foo
|
61
|
+
bar:
|
62
|
+
description: A place to get a drink
|
63
|
+
type: string
|
64
|
+
/nested_test_resource:
|
65
|
+
post:
|
66
|
+
description: An example of a nested resource
|
67
|
+
body:
|
68
|
+
application/x-www-form-urlencoded:
|
69
|
+
formParameters:
|
70
|
+
stuff:
|
71
|
+
description: Just some junk
|
72
|
+
type: string
|
73
|
+
example: Junk
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'deep_clone'
|
2
|
+
require 'fakefs/spec_helpers'
|
3
|
+
require 'jekyll'
|
4
|
+
require 'pry'
|
5
|
+
require 'rspec/expectations'
|
6
|
+
require_relative '../lib/jekyll-ramler.rb'
|
7
|
+
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.include FakeFS::SpecHelpers, fakefs:true
|
11
|
+
end
|
12
|
+
|
13
|
+
def pretty_json(json)
|
14
|
+
JSON.pretty_generate(JSON.parse(json))
|
15
|
+
end
|
16
|
+
|
17
|
+
def spec_pwd
|
18
|
+
spec_pwd = File.dirname(__FILE__)
|
19
|
+
end
|
20
|
+
|
21
|
+
def example_raml_hash
|
22
|
+
raml_hash = {
|
23
|
+
'title' => 'Test!',
|
24
|
+
'resources' => {
|
25
|
+
'/test_resource' => {
|
26
|
+
'post' => {
|
27
|
+
'description' => 'An example of a schema with no inheritance',
|
28
|
+
'body' => {
|
29
|
+
'application/x-www-form-urlencoded' => {
|
30
|
+
'formParameters' => {
|
31
|
+
'foo' => {
|
32
|
+
'description' => 'Fooing',
|
33
|
+
'type' => 'string',
|
34
|
+
'example' => 'Foo'
|
35
|
+
},
|
36
|
+
'bar' => {
|
37
|
+
'description' => 'A place to get a drink',
|
38
|
+
'type' => 'object'
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
}
|
45
|
+
}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_simple_raml
|
50
|
+
JSON.parse(`raml-cop #{File.join(spec_pwd, 'spec_assets/raml/simple.raml')} -j`)
|
51
|
+
end
|
52
|
+
|
53
|
+
def simple_schema
|
54
|
+
pretty_json(File.read(File.join(spec_pwd, 'spec_assets/json/schema/simple_schema.schema.json')))
|
55
|
+
end
|
56
|
+
|
57
|
+
def ref_schema
|
58
|
+
pretty_json(File.read(File.join(spec_pwd, 'spec_assets/json/schema/ref_schema.schema.json')))
|
59
|
+
end
|
60
|
+
|
61
|
+
def foo_bit
|
62
|
+
pretty_json(File.read(File.join(spec_pwd, 'spec_assets/json/schema/foo.include.schema.json')))
|
63
|
+
end
|
64
|
+
|
65
|
+
def allOf_schema
|
66
|
+
pretty_json(File.read(File.join(spec_pwd, 'spec_assets/json/schema/allOf_schema.schema.json')))
|
67
|
+
end
|
68
|
+
|
69
|
+
def recursive_resource_search(raml_hash, site, parent='')
|
70
|
+
passed = raml_hash['resources'].all? do |resource_hash|
|
71
|
+
site.pages.any? do |page|
|
72
|
+
page.data['title'] == "#{parent}#{resource_hash['relativeUri']}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
if passed
|
77
|
+
raml_hash['resources'].each do |resource_hash|
|
78
|
+
if resource_hash.include?('resources')
|
79
|
+
passed = recursive_resource_search(resource_hash, site, resource_hash['relativeUri'])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
passed
|
85
|
+
end
|
86
|
+
|
87
|
+
def documentation_search(raml_hash, site)
|
88
|
+
raml_hash['documentation'].all? do |resource_hash|
|
89
|
+
site.pages.any? do |page|
|
90
|
+
page.data['title'] == resource_hash['title']
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def security_search(raml_hash, site)
|
96
|
+
raml_hash['securitySchemes'].all? do |security_hash|
|
97
|
+
security_hash.all? do |name, hash|
|
98
|
+
site.pages.any? do |page|
|
99
|
+
page.data['title'] == name
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
RSpec::Matchers.define :dereference do |expected_json|
|
106
|
+
match do |referenced_json|
|
107
|
+
!expected_json.include?('$ref') and referenced_json.all? {|k, v| expected_json[k] == v }
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require_relative './spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Utils' do
|
4
|
+
context 'JSON Schema Compiler' do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@jsc = Jekyll::JsonSchemaCompiler.new
|
8
|
+
@raml_hash = example_raml_hash
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'does not modify an object that does not include a schema item for application/json' do
|
12
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
13
|
+
@raml_hash = @jsc.compile(@raml_hash)
|
14
|
+
expect(@raml_hash).to eq orig_raml_hash
|
15
|
+
|
16
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'] = {
|
17
|
+
'example' => "{ 'bar' => {} }"
|
18
|
+
}
|
19
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
20
|
+
@raml_hash = @jsc.compile(@raml_hash)
|
21
|
+
expect(@raml_hash).to eq orig_raml_hash
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'does not modify an object with a schema item for application/json that does not include $ref or allOf' do
|
25
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'] = {
|
26
|
+
'schema' => simple_schema
|
27
|
+
}
|
28
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
29
|
+
@raml_hash = @jsc.compile(@raml_hash)
|
30
|
+
expect(@raml_hash).to eq orig_raml_hash
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
it 'replaces $ref items in application/json schema with the referred to content' do
|
35
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'] = {
|
36
|
+
'schema' => ref_schema
|
37
|
+
}
|
38
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
39
|
+
|
40
|
+
@raml_hash = @jsc.compile(@raml_hash)
|
41
|
+
orig_schema = orig_raml_hash['resources']['/test_resource']['post']['body']['application/json'].delete('schema')
|
42
|
+
orig_schema = JSON.parse(orig_schema)
|
43
|
+
new_schema = @raml_hash['resources']['/test_resource']['post']['body']['application/json'].delete('schema')
|
44
|
+
new_schema = JSON.parse(new_schema)
|
45
|
+
|
46
|
+
# The rest of the raml object should be identical
|
47
|
+
expect(@raml_hash).to eq(orig_raml_hash)
|
48
|
+
|
49
|
+
# Compiled, new_schema should include referred to properties
|
50
|
+
referenced_json = JSON.parse(foo_bit)
|
51
|
+
expect(new_schema['properties']).to dereference(referenced_json)
|
52
|
+
|
53
|
+
# The rest of the schema should be unchanged
|
54
|
+
referenced_json.each { |k, v| new_schema['properties'].delete(k) }
|
55
|
+
orig_schema['properties'].delete('$ref')
|
56
|
+
expect(new_schema).to eq(orig_schema)
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'merges allOf items into the parent object in application/json schema items' do
|
61
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'] = {
|
62
|
+
'schema' => allOf_schema
|
63
|
+
}
|
64
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
65
|
+
|
66
|
+
@raml_hash = @jsc.compile(@raml_hash)
|
67
|
+
orig_schema = orig_raml_hash['resources']['/test_resource']['post']['body']['application/json'].delete('schema')
|
68
|
+
orig_schema = JSON.parse(orig_schema)
|
69
|
+
new_schema = @raml_hash['resources']['/test_resource']['post']['body']['application/json'].delete('schema')
|
70
|
+
new_schema = JSON.parse(new_schema)
|
71
|
+
|
72
|
+
# The rest of the raml object should be identical
|
73
|
+
expect(@raml_hash).to eq(orig_raml_hash)
|
74
|
+
|
75
|
+
# Compiled, new_schema should have merged all of the allOf items
|
76
|
+
# allOf_schema properties *should* compile to be equal to *simple_schema* properties
|
77
|
+
expect(new_schema).not_to include('allOf')
|
78
|
+
expect(new_schema['properties']).to eq(JSON.parse(simple_schema)['properties'])
|
79
|
+
|
80
|
+
# The rest of the schema should be unchanged
|
81
|
+
orig_schema.delete('properties')
|
82
|
+
orig_schema.delete('allOf')
|
83
|
+
new_schema.delete('properties')
|
84
|
+
expect(new_schema).to eq(orig_schema)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative './spec_helper.rb'
|
2
|
+
|
3
|
+
describe 'Utils' do
|
4
|
+
context 'RAML Schema Generator' do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@raml_hash = example_raml_hash
|
8
|
+
@site = Jekyll::Site.new(Jekyll.configuration({
|
9
|
+
"skip_config_files" => true,
|
10
|
+
"json_schema_schema_uri" => "http://json-schema.org/draft-04/schema#"
|
11
|
+
}))
|
12
|
+
@rsg = Jekyll::RamlSchemaGenerator.new(@site, 'Simple Schema')
|
13
|
+
end
|
14
|
+
context '.generate_json_schema' do
|
15
|
+
it 'creates a JSON Schema string that includes all properties from application/x-www-form-urlencoded:formParameters' do
|
16
|
+
json_schema = JSON.parse(@rsg.generate_json_schema(@raml_hash['resources']['/test_resource']['post']) )
|
17
|
+
expect(json_schema['properties']).to eq(JSON.parse(simple_schema)['properties'])
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'creates a JSON Schema with a required attribute if application/x-www-form-urlencoded:formParameters includes a required parameter' do
|
21
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/x-www-form-urlencoded']['formParameters']['bar']['required'] = true
|
22
|
+
json_schema = JSON.parse(@rsg.generate_json_schema(@raml_hash['resources']['/test_resource']['post']))
|
23
|
+
expect(json_schema).to include('required')
|
24
|
+
expect(json_schema['required']).to include('bar')
|
25
|
+
expect(json_schema['properties']).to eq(JSON.parse(simple_schema)['properties'])
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'throws an error if provided RAML does not include body:application/x-www-form-urlencoded:formParameters' do
|
30
|
+
body = @raml_hash['resources']['/test_resource']['post'].delete('body')
|
31
|
+
expect{@rsg.generate_json_schema(@raml_hash)}.to raise_error(NoMethodError)
|
32
|
+
@raml_hash['resources']['/test_resource']['post']['body'] = body
|
33
|
+
form = @raml_hash['resources']['/test_resource']['post']['body'].delete('application/x-www-form-urlencoded')
|
34
|
+
expect{@rsg.generate_json_schema(@raml_hash)}.to raise_error
|
35
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/x-www-form-urlencoded'] = form
|
36
|
+
formParameters = @raml_hash['resources']['/test_resource']['post']['body']['application/x-www-form-urlencoded'].delete('formParameters')
|
37
|
+
expect{@rsg.generate_json_schema(@raml_hash)}.to raise_error
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '.insert_json_schema' do
|
42
|
+
it 'inserts an appropriate string into application/json:schema' do
|
43
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'] = {}
|
44
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
45
|
+
|
46
|
+
json_schema = @rsg.generate_json_schema(@raml_hash['resources']['/test_resource']['post'])
|
47
|
+
@rsg.insert_json_schema(@raml_hash['resources']['/test_resource']['post'], json_schema)
|
48
|
+
|
49
|
+
expect(@raml_hash['resources']['/test_resource']['post']['body']['application/json']).to include('schema')
|
50
|
+
expect(@raml_hash['resources']['/test_resource']['post']['body']['application/json']['schema']).to eq(json_schema)
|
51
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'].delete('schema')
|
52
|
+
expect(@raml_hash).to eq(orig_raml_hash)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'throws an error if provided object does not have expected properties' do
|
56
|
+
expect{@rsg.insert_json_schema(@raml_hash, 'foobar')}.to raise_error
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context '.insert_schemas' do
|
61
|
+
it 'inserts an appropriate JSON Schema string in application/json:schema' do
|
62
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'] = {}
|
63
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
64
|
+
@raml_hash = @rsg.insert_schemas(@raml_hash)
|
65
|
+
expect(@raml_hash['resources']['/test_resource']['post']['body']['application/json']).to include('schema')
|
66
|
+
expect(@raml_hash['resources']['/test_resource']['post']['body']['application/json']['schema']).to eq(pretty_json(simple_schema))
|
67
|
+
|
68
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'].delete('schema')
|
69
|
+
expect(@raml_hash).to eq(orig_raml_hash)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'does nothing if application/json:schema already exists' do
|
73
|
+
orig_schema = 'foobar'
|
74
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/json'] = { 'schema' => orig_schema }
|
75
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
76
|
+
@rsg.insert_schemas(@raml_hash)
|
77
|
+
|
78
|
+
expect(@raml_hash).to eq(orig_raml_hash)
|
79
|
+
expect(@raml_hash['resources']['/test_resource']['post']['body']['application/json']['schema']).to eq(orig_schema)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'does nothing if there is no application/x-www-form-urlencoded:formParameters' do
|
83
|
+
@raml_hash['resources']['/test_resource']['post']['body']['application/x-www-form-urlencoded'].delete('formParameters')
|
84
|
+
orig_raml_hash = DeepClone.clone @raml_hash
|
85
|
+
@rsg.insert_schemas(@raml_hash)
|
86
|
+
|
87
|
+
expect(@raml_hash).to eq(orig_raml_hash)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: jekyll-ramler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- GovDelivery
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-09 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: jekyll
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: "Generates Jekyll pages for overview, security, and \n resource
|
28
|
+
documentation specificed in a RAML file."
|
29
|
+
email: support@govdelivery.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- .travis.yml
|
35
|
+
- Gemfile
|
36
|
+
- Gemfile.lock
|
37
|
+
- LICENSE.md
|
38
|
+
- README.md
|
39
|
+
- jekyll-ramler.gemspec
|
40
|
+
- lib/jekyll-ramler.rb
|
41
|
+
- lib/raml-generate.rb
|
42
|
+
- lib/utils.rb
|
43
|
+
- spec/reference_page_generator_spec.rb
|
44
|
+
- spec/spec_assets/json/schema/allOf_schema.schema.json
|
45
|
+
- spec/spec_assets/json/schema/foo.include.schema.json
|
46
|
+
- spec/spec_assets/json/schema/ref_schema.schema.json
|
47
|
+
- spec/spec_assets/json/schema/simple_schema.schema.json
|
48
|
+
- spec/spec_assets/raml/simple.raml
|
49
|
+
- spec/spec_helper.rb
|
50
|
+
- spec/utils_json_schema_compiler_spec.rb
|
51
|
+
- spec/utils_raml_schema_generator_spec.rb
|
52
|
+
homepage: https://github.com/govdelivery/jekyll-ramler
|
53
|
+
licenses:
|
54
|
+
- BSD-3-Cluase
|
55
|
+
metadata: {}
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 2.0.14
|
73
|
+
signing_key:
|
74
|
+
specification_version: 4
|
75
|
+
summary: Jekyll plugin that generates API documentation pages based on RAML
|
76
|
+
test_files: []
|