Skip to content

Commit e2d6037

Browse files
committed
Introduce the ability to define namespaces #802
1 parent 152c886 commit e2d6037

11 files changed

Lines changed: 265 additions & 5 deletions

File tree

crates/weaver_forge/src/registry.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ use weaver_resolved_schema::lineage::GroupLineage;
1414
use weaver_resolved_schema::registry::{Group, Registry};
1515
use weaver_semconv::any_value::AnyValueSpec;
1616
use weaver_semconv::deprecated::Deprecated;
17+
use weaver_semconv::footer::FooterSpec;
1718
use weaver_semconv::group::{GroupType, InstrumentSpec, SpanKindSpec};
19+
use weaver_semconv::header::HeaderSpec;
1820
use weaver_semconv::stability::Stability;
1921
use weaver_semconv::YamlValue;
2022

@@ -116,6 +118,12 @@ pub struct ResolvedGroup {
116118
#[serde(default)]
117119
#[serde(skip_serializing_if = "Vec::is_empty")]
118120
pub entity_associations: Vec<String>,
121+
/// Header for the namespace/page.
122+
#[serde(skip_serializing_if = "Option::is_none")]
123+
pub header: Option<HeaderSpec>,
124+
/// Footer for the namespace/page.
125+
#[serde(skip_serializing_if = "Option::is_none")]
126+
pub footer: Option<FooterSpec>,
119127
/// Annotations for the group.
120128
#[serde(default)]
121129
#[serde(skip_serializing_if = "Option::is_none")]
@@ -172,6 +180,8 @@ impl ResolvedGroup {
172180
lineage,
173181
display_name: group.display_name.clone(),
174182
body: group.body.clone(),
183+
footer: group.footer.clone(),
184+
header: group.header.clone(),
175185
entity_associations: group.entity_associations.clone(),
176186
annotations: group.annotations.clone(),
177187
})
@@ -233,6 +243,8 @@ impl ResolvedRegistry {
233243
lineage,
234244
display_name: group.display_name.clone(),
235245
body: group.body.clone(),
246+
footer: group.footer.clone(),
247+
header: group.header.clone(),
236248
entity_associations: group.entity_associations.clone(),
237249
annotations: group.annotations.clone(),
238250
}

crates/weaver_resolved_schema/src/registry.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,12 @@ use crate::catalog::Catalog;
1313
use crate::error::{handle_errors, Error};
1414
use crate::lineage::GroupLineage;
1515
use crate::registry::GroupStats::{
16-
AttributeGroup, Entity, Event, Metric, MetricGroup, Scope, Span, Undefined,
16+
AttributeGroup, Entity, Event, Metric, MetricGroup, Scope, Span, Undefined, NameSpace
1717
};
1818
use serde::{Deserialize, Serialize};
1919
use weaver_semconv::deprecated::Deprecated;
20+
use weaver_semconv::footer::FooterSpec;
21+
use weaver_semconv::header::HeaderSpec;
2022
use weaver_semconv::group::{GroupType, InstrumentSpec, SpanKindSpec};
2123
use weaver_semconv::provenance::Provenance;
2224
use weaver_semconv::stability::Stability;
@@ -127,6 +129,12 @@ pub struct Group {
127129
/// This fields is only used for event groups.
128130
#[serde(skip_serializing_if = "Option::is_none")]
129131
pub body: Option<AnyValueSpec>,
132+
/// Header for the namespace/page.
133+
#[serde(skip_serializing_if = "Option::is_none")]
134+
pub header: Option<HeaderSpec>,
135+
/// Footer for the namespace/page.
136+
#[serde(skip_serializing_if = "Option::is_none")]
137+
pub footer: Option<FooterSpec>,
130138
/// Annotations for the group.
131139
#[serde(default)]
132140
#[serde(skip_serializing_if = "Option::is_none")]
@@ -155,6 +163,7 @@ impl Group {
155163
GroupType::MetricGroup => None,
156164
GroupType::Entity => self.name.as_deref(),
157165
GroupType::Scope => None,
166+
GroupType::NameSpace => self.name.as_deref(),
158167
GroupType::Undefined => None,
159168
}
160169
}
@@ -218,6 +227,11 @@ pub enum GroupStats {
218227
/// Common statistics for this type of group.
219228
common_stats: CommonGroupStats,
220229
},
230+
/// Statistics for a namespace.
231+
NameSpace {
232+
/// Common statistics for this type of group.
233+
common_stats: CommonGroupStats,
234+
},
221235
/// Statistics for a span.
222236
Span {
223237
/// Common statistics for this type of group.
@@ -307,6 +321,9 @@ impl Registry {
307321
GroupType::Scope => Scope {
308322
common_stats: CommonGroupStats::default(),
309323
},
324+
GroupType::NameSpace => NameSpace {
325+
common_stats: CommonGroupStats::default(),
326+
},
310327
GroupType::Span => Span {
311328
common_stats: CommonGroupStats::default(),
312329
span_kind_breakdown: HashMap::new(),
@@ -359,6 +376,9 @@ impl Registry {
359376
Scope { common_stats } => {
360377
common_stats.update_stats(group);
361378
}
379+
NameSpace { common_stats } => {
380+
common_stats.update_stats(group);
381+
}
362382
Span {
363383
common_stats,
364384
span_kind_breakdown,

crates/weaver_resolver/src/registry.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,8 @@ fn group_from_spec(group: GroupSpecWithProvenance) -> UnresolvedGroup {
397397
lineage: Some(GroupLineage::new(group.provenance.clone())),
398398
display_name: group.spec.display_name,
399399
body: group.spec.body,
400+
header: group.spec.header,
401+
footer: group.spec.footer,
400402
annotations: group.spec.annotations,
401403
entity_associations: group.spec.entity_associations,
402404
},
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
groups:
2+
- id: namespace.gen-ai
3+
type: namespace
4+
name: gen-ai
5+
note: This is a description of the gen ai namespace
6+
brief: This is a summary of the gen ai namespace
7+
header:
8+
title: Migration strategy
9+
content: To protect personl information the following document should be followed.
10+
style: warning
11+
footer:
12+
content: Footer
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
//! Stability specification.
4+
5+
use schemars::JsonSchema;
6+
use serde::{Deserialize, Serialize};
7+
use crate::style::Style;
8+
9+
/// A header specification
10+
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)]
11+
#[serde(deny_unknown_fields)]
12+
pub struct FooterSpec {
13+
14+
/// The title of the footer.
15+
pub title: Option<String>,
16+
/// The content in the footer.
17+
pub content: String,
18+
/// The style of the footer.
19+
pub style: Option<Style>,
20+
}
21+
22+
impl FooterSpec {
23+
/// returns the title of the footer
24+
#[must_use]
25+
fn title(&self) -> &Option<String> {
26+
&self.title
27+
}
28+
/// returns the content of the footer
29+
#[must_use]
30+
fn content(&self) -> &String {
31+
&self.content
32+
}
33+
/// returns the style of the footer
34+
#[must_use]
35+
fn style(&self) -> &Option<Style> {
36+
&self.style
37+
}
38+
}

crates/weaver_semconv/src/group.rs

Lines changed: 63 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ use std::fmt::{Display, Formatter};
1313
use crate::any_value::AnyValueSpec;
1414
use crate::attribute::{AttributeSpec, AttributeType, PrimitiveOrArrayTypeSpec};
1515
use crate::deprecated::Deprecated;
16+
use crate::footer::FooterSpec;
1617
use crate::group::InstrumentSpec::{Counter, Gauge, Histogram, UpDownCounter};
18+
use crate::header::HeaderSpec;
1719
use crate::provenance::Provenance;
1820
use crate::semconv::Imports;
1921
use crate::stability::Stability;
@@ -109,6 +111,12 @@ pub struct GroupSpec {
109111
/// Note: only valid if type is event
110112
#[serde(skip_serializing_if = "Option::is_none")]
111113
pub body: Option<AnyValueSpec>,
114+
/// Header for the namespace/page.
115+
#[serde(skip_serializing_if = "Option::is_none")]
116+
pub header: Option<HeaderSpec>,
117+
/// Footer for the namespace/page.
118+
#[serde(skip_serializing_if = "Option::is_none")]
119+
pub footer: Option<FooterSpec>,
112120
/// Annotations for the group.
113121
#[serde(default)]
114122
#[serde(skip_serializing_if = "Option::is_none")]
@@ -137,8 +145,8 @@ impl GroupSpec {
137145
});
138146
}
139147

140-
// Field stability is required for all group types except attribute group.
141-
if self.r#type != GroupType::AttributeGroup && self.stability.is_none() {
148+
// Field stability is required for all group types except namespaces & attribute group.
149+
if self.r#type != GroupType::AttributeGroup && self.r#type != GroupType::NameSpace && self.stability.is_none() {
142150
errors.push(Error::InvalidGroupStability {
143151
path_or_url: path_or_url.to_owned(),
144152
group_id: self.id.clone(),
@@ -159,9 +167,10 @@ impl GroupSpec {
159167
// Groups should only reference attributes once.
160168
validate_duplicate_attribute_ref(&mut errors, &self.attributes, &self.id, path_or_url);
161169

162-
// All types, except metric and event, must have extends or attributes or both.
170+
// All types, except namespaces, metric and event, must have extends or attributes or both.
163171
if self.r#type != GroupType::Metric
164172
&& self.r#type != GroupType::Event
173+
&& self.r#type != GroupType::NameSpace
165174
&& self.extends.is_none()
166175
&& self.attributes.is_empty()
167176
{
@@ -182,6 +191,7 @@ impl GroupSpec {
182191
.to_owned(),
183192
});
184193
}
194+
// TODO: this should become a warning as span events are deprecated
185195
if !self.events.is_empty() {
186196
errors.push(Error::InvalidGroup {
187197
path_or_url: path_or_url.to_owned(),
@@ -545,7 +555,7 @@ fn validate_any_value(
545555
}
546556
}
547557

548-
/// The different types of groups: `attribute_group`, `span`, `event`, `metric`, `entity`, `scope`.
558+
/// The different types of groups: `attribute_group`, `span`, `event`, `metric`, `entity`, `scope`, `namespace`.
549559
///
550560
/// Note: The `resource` type is no longer used and is an alias for `entity`.
551561
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Hash, Clone, JsonSchema)]
@@ -572,6 +582,8 @@ pub enum GroupType {
572582
Scope,
573583
/// Undefined group type.
574584
Undefined,
585+
/// Namespace semantic convention.
586+
NameSpace,
575587
}
576588

577589
impl Default for GroupType {
@@ -697,6 +709,8 @@ mod tests {
697709
name: None,
698710
display_name: None,
699711
body: None,
712+
header: None,
713+
footer: None,
700714
annotations: None,
701715
entity_associations: Vec::new(),
702716
};
@@ -862,6 +876,8 @@ mod tests {
862876
name: None,
863877
display_name: None,
864878
body: None,
879+
header: None,
880+
footer: None,
865881
annotations: None,
866882
entity_associations: Vec::new(),
867883
};
@@ -1159,6 +1175,8 @@ mod tests {
11591175
),
11601176
},
11611177
}),
1178+
header: None,
1179+
footer: None,
11621180
annotations: None,
11631181
entity_associations: Vec::new(),
11641182
};
@@ -1374,6 +1392,8 @@ mod tests {
13741392
),
13751393
},
13761394
}),
1395+
header: None,
1396+
footer: None,
13771397
annotations: None,
13781398
entity_associations: Vec::new(),
13791399
};
@@ -1518,6 +1538,8 @@ mod tests {
15181538
name: None,
15191539
display_name: None,
15201540
body: None,
1541+
header: None,
1542+
footer: None,
15211543
annotations: None,
15221544
entity_associations: Vec::new(),
15231545
};
@@ -1688,6 +1710,8 @@ mod tests {
16881710
name: None,
16891711
display_name: None,
16901712
body: None,
1713+
header: None,
1714+
footer: None,
16911715
annotations: None,
16921716
entity_associations: Vec::new(),
16931717
};
@@ -1840,6 +1864,8 @@ mod tests {
18401864
name: None,
18411865
display_name: None,
18421866
body: None,
1867+
header: None,
1868+
footer: None,
18431869
annotations: None,
18441870
entity_associations: Vec::new(),
18451871
};
@@ -1900,6 +1926,8 @@ mod tests {
19001926
name: None,
19011927
display_name: None,
19021928
body: None,
1929+
header: None,
1930+
footer: None,
19031931
annotations: None,
19041932
entity_associations: vec!["test".to_owned()],
19051933
};
@@ -1938,4 +1966,35 @@ mod tests {
19381966
result
19391967
);
19401968
}
1969+
1970+
#[test]
1971+
fn test_validate_namespace(){
1972+
let mut group = GroupSpec {
1973+
id: "test".to_owned(),
1974+
r#type: GroupType::NameSpace,
1975+
name: Some("test_namespace".to_owned()),
1976+
brief: "test".to_owned(),
1977+
note: "test".to_owned(),
1978+
prefix: "".to_owned(),
1979+
extends: None,
1980+
stability: None,
1981+
deprecated: None,
1982+
span_kind: None,
1983+
events: vec![],
1984+
metric_name: None,
1985+
instrument: None,
1986+
unit: None,
1987+
display_name: None,
1988+
attributes: vec![],
1989+
body: None,
1990+
header: None,
1991+
footer: None,
1992+
annotations: None,
1993+
entity_associations: Vec::new(),
1994+
};
1995+
assert!(group
1996+
.validate("<test>")
1997+
.into_result_failing_non_fatal()
1998+
.is_ok());
1999+
}
19412000
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
3+
//! Stability specification.
4+
5+
use schemars::JsonSchema;
6+
use serde::{Deserialize, Serialize};
7+
use crate::style::Style;
8+
9+
/// A header specification
10+
#[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, JsonSchema)]
11+
#[serde(deny_unknown_fields)]
12+
pub struct HeaderSpec {
13+
14+
/// The title of the header.
15+
pub title: Option<String>,
16+
/// The content in the header.
17+
pub content: String,
18+
/// The style of the header.
19+
pub style: Style,
20+
}
21+
22+
impl HeaderSpec {
23+
/// returns the title of the header
24+
#[must_use]
25+
fn title(&self) -> &Option<String> {
26+
&self.title
27+
}
28+
/// returns the content of the header
29+
#[must_use]
30+
fn content(&self) -> &String {
31+
&self.content
32+
}
33+
/// returns the style of the header
34+
#[must_use]
35+
fn style(&self) -> &Style {
36+
&self.style
37+
}
38+
}

0 commit comments

Comments
 (0)