mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
openapi pre, pre apibp conversion
This commit is contained in:
@@ -260,7 +260,7 @@ Updates the given group by applying the attributes provided in the body.
|
||||
}
|
||||
}
|
||||
|
||||
## Delete group [/api/v3/group/{id}]
|
||||
## Delete group [/api/v3/groups/{id}]
|
||||
|
||||
## Delete group [DELETE]
|
||||
|
||||
|
||||
@@ -135,6 +135,7 @@ import {
|
||||
slideToggleSelector
|
||||
} from "core-app/shared/components/slide-toggle/slide-toggle.component";
|
||||
import { BackupComponent, backupSelector } from "core-app/core/setup/globals/components/admin/backup.component";
|
||||
import { DocsComponent, docsSelector } from "core-app/core/setup/globals/components/docs/docs.component";
|
||||
import {
|
||||
EnterpriseBaseComponent,
|
||||
enterpriseBaseSelector,
|
||||
@@ -220,7 +221,8 @@ export const globalDynamicComponents:OptionalBootstrapDefinition[] = [
|
||||
{ selector: editableQueryPropsSelector, cls: EditableQueryPropsComponent },
|
||||
{ selector: slideToggleSelector, cls: SlideToggleComponent },
|
||||
{ selector: backupSelector, cls: BackupComponent },
|
||||
{ selector: opInAppNotificationBellSelector, cls: InAppNotificationBellComponent }
|
||||
{ selector: opInAppNotificationBellSelector, cls: InAppNotificationBellComponent },
|
||||
{ selector: docsSelector, cls: DocsComponent }
|
||||
];
|
||||
|
||||
|
||||
|
||||
+58
-2
@@ -1,7 +1,63 @@
|
||||
module API
|
||||
module OpenAPI
|
||||
def self.spec(version: :stable)
|
||||
API::OpenAPI::BlueprintImport.convert version: version
|
||||
extend self
|
||||
|
||||
def spec(version: :stable)
|
||||
spec_path = Rails.application.root.join("docs/api/apiv3/openapi-spec.yml")
|
||||
|
||||
if spec_path.exist?
|
||||
assemble_spec spec_path
|
||||
else
|
||||
API::OpenAPI::BlueprintImport.convert version: version, single_file: true
|
||||
end
|
||||
end
|
||||
|
||||
def assemble_spec(file_path)
|
||||
spec = YAML.load File.read(file_path.to_s)
|
||||
|
||||
substitute_refs(spec, path: file_path.parent, root_path: file_path.parent)
|
||||
end
|
||||
|
||||
def substitute_refs(spec, path:, root_path:, root_spec: spec)
|
||||
if spec.is_a?(Hash)
|
||||
if spec.size == 1 && spec.keys.first == "$ref"
|
||||
ref_path = path.join spec.values.first
|
||||
ref_value = YAML.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.map { |k, v| [k, substitute_refs(v, path: path, root_path: root_path, root_spec: root_spec)] }.to_h
|
||||
end
|
||||
elsif spec.is_a?(Array)
|
||||
spec.map { |s| substitute_refs s, path: path, root_path: root_path, root_spec: root_spec }
|
||||
else
|
||||
spec
|
||||
end
|
||||
end
|
||||
|
||||
def resolve_refs(spec, path:, root_path:, root_spec:)
|
||||
if spec.is_a?(Hash)
|
||||
if spec.size == 1 && spec.keys.first == "$ref"
|
||||
ref_path = spec.values.first
|
||||
|
||||
if ref_path.start_with?(".")
|
||||
schema_file = path.join(ref_path).to_s.sub(root_path.to_s, ".")
|
||||
schema_path = path.join(ref_path).parent.to_s.sub(root_path.to_s, "").split("/").drop(1)
|
||||
schema_name = root_spec.dig(*schema_path).find { |k, v| v["$ref"] == schema_file }.first
|
||||
schema_ref = path.join(ref_path).parent.join(schema_name).to_s.sub(root_path.to_s, "#")
|
||||
|
||||
{ spec.keys.first => schema_ref }
|
||||
else
|
||||
spec
|
||||
end
|
||||
else
|
||||
spec.map { |k, v| [k, resolve_refs(v, path: path, root_path: root_path, root_spec: root_spec)] }.to_h
|
||||
end
|
||||
elsif spec.is_a?(Array)
|
||||
spec.map { |v| resolve_refs(v, path: path, root_path: root_path, root_spec: root_spec) }
|
||||
else
|
||||
spec
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,7 +21,7 @@ module API
|
||||
@include_directive_regex ||= /\<\!\-\-\s*include\((.*)\)\s*\-\-\>/
|
||||
end
|
||||
|
||||
def convert(version: :stable)
|
||||
def convert(version: :stable, single_file: false)
|
||||
input_file = Rails.application.root.join("docs/api/apiv3-doc-#{version}.apib")
|
||||
md_file = Tempfile.new("apibp.md").path
|
||||
assemble_file input_path: input_file, output_path: md_file
|
||||
@@ -31,10 +31,119 @@ module API
|
||||
add_security! spec
|
||||
amend_schemas! spec, apibp: File.read(md_file)
|
||||
|
||||
if !single_file
|
||||
split_up_schemas! spec
|
||||
split_up_paths! spec
|
||||
split_up_tags! spec
|
||||
end
|
||||
|
||||
spec
|
||||
ensure
|
||||
FileUtils.rm_f md_file if File.exist? md_file
|
||||
end
|
||||
|
||||
def split_up_schemas!(spec)
|
||||
file_path = Rails.application.root.join "docs/api/apiv3/components/schemas"
|
||||
|
||||
FileUtils.mkdir_p file_path.to_s
|
||||
|
||||
new_schemas = spec["components"]["schemas"].map do |name, content|
|
||||
identifier = name.underscore
|
||||
file_name = "#{identifier}.yml"
|
||||
|
||||
File.open(file_path.join(file_name), "w") do |f|
|
||||
f.write "# Schema: #{name}\n"
|
||||
f.write content.to_yaml
|
||||
end
|
||||
|
||||
[name, { "$ref" => "./components/schemas/#{file_name}"}]
|
||||
end
|
||||
|
||||
spec["components"]["schemas"] = new_schemas.to_h
|
||||
end
|
||||
|
||||
def split_up_tags!(spec)
|
||||
file_path = Rails.application.root.join "docs/api/apiv3/tags"
|
||||
|
||||
FileUtils.mkdir_p file_path.to_s
|
||||
|
||||
new_tags = spec["tags"].map do |value|
|
||||
identifier = value["name"].downcase.gsub("&", "and").gsub(" ", "_")
|
||||
file_name = "#{identifier}.yml"
|
||||
|
||||
File.open(file_path.join(file_name), "w") do |f|
|
||||
f.write value.to_yaml
|
||||
end
|
||||
|
||||
{ "$ref" => "./tags/#{file_name}"}
|
||||
end
|
||||
|
||||
spec["tags"] = new_tags
|
||||
end
|
||||
|
||||
def split_up_paths!(spec)
|
||||
file_path = Rails.application.root.join "docs/api/apiv3/paths"
|
||||
|
||||
FileUtils.mkdir_p file_path.to_s
|
||||
|
||||
new_paths = spec["paths"].map do |path, content|
|
||||
segments = path.sub("/api/v3", "").split("/").reject(&:blank?)
|
||||
|
||||
(0..(segments.size - 1)).each do |i|
|
||||
if i > 0 && segments[i].end_with?("id}")
|
||||
before = segments[i - 1]
|
||||
after = before.singularize
|
||||
|
||||
# certain words like 'news' can't be singularized
|
||||
if before == after
|
||||
segments[i - 1] = "#{before}_item"
|
||||
else
|
||||
segments[i - 1] = after
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
identifier = segments.reject { |s| s.end_with?("id}") }.join("_").presence || "root"
|
||||
file_name = "#{identifier}.yml"
|
||||
|
||||
File.open(file_path.join(file_name), "w") do |f|
|
||||
f.write "# #{path}\n"
|
||||
f.write fix_operation_ids!(fix_references!(content.dup, context: spec)).to_yaml
|
||||
end
|
||||
|
||||
[path, { "$ref" => "./paths/#{file_name}"}]
|
||||
end
|
||||
|
||||
raise "Splitting up into paths failed! Expected same number of paths. " unless new_paths.size == spec["paths"].size
|
||||
|
||||
spec["paths"] = new_paths.to_h
|
||||
end
|
||||
|
||||
def fix_operation_ids!(spec)
|
||||
spec.each do |key, value|
|
||||
if value.is_a? Hash
|
||||
fix_operation_ids! value
|
||||
elsif key == "operationId"
|
||||
spec[key] = spec[key].gsub " ", "_"
|
||||
end
|
||||
end
|
||||
|
||||
spec
|
||||
end
|
||||
|
||||
def fix_references!(spec, context:)
|
||||
spec.each do |key, value|
|
||||
if value.is_a? Hash
|
||||
fix_references! value, context: context
|
||||
elsif value.is_a? Array
|
||||
spec[key] = value.map { |v| v.is_a?(Hash) ? fix_references!(v.dup, context: context) : v }
|
||||
elsif key == "$ref" && value.start_with?("#/components")
|
||||
spec[key] = '.' + context.dig(*(value.split("/").drop(1) + ['$ref']))
|
||||
end
|
||||
end
|
||||
|
||||
spec
|
||||
end
|
||||
|
||||
def add_security!(spec)
|
||||
spec["components"]["securitySchemes"] = {
|
||||
@@ -106,7 +215,6 @@ module API
|
||||
"href" => {
|
||||
"type" => "string",
|
||||
"nullable" => true,
|
||||
"format" => "uri",
|
||||
"description" => "URL to the referenced resource (might be relative)"
|
||||
},
|
||||
"title" => {
|
||||
@@ -273,7 +381,7 @@ module API
|
||||
|
||||
link = {}
|
||||
value = {
|
||||
"allOf" => [{ "$ref" => "#/components/schemas/Link" }, link]
|
||||
"allOf" => [{ "$ref" => "./link.yml" }, link]
|
||||
}
|
||||
|
||||
set_description! link, row, desc_index
|
||||
@@ -351,6 +459,17 @@ module API
|
||||
|
||||
add_conditions! value, row, cond_index
|
||||
|
||||
if type == "Formattable"
|
||||
value.delete "type"
|
||||
|
||||
value = {
|
||||
"allOf" => [
|
||||
{ "$ref" => "./formattable.yml" },
|
||||
value
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
[name, value]
|
||||
end
|
||||
|
||||
|
||||
+12
-2
@@ -49,9 +49,19 @@ namespace :api do
|
||||
desc 'Saves the API spec (OAS3.0) to ./docs/api/openproject-apiv3-<branch>.yml'
|
||||
task :update_spec, [:branch] => [:environment] do |task, args|
|
||||
branch = (args[:branch] || "stable").to_sym
|
||||
spec = API::OpenAPI::BlueprintImport.convert version: branch
|
||||
spec = API::OpenAPI::BlueprintImport.convert version: branch, single_file: false
|
||||
|
||||
File.open(Rails.application.root.join("docs/api/apiv3-oas-#{branch}.yml"), "w") do |f|
|
||||
File.open(Rails.application.root.join("docs/api/apiv3/openapi-spec.yml"), "w") do |f|
|
||||
f.write spec.to_yaml
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Saves the API spec (OAS3.0) to ./docs/api/openproject-apiv3-single.yml'
|
||||
task :assemble_spec, [:branch] => [:environment] do |task, args|
|
||||
branch = (args[:branch] || "stable").to_sym
|
||||
spec = API::OpenAPI.spec
|
||||
|
||||
File.open(Rails.application.root.join("docs/api/apiv3/openapi-spec-single.yml"), "w") do |f|
|
||||
f.write spec.to_yaml
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user