OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
openproject/lib/api/open_api.rb

127 lines
3.8 KiB

module API
module OpenAPI
extend self
def spec
spec = if Rails.env.development?
load_spec
else
memoized_spec
end
spec["servers"] = [
{
"description" => "This server",
"url" => "#{Setting.protocol}://#{Setting.host_name}/"
}
]
spec
end
def assemble_spec(file_path)
spec = YAML.safe_load File.read(file_path.to_s)
substitute_refs(spec, path: file_path.parent, root_path: file_path.parent)
rescue Psych::SyntaxError => e
raise "Failed to load #{file_path}: #{e.class} #{e.message}"
end
private
def memoized_spec
@memoized_spec ||= load_spec
end
def load_spec
spec_path = Rails.application.root.join("docs/api/apiv3/openapi-spec.yml")
if spec_path.exist?
assemble_spec spec_path
else
raise "Could not find openapi-spec.yml under #{spec_path}"
end
end
def substitute_refs(spec, path:, root_path:, root_spec: spec)
case spec
when Hash
substitute_refs_in_hash spec, path: path, root_path: root_path, root_spec: root_spec
when Array
spec.map { |s| substitute_refs s, path: path, root_path: root_path, root_spec: root_spec }
else
spec
end
end
def substitute_refs_in_hash(spec, path:, root_path:, root_spec: spec)
if spec.size == 1 && spec.keys.first == "$ref"
ref_path = path.join spec.values.first
ref_value = YAML.safe_load File.read(ref_path.to_s)
resolve_refs ref_value, path: ref_path.parent, root_path: root_path, root_spec: root_spec
else
spec.transform_values { |v| substitute_refs(v, path: path, root_path: root_path, root_spec: root_spec) }
end
rescue Psych::SyntaxError => e
raise "Failed to load #{ref_path}: #{e.class} #{e.message}"
end
def resolve_refs(spec, path:, root_path:, root_spec:)
case spec
when Hash
resolve_refs_in_hash spec, path: path, root_path: root_path, root_spec: root_spec
when Array
spec.map { |v| resolve_refs v, path: path, root_path: root_path, root_spec: root_spec }
else
spec
end
end
def resolve_refs_in_hash(spec, path:, root_path:, root_spec:)
if spec.size == 1 && spec.keys.first == "$ref"
resolve_ref spec, path: path, root_path: root_path, root_spec: root_spec
else
spec.transform_values { |v| resolve_refs v, path: path, root_path: root_path, root_spec: root_spec }
end
end
def resolve_ref(spec, path:, root_path:, root_spec:)
ref_path = spec.values.first
if ref_path.start_with?(".")
{ spec.keys.first => schema_ref(ref_path, path: path, root_path: root_path, root_spec: root_spec) }
else
spec
end
end
def schema_ref(ref_path, path:, root_path:, root_spec:)
name = schema_name ref_path, path: path, root_path: root_path, root_spec: root_spec
path.join(ref_path).parent.join(name).to_s.sub(root_path.to_s, "#")
end
def schema_file(ref_path, path:, root_path:)
path.join(ref_path).to_s.sub root_path.to_s, "."
end
def schema_path(ref_path, path:, root_path:)
path.join(ref_path).parent.to_s.sub(root_path.to_s, "").split("/").drop 1
end
def schema_name(ref_path, path:, root_path:, root_spec:)
file = schema_file ref_path, path: path, root_path: root_path
spec_path = schema_path ref_path, path: path, root_path: root_path
spec_files = root_spec.dig(*spec_path)
raise "Path not defined #{spec_path}" unless spec_files
spec_file = spec_files.find { |_k, v| v["$ref"] == file }&.first
raise "Reference '#{file}' not valid within #{spec_path}" unless spec_file
spec_file
end
end
end