1mod data_classification_type;
2mod data_flow_type;
3
4use crate::common::license::Licenses;
5use crate::common::organization::OrganizationalEntity;
6use crate::component::external_reference::ExternalReference;
7use crate::service::data_classification_type::DataClassificationType;
8use derive_builder::Builder;
9use serde::{Deserialize, Serialize};
10use yaserde_derive::{YaDeserialize, YaSerialize};
11
12#[derive(Clone, Builder, PartialEq, Debug, Serialize, Deserialize, YaSerialize, YaDeserialize)]
13pub struct Services {
14 pub service: Vec<Service>,
15}
16
17impl Services {
18 pub fn new(service: Vec<Service>) -> Services {
19 Services { service }
20 }
21}
22
23#[derive(Clone, Builder, PartialEq, Debug, Serialize, Deserialize, YaSerialize, YaDeserialize)]
24#[yaserde(
25 prefix = "ns",
26 default_namespace = "ns",
27 namespace = "ns: http://cyclonedx.org/schema/bom/1.2"
28)]
29pub struct Service {
30 #[serde(rename = "bom-ref")]
31 #[yaserde(rename = "bom-ref", attribute)]
32 pub bom_ref: Option<String>,
33
34 #[yaserde(prefix = "ns")]
35 pub provider: Option<OrganizationalEntity>,
36 #[yaserde(prefix = "ns")]
37 pub group: Option<String>,
38 #[yaserde(prefix = "ns")]
39 pub name: String,
40 #[yaserde(prefix = "ns")]
41 pub version: Option<String>,
42 #[yaserde(prefix = "ns")]
43 pub description: Option<String>,
44 #[yaserde(prefix = "ns")]
45 pub endpoints: Option<Endpoints>,
46 #[yaserde(prefix = "ns")]
47 pub authenticated: Option<bool>,
48 #[serde(rename = "x-trust-boundary")]
49 #[yaserde(rename = "x-trust-boundary", prefix = "ns")]
50 pub x_trust_boundary: Option<bool>,
51 pub data: Option<Classifications>,
52 pub licenses: Option<Licenses>,
53 #[serde(rename = "externalReferences")]
54 #[yaserde(rename = "externalReferences", prefix = "ns")]
55 pub external_references: Option<ExternalReferences>,
56 pub services: Vec<Service>,
57}
58
59#[derive(Clone, Default, PartialEq, Debug, Serialize, Deserialize, YaSerialize, YaDeserialize)]
60pub struct ExternalReferences {
61 pub reference: Vec<ExternalReference>,
62}
63
64impl ExternalReferences {
65 pub fn new(reference: Vec<ExternalReference>) -> ExternalReferences {
66 ExternalReferences { reference }
67 }
68}
69
70#[derive(Clone, Default, PartialEq, Debug, Serialize, Deserialize, YaSerialize, YaDeserialize)]
71pub struct Classifications {
72 pub classification: Vec<DataClassificationType>,
73}
74
75impl Classifications {
76 pub fn new(classification: Vec<DataClassificationType>) -> Classifications {
77 Classifications { classification }
78 }
79}
80
81#[derive(Clone, Default, PartialEq, Debug, Serialize, Deserialize, YaSerialize, YaDeserialize)]
82pub struct Endpoints {
83 pub endpoint: Vec<EndpointType>,
84}
85
86impl Endpoints {
87 pub fn new(endpoint: Vec<EndpointType>) -> Endpoints {
88 Endpoints { endpoint }
89 }
90}
91
92#[derive(Clone, PartialEq, Debug, Serialize, Deserialize, YaSerialize, YaDeserialize)]
93pub struct EndpointType {
94 #[yaserde(text)]
95 pub value: String,
96}
97
98impl EndpointType {
99 pub fn new(value: String) -> EndpointType {
100 EndpointType { value }
101 }
102}
103
104#[cfg(test)]
105pub mod tests {
106 use super::*;
107 use crate::common::license::*;
108 use crate::common::organization::*;
109 use crate::component::external_reference::*;
110 use crate::service::data_classification_type::*;
111 use crate::service::data_flow_type::DataFlowType;
112 use std::fs::File;
113 use std::io::BufReader;
114 use std::path::PathBuf;
115 use yaserde::ser::Config;
116
117 #[test]
118 pub fn print_xml() {
119 let expected = ServiceBuilder::default()
120 .bom_ref(Option::from(
121 "b2a46a4b-8367-4bae-9820-95557cfe03a8".to_string(),
122 ))
123 .provider(Option::from(
124 OrganizationalEntityBuilder::default()
125 .name(Option::from("Partner Org".to_string()))
126 .url(vec!["https://partner.org".to_string()])
127 .contact(vec![OrganizationalContactBuilder::default()
128 .name(Option::from("Support".to_string()))
129 .email(vec!["support@partner".to_string()])
130 .phone(vec!["800-555-1212".to_string()])
131 .build()
132 .unwrap()])
133 .build()
134 .unwrap(),
135 ))
136 .group(Option::from("org.partner".to_string()))
137 .name("Stock ticker service".to_string())
138 .version(Option::from("2020-Q2".to_string()))
139 .description(Option::from(
140 "Provides real-time stock information".to_string(),
141 ))
142 .endpoints(Option::from(Endpoints::new(vec![
143 EndpointType::new("https://partner.org/api/v1/lookup".to_string()),
144 EndpointType::new("https://partner.org/api/v1/stock".to_string()),
145 ])))
146 .authenticated(Option::from(true))
147 .x_trust_boundary(Option::from(true))
148 .data(Option::from(Classifications::new(vec![
149 DataClassificationTypeBuilder::default()
150 .flow(DataFlowType::InBound)
151 .value("PII".to_string())
152 .build()
153 .unwrap(),
154 DataClassificationTypeBuilder::default()
155 .flow(DataFlowType::Outbound)
156 .value("PIFI".to_string())
157 .build()
158 .unwrap(),
159 DataClassificationTypeBuilder::default()
160 .flow(DataFlowType::BiDirectional)
161 .value("public".to_string())
162 .build()
163 .unwrap(),
164 ])))
165 .licenses(Option::from(
166 LicensesBuilder::default()
167 .license(vec![LicenseTypeBuilder::default()
168 .id(None)
169 .name(Option::from("Partner License".to_string()))
170 .text(None)
171 .url(None)
172 .build()
173 .unwrap()])
174 .expression(None)
175 .build()
176 .unwrap(),
177 ))
178 .external_references(Option::from(ExternalReferences::new(vec![
179 ExternalReference::new(
180 ExternalReferenceType::Website,
181 "http://partner.org".to_string(),
182 None,
183 ),
184 ExternalReference::new(
185 ExternalReferenceType::Documentation,
186 "http://api.partner.org/swagger".to_string(),
187 None,
188 ),
189 ])))
190 .services(Vec::new())
191 .build()
192 .unwrap();
193
194 let parsed = yaserde::ser::to_string_with_config(
195 &expected,
196 &Config {
197 perform_indent: true,
198 write_document_declaration: false,
199 indent_string: None,
200 },
201 )
202 .unwrap();
203
204 let actual: Service = yaserde::de::from_str(parsed.as_str()).unwrap();
205
206 assert_eq!(expected, actual);
207 }
208
209 #[test]
210 pub fn can_decode() {
211 let reader = setup("service-1.2.xml");
212
213 let response: Service = yaserde::de::from_reader(reader).unwrap();
214
215 assert_eq!(response.name, "Stock ticker service");
216 assert_eq!(response.group.unwrap(), "org.partner");
217 assert_eq!(response.version.unwrap(), "2020-Q2");
218 assert_eq!(
219 response.description.unwrap(),
220 "Provides real-time stock information"
221 );
222 let endpoints = response.endpoints.unwrap();
223 assert_eq!(endpoints.endpoint.len(), 2);
224 assert_eq!(
225 endpoints.endpoint[0].value,
226 "https://partner.org/api/v1/lookup"
227 );
228 assert_eq!(
229 endpoints.endpoint[1].value,
230 "https://partner.org/api/v1/stock"
231 );
232 assert_eq!(response.authenticated.unwrap(), true);
233 assert_eq!(response.x_trust_boundary.unwrap(), true);
234
235 let classifications = response.data.unwrap().classification;
236 assert_eq!(classifications.len(), 3);
237 assert_eq!(classifications[0].flow, DataFlowType::InBound);
238 assert_eq!(classifications[0].value, "PII");
239 assert_eq!(classifications[1].flow, DataFlowType::Outbound);
240 assert_eq!(classifications[1].value, "PIFI");
241 assert_eq!(classifications[2].flow, DataFlowType::BiDirectional);
242 assert_eq!(classifications[2].value, "public");
243
244 let licenses = response.licenses.unwrap();
245 assert!(!licenses.expression.is_some());
246 assert_eq!(licenses.license.len(), 1);
247 let license_type = licenses.license[0].clone();
248 assert_eq!(license_type.name.unwrap(), "Partner license");
249
250 let references = response.external_references.unwrap().reference;
251 assert_eq!(references.len(), 2);
252 assert_eq!(references[0].ref_type, ExternalReferenceType::Website);
253 assert_eq!(references[0].url, "http://partner.org");
254 assert_eq!(references[1].ref_type, ExternalReferenceType::Documentation);
255 assert_eq!(references[1].url, "http://api.partner.org/swagger");
256 }
257
258 fn setup(file: &str) -> BufReader<File> {
259 let mut test_folder = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
260 test_folder.push("resources/test/".to_owned() + file);
261 let file = File::open(test_folder);
262 let reader = BufReader::new(file.unwrap());
263 reader
264 }
265}