Skip to content

Commit

Permalink
feat!: add support for multiple version of specification of cdevents
Browse files Browse the repository at this point in the history
- link as git submodule several versions of cdevents specs
- add version into module name of subject's content
- generate a module per specs with alias to the versionned subejct's content module
- generate a module `latest`  with alias to the latest version of each subject's content module
- ignore subject'content with modifier in their version
- update tests to load every json schema and test it (selection based on `context.type`

FIX #16

Signed-off-by: David Bernard <david.bernard.31@gmail.com>
  • Loading branch information
davidB committed Mar 3, 2024
1 parent 6d70681 commit f70d5aa
Show file tree
Hide file tree
Showing 50 changed files with 969 additions and 614 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ clean:
cargo clean

generate:
cargo run -p generator -- --templates-dir "generator/templates" --jsonschema-dir "cdevents-specs/spec-v0.3/schemas" --dest "cdevents-sdk/src/generated"
cargo run -p generator -- --templates-dir "generator/templates" --jsonschemas "cdevents-specs/*/schemas/*.json" --dest "cdevents-sdk/src/generated"

test:
cargo nextest run --all-features
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ To send a CDEvent as CloudEvent:
// from examples/pipelinerun_finished.rs
use std::error::Error;

use cdevents_sdk::{CDEvent, Subject, pipelinerun_finished, Content};
use cdevents_sdk::{CDEvent, Subject, latest::pipelinerun_finished, Content};
use cloudevents::{Event, AttributesReader};

fn main() -> Result<(), Box<dyn Error>> {
Expand Down
1 change: 1 addition & 0 deletions cdevents-sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ time = { version = "0.3", features = ["serde-human-readable"] }
[dev-dependencies]
assert-json-diff = "2.0"
boon = "0.5"
glob = "0.3"
proptest = "1"
rstest = "0.18"

Expand Down
2 changes: 1 addition & 1 deletion cdevents-sdk/examples/pipelinerun_finished.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::error::Error;

use cdevents_sdk::{CDEvent, Subject, pipelinerun_finished};
use cdevents_sdk::{CDEvent, Subject, latest::pipelinerun_finished};
use cloudevents::{Event, AttributesReader};

fn main() -> Result<(), Box<dyn Error>> {
Expand Down
2 changes: 1 addition & 1 deletion cdevents-sdk/src/cloudevents.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ mod tests {
#[test]
fn test_into_cloudevent() -> Result<(), Box<dyn std::error::Error>> {
let cdevent = CDEvent::from(
Subject::from(build_queued::Content{})
Subject::from(latest::build_queued::Content{})
.with_id("subject123".try_into()?)
.with_source("/event/source/123".try_into()?)
)
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use serde::{Serialize, Deserialize};
#[serde(deny_unknown_fields)]
pub struct Content {
#[serde(rename = "signature",)]
pub signature: String,
pub signature: crate::NonEmptyString,
}

#[cfg(test)]
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
41 changes: 41 additions & 0 deletions cdevents-sdk/src/generated/change_created_0_2_0.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @generated
// by cdevents/sdk-rust/generator (subject.hbs)

#[cfg(feature = "testkit")] use proptest_derive::Arbitrary;
use serde::{Serialize, Deserialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "testkit", derive(Arbitrary))]
#[serde(deny_unknown_fields)]
pub struct Content {
#[serde(rename = "description", default, skip_serializing_if = "Option::is_none",)]
pub description: Option<crate::NonEmptyString>,
#[serde(rename = "repository", default, skip_serializing_if = "Option::is_none",)]
pub repository: Option<ContentRepository>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[cfg_attr(feature = "testkit", derive(Arbitrary))]
#[serde(deny_unknown_fields)]
pub struct ContentRepository {
#[serde(rename = "id",)]
pub id: crate::Id,
#[serde(rename = "source", default, skip_serializing_if = "Option::is_none",)]
pub source: Option<crate::UriReference>,
}

#[cfg(test)]
mod tests {
use proptest::prelude::*;
use super::*;

proptest! {
#[test]
#[cfg(feature = "testkit")]
fn arbitraries_are_json_valid(s in any::<Content>()) {
let json_str = serde_json::to_string(&s).unwrap();
let actual = serde_json::from_str::<Content>(&json_str).unwrap();
assert_eq!(s, actual);
}
}
}
File renamed without changes.
1,235 changes: 687 additions & 548 deletions cdevents-sdk/src/generated/mod.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub struct Content {
#[serde(rename = "owner", default, skip_serializing_if = "Option::is_none",)]
pub owner: Option<String>,
#[serde(rename = "url",)]
pub url: String,
pub url: crate::Uri,
#[serde(rename = "viewUrl", default, skip_serializing_if = "Option::is_none",)]
pub view_url: Option<String>,
}
Expand Down
77 changes: 55 additions & 22 deletions cdevents-sdk/tests/specs.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,58 @@
use assert_json_diff::assert_json_eq;
use cdevents_sdk::CDEvent;
use rstest::rstest;
use std::fs;
use rstest::*;
use std::{collections::HashMap, fs};
use std::path::PathBuf;
use proptest::prelude::*;
use boon::{Schemas, Compiler};
use boon::{Schemas, Compiler, SchemaIndex};
use glob::glob;
use std::sync::OnceLock;

struct EventsSchemas {
schemas: Schemas,
mapping: HashMap<String, SchemaIndex>,
}

impl EventsSchemas {
fn load() -> Self {
let mut schemas = Schemas::new();
let mut compiler = Compiler::new();
let mut mapping = HashMap::new();
for entry in glob("../cdevents-specs/*/schemas/*.json").expect("Failed to read glob pattern") {
let schemapath = entry.unwrap();
//TODO avoid to read the schema twice (as json, then as jsonschema)
let jsonschema: serde_json::Value = serde_json::from_str(&std::fs::read_to_string(&schemapath).unwrap()).unwrap();
let ty = jsonschema["properties"]["context"]["properties"]["type"]["default"].as_str()
.unwrap_or_default()
.to_string();
if !mapping.contains_key(&ty) {
let sch_index = compiler.compile(&schemapath.to_string_lossy(), &mut schemas);
if let Err(err) = sch_index {
panic!("{err:#}"); //like a assert(false,...)
}
let sch_index = sch_index.unwrap();
mapping.insert(ty, sch_index);
}
}
Self {
schemas, mapping
}
}

fn check_against_schema(&self, json: &serde_json::Value, ty: &str) {
let sch_index = self.mapping.get(ty).expect(&format!("to have schema for {ty}"));
let result = self.schemas.validate(json, sch_index.clone());
if let Err(err) = result {
panic!("{err}");
}
}
}

static EVENTS_SCHEMA_CELL: OnceLock<EventsSchemas> = OnceLock::new();

fn events_schemas() -> &'static EventsSchemas {
EVENTS_SCHEMA_CELL.get_or_init(|| EventsSchemas::load())
}

#[rstest]
fn for_each_example(#[files("../cdevents-specs/spec-v0.3/examples/*.json")] path: PathBuf) {
Expand All @@ -30,37 +78,22 @@ fn for_each_example(#[files("../cdevents-specs/spec-v0.3/examples/*.json")] path
assert_json_eq!(example_json, cdevent_json);
}

fn check_against_schema(json: &serde_json::Value, ty: &str) {
let (subject, predicate) = cdevents_sdk::extract_subject_predicate(ty).expect("valid type: {ty}");
let schemapath = format!("../cdevents-specs/spec-v0.3/schemas/{subject}{predicate}.json").to_lowercase();
//TODO optimize to not recompile a previously read schema
let mut schemas = Schemas::new();
let mut compiler = Compiler::new();
let sch_index = compiler.compile(&schemapath, &mut schemas);
if let Err(err) = sch_index {
panic!("{err:#}"); //like a assert(false,...)
}
let sch_index = sch_index.unwrap();
let result = schemas.validate(json, sch_index);
if let Err(err) = result {
panic!("{err}");
}
}

#[rstest]
fn validate_example_against_schema(#[files("../cdevents-specs/spec-v0.3/examples/*.json")] path: PathBuf) {
let events_schemas = events_schemas();
let example_txt = fs::read_to_string(path).expect("to read file as string");
let example_json: serde_json::Value =
serde_json::from_str(&example_txt).expect("to parse as json");
let ty = example_json["context"]["type"].as_str().expect("valid context.type in json");
check_against_schema(&example_json, ty);
events_schemas.check_against_schema(&example_json, ty);
}

proptest! {
#[test]
#[cfg(feature = "testkit")]
fn arbitraries_check_jsonschema(s in any::<CDEvent>()) {
let events_schemas = events_schemas();
let json = serde_json::to_value(&s).unwrap();
check_against_schema(&json, s.ty());
events_schemas.check_against_schema(&json, s.ty());
}
}
2 changes: 2 additions & 0 deletions generator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ description = "generate cdevents type from json schema on cdevents-spec"
anyhow = "1.0"
clap = { version = "4", features = ["derive"] }
cruet = "0.14"
glob = "0.3"
handlebars = { version = "5", features = ["dir_source"] }
handlebars_misc_helpers = { version = "0.15", default-features = false, features = [
"string",
"json",
] }
indexmap = "2.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
url = "2.5"
Loading

0 comments on commit f70d5aa

Please sign in to comment.