require "erb" require "json" module GovukTechDocs module ApiReference class Renderer def initialize(app, document) @app = app @document = document @redcarpet = build_redcarpet(app) # 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 = @document.paths.keys.inject("") do |memo, text| memo + path(text) end schema_names = @document.components.schemas.keys schemas = schema_names.inject("") do |memo, schema_name| memo + schema(schema_name) 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(title) schema = @document.components.schemas[title] return unless schema properties = if schema.all_of schema.all_of.each_with_object({}) do |nested, memo| memo.merge!(nested.properties.to_h) end else {} end properties.merge!(schema.properties.to_h) @template_schema.result(binding) end def schemas_from_path(text) operations = get_operations(@document.paths[text]) schemas = operations.flat_map do |_, operation| operation.responses.inject([]) do |memo, (_, response)| next memo unless response.content["application/json"] schema = response.content["application/json"].schema memo << schema.name if schema.name memo + schemas_from_schema(schema) end end # Render all referenced schemas output = schemas.uniq.inject("") do |memo, schema_name| memo + schema(schema_name) end output.prepend('

Schemas

') unless output.empty? output end def schemas_from_schema(schema) schemas = schema.properties.map(&:last) schemas << schema.items if schema.items && schema.type == "array" schemas += schema.all_of.to_a.flat_map { |s| s.properties.map(&:last) } schemas.flat_map do |nested| sub_schemas = schemas_from_schema(nested) nested.name ? [nested.name] + sub_schemas : sub_schemas end end def operations(path, path_id) get_operations(path).inject("") do |memo, (key, operation)| id = "#{path_id}-#{key.parameterize}" parameters = parameters(operation, id) responses = responses(operation, id) memo + @template_operation.result(binding) end end def parameters(operation, operation_id) parameters = operation.parameters id = "#{operation_id}-parameters" @template_parameters.result(binding) end def responses(operation, operation_id) responses = operation.responses id = "#{operation_id}-responses" @template_responses.result(binding) 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 = schema_data.properties.to_h schema_data.all_of.to_a.each do |all_of_schema| properties.merge!(all_of_schema.properties.to_h) end properties.transform_values do |schema| case schema.type when "object" schema_properties(schema.items || schema) when "array" schema.items ? [schema_properties(schema.items)] : [] else schema.example || schema.type end end end private def build_redcarpet(app) renderer = GovukTechDocs::TechDocsHTMLRenderer.new(context: app.config_context) Redcarpet::Markdown.new(renderer) 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) { "get" => path.get, "put" => path.put, "post" => path.post, "delete" => path.delete, "patch" => path.patch, }.compact end def get_schema_link(schema) return unless schema.name id = "schema-#{schema.name.parameterize}" "#{schema.name}" end def render_markdown(text) @redcarpet.render(text) if text end end end end