fix(repository-api): reject overlapping oneof literals
This commit is contained in:
@@ -404,6 +404,7 @@ fn render_schema(value: &Value, context: &str) -> Result<String, GenerationError
|
|||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let mut rendered = Vec::with_capacity(branches.len());
|
let mut rendered = Vec::with_capacity(branches.len());
|
||||||
|
let mut seen_literals = BTreeSet::new();
|
||||||
for branch in branches {
|
for branch in branches {
|
||||||
let branch_object = object(branch, context)?;
|
let branch_object = object(branch, context)?;
|
||||||
if !branch_object.contains_key("enum") && !branch_object.contains_key("const") {
|
if !branch_object.contains_key("enum") && !branch_object.contains_key("const") {
|
||||||
@@ -411,6 +412,32 @@ fn render_schema(value: &Value, context: &str) -> Result<String, GenerationError
|
|||||||
"{context} contains unsupported ambiguous oneOf semantics"
|
"{context} contains unsupported ambiguous oneOf semantics"
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
let literals = if let Some(constant) = branch_object.get("const") {
|
||||||
|
std::slice::from_ref(constant)
|
||||||
|
} else {
|
||||||
|
branch_object
|
||||||
|
.get("enum")
|
||||||
|
.and_then(Value::as_array)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
GenerationError::invalid(format!(
|
||||||
|
"{context} contains a non-array oneOf enum"
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
.as_slice()
|
||||||
|
};
|
||||||
|
for literal in literals {
|
||||||
|
render_literal(literal, context)?;
|
||||||
|
let normalized = serde_json::to_string(literal).map_err(|error| {
|
||||||
|
GenerationError::invalid(format!(
|
||||||
|
"failed to normalize {context} oneOf literal: {error}"
|
||||||
|
))
|
||||||
|
})?;
|
||||||
|
if !seen_literals.insert(normalized) {
|
||||||
|
return Err(GenerationError::invalid(format!(
|
||||||
|
"{context} contains overlapping oneOf literal branches"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
if let Some(kind) = outer_type {
|
if let Some(kind) = outer_type {
|
||||||
if !literal_schema_matches_type(branch_object, kind) {
|
if !literal_schema_matches_type(branch_object, kind) {
|
||||||
return Err(GenerationError::invalid(format!(
|
return Err(GenerationError::invalid(format!(
|
||||||
@@ -800,6 +827,14 @@ mod tests {
|
|||||||
.to_string()
|
.to_string()
|
||||||
.contains("oneOf branch does not match outer type")
|
.contains("oneOf branch does not match outer type")
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut document: Value = serde_json::from_str(OPENAPI).unwrap();
|
||||||
|
document["components"]["schemas"]["RepositorySourceKind"]["oneOf"]
|
||||||
|
.as_array_mut()
|
||||||
|
.unwrap()
|
||||||
|
.push(serde_json::json!({"const": "invalid", "type": "string"}));
|
||||||
|
let error = generate_repository_typescript(&document.to_string()).unwrap_err();
|
||||||
|
assert!(error.to_string().contains("overlapping oneOf literal"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user