Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions lib/openapi3_parser/node_factory/paths.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def build_node(data, node_context)
def validate(validatable)
paths = validatable.input.keys.grep_v(NodeFactory::EXTENSION_REGEX)
validate_paths(validatable, paths)
validate_operation_ids(validatable)
end

def validate_paths(validatable, paths)
Expand Down Expand Up @@ -66,6 +67,31 @@ def conflicting_paths(paths)

grouped_paths.select { |group| group.size > 1 }.flatten
end

def validate_operation_ids(validatable)
operation_ids = []

validatable.input.each_value do |path_item|
next unless path_item.is_a?(Hash)

%w[get put post delete options head patch trace].each do |method|
operation = path_item[method]
next unless operation.is_a?(Hash)

operation_id = operation["operationId"]
operation_ids << operation_id if operation_id
end
end

counts = operation_ids.each_with_object(Hash.new(0)) { |id, hash| hash[id] += 1 }
dupes = counts.select { |_id, count| count > 1 }.keys

return if dupes.empty?

validatable.add_error(
"Duplicate operationId values: #{dupes.sort.join(', ')}"
)
end
end
end
end
72 changes: 72 additions & 0 deletions spec/lib/openapi3_parser/node_factory/openapi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,78 @@
end
end

describe "validating operationIds" do
it "is valid when operationIds are unique" do
factory_context = create_node_factory_context(
minimal_openapi_definition.merge(
"paths" => {
"/pets" => {
"get" => {
"operationId" => "listPets",
"responses" => { "200" => { "description" => "Success" } }
}
},
"/pets/{id}" => {
"get" => {
"operationId" => "getPet",
"responses" => { "200" => { "description" => "Success" } }
}
}
}
)
)
expect(described_class.new(factory_context)).to be_valid
end

it "is valid when some operations have no operationId" do
factory_context = create_node_factory_context(
minimal_openapi_definition.merge(
"paths" => {
"/pets" => {
"get" => {
"operationId" => "listPets",
"responses" => { "200" => { "description" => "Success" } }
}
},
"/pets/{id}" => {
"get" => {
"responses" => { "200" => { "description" => "Success" } }
}
}
}
)
)
expect(described_class.new(factory_context)).to be_valid
end

it "is invalid when operationIds are duplicated" do
factory_context = create_node_factory_context(
minimal_openapi_definition.merge(
"paths" => {
"/pets" => {
"get" => {
"operationId" => "getPets",
"responses" => { "200" => { "description" => "Success" } }
}
},
"/pets/{id}" => {
"get" => {
"operationId" => "getPets",
"responses" => { "200" => { "description" => "Success" } }
}
}
}
)
)

instance = described_class.new(factory_context)
expect(instance).not_to be_valid
expect(instance)
.to have_validation_error("#/paths")
.with_message("Duplicate operationId values: getPets")
end
end

describe "default values for servers" do
it "contains a basic root server when servers input is nil" do
node = create_node(minimal_openapi_definition.merge({ "servers" => nil }))
Expand Down