Module: OpenapiRuby::Serving

Defined in:
lib/openapi_ruby/serving.rb

Overview

Host-neutral logic behind the served schema documents and the Swagger UI. The Rails controllers and RackApp are both thin shells over this, so the two hosts cannot drift apart.

Class Method Summary collapse

Class Method Details

.content_type_for(file_path) ⇒ Object



52
53
54
# File 'lib/openapi_ruby/serving.rb', line 52

def content_type_for(file_path)
  file_path.end_with?(".json") ? "application/json" : "application/x-yaml"
end

.gem_rootObject



76
77
78
# File 'lib/openapi_ruby/serving.rb', line 76

def gem_root
  File.expand_path("../..", __dir__)
end

.oauth2_redirect_fileObject



72
73
74
# File 'lib/openapi_ruby/serving.rb', line 72

def oauth2_redirect_file
  File.join(gem_root, "app", "views", "openapi_ruby", "oauth2_redirect.html")
end

.parse_content(file_path, content) ⇒ Object



56
57
58
59
60
61
62
# File 'lib/openapi_ruby/serving.rb', line 56

def parse_content(file_path, content)
  if file_path.end_with?(".json")
    JSON.parse(content)
  else
    YAML.safe_load(content, permitted_classes: [Date, Time])
  end
end

.schema_config_for(schema_name) ⇒ Object



14
15
16
# File 'lib/openapi_ruby/serving.rb', line 14

def schema_config_for(schema_name)
  OpenapiRuby.configuration.schemas[schema_name.to_sym]
end

.schema_document(schema_name, request: nil) ⇒ Object

Returns [content, content_type], or nil when the schema is unknown or has not been generated yet — callers turn that into a 404.

request is handed to the configured :openapi_filter untouched. Rails passes an ActionDispatch::Request and RackApp a Rack::Request; the hook only ever needs the shared Rack request API.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/openapi_ruby/serving.rb', line 34

def schema_document(schema_name, request: nil)
  schema_config = schema_config_for(schema_name)
  return nil unless schema_config

  file_path = schema_file_path(schema_name)
  return nil unless File.exist?(file_path)

  content = File.read(file_path)

  if schema_config[:openapi_filter]
    doc = parse_content(file_path, content)
    schema_config[:openapi_filter].call(doc, request)
    content = serialize_doc(file_path, doc)
  end

  [content, content_type_for(file_path)]
end

.schema_file_path(schema_name) ⇒ Object



18
19
20
21
22
# File 'lib/openapi_ruby/serving.rb', line 18

def schema_file_path(schema_name)
  config = OpenapiRuby.configuration
  ext = (config.schema_output_format == :json) ? "json" : "yaml"
  File.join(OpenapiRuby.app_root, config.schema_output_dir, "#{schema_name}.#{ext}")
end

.schema_formatObject



24
25
26
# File 'lib/openapi_ruby/serving.rb', line 24

def schema_format
  (OpenapiRuby.configuration.schema_output_format == :json) ? :json : :yaml
end

.schema_namesObject



10
11
12
# File 'lib/openapi_ruby/serving.rb', line 10

def schema_names
  OpenapiRuby.configuration.schemas.keys.map(&:to_s)
end

.schema_urls_js(schema_urls) ⇒ Object



121
122
123
124
125
126
127
# File 'lib/openapi_ruby/serving.rb', line 121

def schema_urls_js(schema_urls)
  if schema_urls.size > 1
    "urls: #{schema_urls.to_json}"
  else
    "url: \"#{schema_urls.first&.fetch(:url)}\""
  end
end

.serialize_doc(file_path, doc) ⇒ Object



64
65
66
67
68
69
70
# File 'lib/openapi_ruby/serving.rb', line 64

def serialize_doc(file_path, doc)
  if file_path.end_with?(".json")
    JSON.pretty_generate(doc)
  else
    doc.to_yaml
  end
end

.swagger_ui_html(schema_urls:, ui_config: {}) ⇒ Object

schema_urls is an array of name: — the caller builds them, since only it knows how the docs are mounted.



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/openapi_ruby/serving.rb', line 82

def swagger_ui_html(schema_urls:, ui_config: {})
  <<~HTML
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>#{ui_config[:title] || "API Documentation"}</title>
      <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css">
      <style>
        html { box-sizing: border-box; overflow-y: scroll; }
        *, *:before, *:after { box-sizing: inherit; }
        body { margin: 0; background: #fafafa; }
      </style>
    </head>
    <body>
      <div id="swagger-ui"></div>
      <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
      <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-standalone-preset.js"></script>
      <script>
        SwaggerUIBundle({
          #{schema_urls_js(schema_urls)},
          dom_id: '#swagger-ui',
          deepLinking: true,
          presets: [
            SwaggerUIBundle.presets.apis,
            SwaggerUIStandalonePreset
          ],
          plugins: [
            SwaggerUIBundle.plugins.DownloadUrl
          ],
          layout: "#{(schema_urls.size > 1) ? "StandaloneLayout" : "BaseLayout"}",
          #{ui_config_js(ui_config)}
        });
      </script>
    </body>
    </html>
  HTML
end

.ui_config_js(ui_config) ⇒ Object



129
130
131
132
133
# File 'lib/openapi_ruby/serving.rb', line 129

def ui_config_js(ui_config)
  ui_config.except(:title).map { |k, v|
    "#{k}: #{v.to_json}"
  }.join(",\n          ")
end