require 'erb'
require 'json'
module GovukTechDocs
module ApiReference
class Renderer
def initialize(app, document)
@app = app
@document = document
# Load template files
@template_api_full = get_renderer('api_reference_full.html.erb')
@template_path = get_renderer('path.html.erb')
@template_schema = get_renderer('schema.html.erb')
@template_operation = get_renderer('operation.html.erb')
@template_parameters = get_renderer('parameters.html.erb')
@template_responses = get_renderer('responses.html.erb')
end
def api_full(info, servers)
paths = ''
paths_data = @document.paths
paths_data.each do |path_data|
# For some reason paths.each returns an array of arrays [title, object]
# instead of an array of objects
text = path_data[0]
paths += path(text)
end
schemas = ''
schemas_data = @document.components.schemas
schemas_data.each do |schema_data|
text = schema_data[0]
schemas += schema(text)
end
@template_api_full.result(binding)
end
def path(text)
path = @document.paths[text]
id = text.parameterize
operations = operations(path, id)
@template_path.result(binding)
end
def schema(text)
schemas = ''
schemas_data = @document.components.schemas
schemas_data.each do |schema_data|
all_of = schema_data[1]["allOf"]
properties = []
if !all_of.blank?
all_of.each do |schema_nested|
schema_nested.properties.each do |property|
properties.push property
end
end
end
schema_data[1].properties.each do |property|
properties.push property
end
if schema_data[0] == text
title = schema_data[0]
schema = schema_data[1]
return @template_schema.result(binding)
end
end
end
def schemas_from_path(text)
path = @document.paths[text]
operations = get_operations(path)
# Get all referenced schemas
schemas = []
operations.compact.each_value do |operation|
responses = operation.responses
responses.each do |_rkey, response|
if response.content['application/json']
schema = response.content['application/json'].schema
schema_name = get_schema_name(schema.node_context.source_location.to_s)
if !schema_name.nil?
schemas.push schema_name
end
schemas.concat(schemas_from_schema(schema))
end
end
end
# Render all referenced schemas
output = ''
schemas.uniq.each do |schema_name|
output += schema(schema_name)
end
if !output.empty?
output.prepend('
Schemas
')
end
output
end
def schemas_from_schema(schema)
schemas = []
properties = []
schema.properties.each do |property|
properties.push property[1]
end
if schema.type == 'array'
properties.push schema.items
end
all_of = schema["allOf"]
if !all_of.blank?
all_of.each do |schema_nested|
schema_nested.properties.each do |property|
properties.push property[1]
end
end
end
properties.each do |property|
# Must be a schema be referenced by another schema
# And not a property of a schema
if property.node_context.referenced_by.to_s.include?('#/components/schemas') &&
!property.node_context.source_location.to_s.include?('/properties/')
schema_name = get_schema_name(property.node_context.source_location.to_s)
end
if !schema_name.nil?
schemas.push schema_name
end
# Check sub-properties for references
schemas.concat(schemas_from_schema(property))
end
schemas
end
def operations(path, path_id)
output = ''
operations = get_operations(path)
operations.compact.each do |key, operation|
id = "#{path_id}-#{key.parameterize}"
parameters = parameters(operation, id)
responses = responses(operation, id)
output += @template_operation.result(binding)
end
output
end
def parameters(operation, operation_id)
parameters = operation.parameters
id = "#{operation_id}-parameters"
output = @template_parameters.result(binding)
output
end
def responses(operation, operation_id)
responses = operation.responses
id = "#{operation_id}-responses"
output = @template_responses.result(binding)
output
end
def markdown(text)
if text
Tilt['markdown'].new(context: @app) { text }.render
end
end
def json_output(schema)
properties = schema_properties(schema)
JSON.pretty_generate(properties)
end
def json_prettyprint(data)
JSON.pretty_generate(data)
end
def schema_properties(schema_data)
properties = Hash.new
if defined? schema_data.properties
schema_data.properties.each do |key, property|
properties[key] = property
end
end
properties.merge! get_all_of_hash(schema_data)
properties_hash = Hash.new
properties.each do |pkey, property|
if property.type == 'object'
properties_hash[pkey] = Hash.new
items = property.items
if !items.blank?
properties_hash[pkey] = schema_properties(items)
end
if !property.properties.blank?
properties_hash[pkey] = schema_properties(property)
end
elsif property.type == 'array'
properties_hash[pkey] = Array.new
items = property.items
if !items.blank?
properties_hash[pkey].push schema_properties(items)
end
else
properties_hash[pkey] = !property.example.nil? ? property.example : property.type
end
end
properties_hash
end
private
def get_all_of_array(schema)
properties = Array.new
if schema.is_a?(Array)
schema = schema[1]
end
if schema["allOf"]
all_of = schema["allOf"]
end
if !all_of.blank?
all_of.each do |schema_nested|
schema_nested.properties.each do |property|
if property.is_a?(Array)
property = property[1]
end
properties.push property
end
end
end
properties
end
def get_all_of_hash(schema)
properties = Hash.new
if schema["allOf"]
all_of = schema["allOf"]
end
if !all_of.blank?
all_of.each do |schema_nested|
schema_nested.properties.each do |key, property|
properties[key] = property
end
end
end
properties
end
def get_renderer(file)
template_path = File.join(File.dirname(__FILE__), 'templates/' + file)
template = File.open(template_path, 'r').read
ERB.new(template)
end
def get_operations(path)
operations = {}
operations['get'] = path.get if defined? path.get
operations['put'] = path.put if defined? path.put
operations['post'] = path.post if defined? path.post
operations['delete'] = path.delete if defined? path.delete
operations['patch'] = path.patch if defined? path.patch
operations
end
def get_schema_name(text)
unless text.is_a?(String)
return nil
end
# Schema dictates that it's always components['schemas']
text.gsub(/#\/components\/schemas\//, '')
end
def get_schema_link(schema)
schema_name = get_schema_name schema.node_context.source_location.to_s
if !schema_name.nil?
id = "schema-#{schema_name.parameterize}"
output = "#{schema_name}"
output
end
end
end
end
end