Skip to content

Commit 71d1bf6

Browse files
authored
fix(config): recurse through schema refs when determining eligibility for unevaluated properties (vectordotdev#17150)
1 parent c3aa14f commit 71d1bf6

File tree

4 files changed

+64
-24
lines changed

4 files changed

+64
-24
lines changed

.github/actions/spelling/expect.txt

+1
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ MAINPID
723723
majorly
724724
MAKECMDGOALS
725725
Makefiles
726+
markability
726727
markdownify
727728
markdownlintrc
728729
marketo

lib/vector-config/src/schema/helpers.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{
1212
num::ConfigurableNumber, Configurable, ConfigurableRef, GenerateError, Metadata, ToValue,
1313
};
1414

15-
use super::visitors::{DisallowedUnevaluatedPropertiesVisitor, InlineSingleUseReferencesVisitor};
15+
use super::visitors::{DisallowUnevaluatedPropertiesVisitor, InlineSingleUseReferencesVisitor};
1616

1717
/// Applies metadata to the given schema.
1818
///
@@ -482,7 +482,7 @@ pub fn generate_internal_tagged_variant_schema(
482482
pub fn default_schema_settings() -> SchemaSettings {
483483
SchemaSettings::new()
484484
.with_visitor(InlineSingleUseReferencesVisitor::from_settings)
485-
.with_visitor(DisallowedUnevaluatedPropertiesVisitor::from_settings)
485+
.with_visitor(DisallowUnevaluatedPropertiesVisitor::from_settings)
486486
}
487487

488488
pub fn generate_root_schema<T>() -> Result<RootSchema, GenerateError>

lib/vector-config/src/schema/visitors/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ mod unevaluated;
77
pub(self) mod test;
88

99
pub use self::inline_single::InlineSingleUseReferencesVisitor;
10-
pub use self::unevaluated::DisallowedUnevaluatedPropertiesVisitor;
10+
pub use self::unevaluated::DisallowUnevaluatedPropertiesVisitor;

lib/vector-config/src/schema/visitors/unevaluated.rs

+60-21
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ use super::scoped_visit::{
2727
/// with advanced subschema validation, such as `oneOf` or `allOf`, as `unevaluatedProperties`
2828
/// cannot simply be applied to any and all schemas indiscriminately.
2929
#[derive(Debug, Default)]
30-
pub struct DisallowedUnevaluatedPropertiesVisitor {
30+
pub struct DisallowUnevaluatedPropertiesVisitor {
3131
scope_stack: SchemaScopeStack,
3232
eligible_to_flatten: HashMap<String, HashSet<SchemaReference>>,
3333
}
3434

35-
impl DisallowedUnevaluatedPropertiesVisitor {
35+
impl DisallowUnevaluatedPropertiesVisitor {
3636
pub fn from_settings(_: &SchemaSettings) -> Self {
3737
Self {
3838
scope_stack: SchemaScopeStack::default(),
@@ -41,7 +41,7 @@ impl DisallowedUnevaluatedPropertiesVisitor {
4141
}
4242
}
4343

44-
impl Visitor for DisallowedUnevaluatedPropertiesVisitor {
44+
impl Visitor for DisallowUnevaluatedPropertiesVisitor {
4545
fn visit_root_schema(&mut self, root: &mut RootSchema) {
4646
let eligible_to_flatten = build_closed_schema_flatten_eligibility_mappings(root);
4747

@@ -132,7 +132,7 @@ impl Visitor for DisallowedUnevaluatedPropertiesVisitor {
132132
}
133133
}
134134

135-
impl ScopedVisitor for DisallowedUnevaluatedPropertiesVisitor {
135+
impl ScopedVisitor for DisallowUnevaluatedPropertiesVisitor {
136136
fn push_schema_scope<S: Into<SchemaReference>>(&mut self, scope: S) {
137137
self.scope_stack.push(scope.into());
138138
}
@@ -237,12 +237,22 @@ fn build_closed_schema_flatten_eligibility_mappings(
237237
Schema::Object(schema) => schema,
238238
};
239239

240+
debug!(
241+
"Evaluating schema definition '{}' for markability.",
242+
definition_name
243+
);
244+
240245
// If a schema itself would not be considered markable, then we don't need to consider the
241246
// eligibility between parent/child since there's nothing to drive the "now unmark the child
242247
// schemas" logic.
243248
if !is_markable_schema(&root_schema.definitions, parent_schema) {
244249
debug!("Schema definition '{}' not markable.", definition_name);
245250
continue;
251+
} else {
252+
debug!(
253+
"Schema definition '{}' markable. Collecting referents.",
254+
definition_name
255+
);
246256
}
247257

248258
// Collect all referents for this definition, which includes both property-based referents
@@ -251,6 +261,13 @@ fn build_closed_schema_flatten_eligibility_mappings(
251261
let mut referents = HashSet::new();
252262
get_referents(parent_schema, &mut referents);
253263

264+
debug!(
265+
"Collected {} referents for '{}': {:?}",
266+
referents.len(),
267+
definition_name,
268+
referents
269+
);
270+
254271
// Store the parent/child mapping.
255272
parent_to_child.insert(SchemaReference::from(definition_name), referents);
256273
}
@@ -328,12 +345,14 @@ fn is_markable_schema(definitions: &Map<String, Schema>, schema: &SchemaObject)
328345
// subschemas: { "type": "null" } and { "$ref": "#/definitions/T" }. If the schema for `T` is,
329346
// say, just a scalar schema, instead of an object schema... then it wouldn't be marked, and in
330347
// turn, we wouldn't need to mark the schema for `Option<T>`: there's no properties at all.
331-
//
332-
// We'll follow schema references in subschemas up to one level deep trying to figure this out.
333348
if let Some(subschema) = schema.subschemas.as_ref() {
334349
let subschemas = get_object_subschemas_from_parent(subschema).collect::<Vec<_>>();
335350

336-
let has_object_subschema = subschemas.iter().any(|schema| is_object_schema(schema));
351+
debug!("{} subschemas detected.", subschemas.len());
352+
353+
let has_object_subschema = subschemas
354+
.iter()
355+
.any(|schema| is_markable_schema(definitions, schema));
337356
let has_referenced_object_subschema = subschemas
338357
.iter()
339358
.map(|subschema| {
@@ -342,13 +361,33 @@ fn is_markable_schema(definitions: &Map<String, Schema>, schema: &SchemaObject)
342361
.as_ref()
343362
.and_then(|reference| {
344363
let reference = get_cleaned_schema_reference(reference);
345-
definitions.get(reference)
364+
definitions.get_full(reference)
365+
})
366+
.and_then(|(_, name, schema)| schema.as_object().map(|schema| (name, schema)))
367+
.map_or(false, |(name, schema)| {
368+
debug!(
369+
"Following schema reference '{}' for subschema markability.",
370+
name
371+
);
372+
is_markable_schema(definitions, schema)
346373
})
347-
.and_then(Schema::as_object)
348-
.map_or(false, is_object_schema)
349374
})
350375
.any(identity);
351376

377+
debug!(
378+
"Schema {} object subschema(s) and {} referenced subschemas.",
379+
if has_object_subschema {
380+
"has"
381+
} else {
382+
"does not have"
383+
},
384+
if has_referenced_object_subschema {
385+
"has"
386+
} else {
387+
"does not have"
388+
},
389+
);
390+
352391
if has_object_subschema || has_referenced_object_subschema {
353392
return true;
354393
}
@@ -504,7 +543,7 @@ mod tests {
504543

505544
use crate::schema::visitors::test::{as_schema, assert_schemas_eq};
506545

507-
use super::DisallowedUnevaluatedPropertiesVisitor;
546+
use super::DisallowUnevaluatedPropertiesVisitor;
508547

509548
#[test]
510549
fn basic_object_schema() {
@@ -515,7 +554,7 @@ mod tests {
515554
}
516555
}));
517556

518-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
557+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
519558
visitor.visit_root_schema(&mut actual_schema);
520559

521560
let expected_schema = as_schema(json!({
@@ -543,7 +582,7 @@ mod tests {
543582
}
544583
}));
545584

546-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
585+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
547586
visitor.visit_root_schema(&mut actual_schema);
548587

549588
let expected_schema = as_schema(json!({
@@ -580,7 +619,7 @@ mod tests {
580619
}]
581620
}));
582621

583-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
622+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
584623
visitor.visit_root_schema(&mut actual_schema);
585624

586625
let expected_schema = as_schema(json!({
@@ -621,7 +660,7 @@ mod tests {
621660
}]
622661
}));
623662

624-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
663+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
625664
visitor.visit_root_schema(&mut actual_schema);
626665

627666
let expected_schema = as_schema(json!({
@@ -662,7 +701,7 @@ mod tests {
662701
}]
663702
}));
664703

665-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
704+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
666705
visitor.visit_root_schema(&mut actual_schema);
667706

668707
let expected_schema = as_schema(json!({
@@ -696,7 +735,7 @@ mod tests {
696735
}));
697736
let expected_schema = actual_schema.clone();
698737

699-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
738+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
700739
visitor.visit_root_schema(&mut actual_schema);
701740

702741
assert_schemas_eq(expected_schema, actual_schema);
@@ -712,7 +751,7 @@ mod tests {
712751
"additionalProperties": false
713752
}));
714753

715-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
754+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
716755
visitor.visit_root_schema(&mut actual_schema);
717756

718757
let expected_schema = as_schema(json!({
@@ -758,7 +797,7 @@ mod tests {
758797
}
759798
}));
760799

761-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
800+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
762801
visitor.visit_root_schema(&mut actual_schema);
763802

764803
let expected_schema = as_schema(json!({
@@ -813,7 +852,7 @@ mod tests {
813852
}
814853
}));
815854

816-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
855+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
817856
visitor.visit_root_schema(&mut actual_schema);
818857

819858
let expected_schema = as_schema(json!({
@@ -879,7 +918,7 @@ mod tests {
879918
}
880919
}));
881920

882-
let mut visitor = DisallowedUnevaluatedPropertiesVisitor::default();
921+
let mut visitor = DisallowUnevaluatedPropertiesVisitor::default();
883922
visitor.visit_root_schema(&mut actual_schema);
884923

885924
// Expectations:

0 commit comments

Comments
 (0)