-
Notifications
You must be signed in to change notification settings - Fork 145
Add Paths in OpenAPI generator #1297
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
def get_model_class(klass) | ||
case klass | ||
when String | ||
begin | ||
klass.constantize | ||
rescue NameError => e | ||
puts "Warning: Could not constantize #{klass}: #{e.message}" | ||
nil | ||
end | ||
when Class | ||
klass | ||
else | ||
nil | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like it could be just klass = klass.safe_constantize if klass.kind_of?(String)
If we don't need to handle Class or String
and only String
then we could use safe_constantize directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NOTE I threw a breakpoint in else
and Class
and no instances of that so I think we can do collection.klass&.safe_constantize
below
"properties" => { | ||
"name" => {"type" => "string", "example" => collection_name}, | ||
"count" => {"type" => "integer", "description" => "Total number of resources"}, | ||
"subcount" => {"type" => "integer", "description" => "Number of resources returned"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor
"subcount" => {"type" => "integer", "description" => "Number of resources returned"}, | |
"subcount" => {"type" => "integer", "description" => "Number of resources returned after filters applied"}, |
"type" => "object", | ||
"properties" => { | ||
"name" => {"type" => "string", "example" => collection_name}, | ||
"count" => {"type" => "integer", "description" => "Total number of resources"}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"count" => {"type" => "integer", "description" => "Total number of resources"}, | |
"count" => {"type" => "integer", "description" => "Total number of resources without filters applied"}, |
"404" => {"description" => "Resource not found"}, | ||
"401" => {"description" => "Unauthorized"}, | ||
"403" => {"description" => "Forbidden"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very minor, but let's sort these.
"404" => {"description" => "Resource not found"}, | |
"401" => {"description" => "Unauthorized"}, | |
"403" => {"description" => "Forbidden"} | |
"401" => {"description" => "Unauthorized"}, | |
"403" => {"description" => "Forbidden"}, | |
"404" => {"description" => "Resource not found"} |
collection.respond_to?(:collection_actions) && | ||
collection.collection_actions&.key?(action) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
collection.respond_to?(:collection_actions) && | |
collection.collection_actions&.key?(action) | |
collection.respond_to?(:collection_actions) && | |
collection.collection_actions&.key?(action) |
collection.respond_to?(:resource_actions) && | ||
collection.resource_actions&.key?(action) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
collection.respond_to?(:resource_actions) && | |
collection.resource_actions&.key?(action) | |
collection.respond_to?(:resource_actions) && | |
collection.resource_actions&.key?(action) |
def build_update_schema(model_class, schema_name) | ||
properties = build_writable_properties(model_class) | ||
# Make all properties optional for PATCH | ||
properties.each { |_, prop| prop.delete("required") if prop.is_a?(Hash) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
properties.each { |_, prop| prop.delete("required") if prop.is_a?(Hash) } | |
properties.each_value { |prop| prop.delete("required") if prop.is_a?(Hash) } |
if collection.respond_to?(:resource_actions) && collection.resource_actions | ||
collection.resource_actions.keys | ||
else | ||
[] | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if collection.respond_to?(:resource_actions) && collection.resource_actions | |
collection.resource_actions.keys | |
else | |
[] | |
end | |
collection.try(:resource_actions)&.keys || [] |
next unless collection.klass | ||
|
||
# Skip if we can't get a valid model class | ||
model_class = get_model_class(collection.klass) | ||
next unless model_class |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is always a string we could do
model_class = collection.klass&.safe_constantize
next if model_class.nil?
"subcoll_id" => { | ||
"name" => "subcoll_id", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be spelled out here.
"subcoll_id" => { | |
"name" => "subcoll_id", | |
"subcollection_id" => { | |
"name" => "subcollection_id", |
# POST collection - if creation is supported | ||
if supports_collection_action?(collection, :post) | ||
operations["post"] = { | ||
"summary" => "Create #{collection_name.to_s.singularize}", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we need to check the collection_actions
here because not every POST
on the collection creates the resource.
Example: VMs has a lot of collection_actions but none of them are :name: create
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @agrare, I request your input on this to proceed further.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could be similar to the resource_actions you have down here https://github.com/ManageIQ/manageiq-api/pull/1297/files#diff-d14d325d93f1febc7f776ea58816bce4c5b74cd4b3c09b883a2dd2c82889857bR289
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like this @agrare?,
# POST collection - only if creation is supported
if collection.collection_actions&.key?(:create)
operations["post"] = {
"summary" => "Create #{collection_name.to_s.singularize}",
"description" => "Create a new #{collection_name.to_s.singularize}",
"requestBody" => {
"required" => true,
"content" => {
"application/json" => {
"schema" => build_create_schema(model_class, schema_name)
}
}
},
"responses" => {
"201" => {
"description" => "Resource created successfully",
"content" => {
"application/json" => {
"schema" => {"$ref" => "##{SCHEMAS_PATH}/#{schema_name}"}
}
}
},
"400" => {"description" => "Bad Request - Invalid input"},
"401" => {"description" => "Unauthorized"},
"403" => {"description" => "Forbidden"},
"422" => {"description" => "Unprocessable Entity - Validation errors"}
},
"tags" => [collection_name.to_s.titleize]
}
end
# Document other collection_actions (excluding :create) as POSTs
collection.collection_actions&.each do |action, config|
next if action == :create
operations["post_#{action}"] = {
"summary" => "Invoke #{action.to_s.titleize} on #{collection_name}",
"description" => "Perform the #{action} action on the #{collection_name} collection.",
"requestBody" => {
"required" => true,
"content" => {
"application/json" => {
"schema" => {
"type" => "object",
"required" => ["action"],
"properties" => {
"action" => {
"type" => "string",
"enum" => [action.to_s],
"description" => "Name of the collection action"
},
# Optionally add custom params from config if needed
}
}
}
}
},
"responses" => {
"200" => {"description" => "Action performed successfully"},
"400" => {"description" => "Bad Request - Invalid action or parameters"},
"401" => {"description" => "Unauthorized"},
"403" => {"description" => "Forbidden"},
"422" => {"description" => "Action cannot be performed"}
},
"tags" => [collection_name.to_s.titleize]
}
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to special case create there or can we treat it like all of the other collection actions?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes @agrare, We can handle create the same way as the other collection actions
This is an awesome PR @dhamo-ror. Thank you! Can you add the generated openapi.yml to this PR as well, which is the result of running these updates? That way we can also see the final result. Also, not necessarily for this PR, but in the future we also need OPTIONS support for the endpoints, and we need to also add the |
Thanks @Fryguy for your review. I’ll address the comments, update the PR and share the YAML file as well. I also plan to include all actions and endpoints in this, so please share your inputs if I’ve missed anything. |
Hi @Fryguy, I’m attaching the JSON file generated for the API. Please take a look and share your feedback. |
@dhamo-ror It should just be a part of this PR is what I mean. We have the results of running the generator in https://github.com/ManageIQ/manageiq-api/blob/master/config/openapi.json |
Understood @Fryguy, Once we finalize the |
Hi @agrare, could you kindly review this PR and let me know your suggestions.