1use url::Url;
7
8use crate::{
9 date::Date, namespace::OrganizationNamespaceUrl, BodyId, ConsultationId, DateTime, Keyword,
10 Location, MeetingListId, MembershipId, Name, OrganizationClassification, OrganizationId,
11 OrganizationType, Post,
12};
13
14#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct Organization {
17 pub id: OrganizationId,
18
19 #[serde(rename = "type")]
20 pub namespace: OrganizationNamespaceUrl,
21
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub body: Option<BodyId>,
24
25 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub name: Option<Name>,
27
28 #[serde(default, skip_serializing_if = "Vec::is_empty")]
29 pub membership: Vec<MembershipId>,
30
31 #[serde(default, skip_serializing_if = "Option::is_none")]
32 pub meeting: Option<MeetingListId>,
33
34 #[serde(default, skip_serializing_if = "Option::is_none")]
35 pub consultation: Option<ConsultationId>,
36
37 #[serde(default, skip_serializing_if = "Option::is_none")]
38 pub short_name: Option<Name>,
39
40 #[serde(default, skip_serializing_if = "Vec::is_empty")]
41 pub post: Vec<Post>,
42
43 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub sub_organization_of: Option<OrganizationId>,
45
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub organization_type: Option<OrganizationType>,
48
49 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub classification: Option<OrganizationClassification>,
51
52 #[serde(default, skip_serializing_if = "Option::is_none")]
53 pub start_date: Option<Date>,
54
55 #[serde(default, skip_serializing_if = "Option::is_none")]
56 pub end_date: Option<Date>,
57
58 #[serde(default, skip_serializing_if = "Option::is_none")]
59 pub website: Option<Url>,
60
61 #[serde(default, skip_serializing_if = "Option::is_none")]
62 pub location: Option<Location>,
63
64 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub external_body: Option<BodyId>,
66
67 #[serde(default, skip_serializing_if = "Option::is_none")]
68 pub license: Option<Url>,
69
70 #[serde(default, skip_serializing_if = "Vec::is_empty")]
71 pub keyword: Vec<Keyword>,
72
73 pub created: DateTime,
74
75 pub modified: DateTime,
76
77 #[serde(default, skip_serializing_if = "Option::is_none")]
78 pub web: Option<Url>,
79
80 #[serde(default, skip_serializing_if = "Option::is_none")]
81 pub deleted: Option<bool>,
82}
83
84#[cfg(test)]
85mod serde_tests {
86 use super::Organization;
87 use crate::{
88 namespace::{LocationNamespaceUrl, OrganizationNamespaceUrl},
89 Location, OrganizationType,
90 };
91
92 use pretty_assertions::assert_eq;
93 use serde_json::json;
94 use time::macros::{date, datetime};
95
96 fn example_organization() -> Organization {
97 let geojson_feature = {
98 let mut f =
99 geojson::Feature::from(geojson::Geometry::new(geojson::Value::Point(vec![
100 50.1234, 10.4321,
101 ])));
102 f.set_property("name", "Rathausplatz");
103 f
104 };
105
106 Organization {
107 id: "https://oparl.example.org/organization/34"
108 .parse()
109 .expect("value must be parseable as id"),
110 namespace: OrganizationNamespaceUrl::Identifier,
111 body: Some(
112 "https://oparl.example.org/bodies/1"
113 .parse()
114 .expect("value must be parseable as url"),
115 ),
116 name: Some("Ausschuss für Haushalt und Finanzen".into()),
117 membership: vec![
118 "https://oparl.example.org/membership/27"
119 .parse()
120 .expect("value must be parseable as url"),
121 "https://oparl.example.org/membership/48"
122 .parse()
123 .expect("value must be parseable as url"),
124 "https://oparl.example.org/membership/57"
125 .parse()
126 .expect("value must be parseable as url"),
127 ],
128 meeting: Some(
129 "https://oparl.example.org/organization/34/meetings"
130 .parse()
131 .expect("value must be parseable as url"),
132 ),
133 consultation: None,
134 short_name: Some("Finanzausschuss".into()),
135 post: vec![
136 "Vorsitzender".into(),
137 "1. Stellvertreter".into(),
138 "Mitglied".into(),
139 ],
140 sub_organization_of: None,
141 organization_type: Some(OrganizationType::Council),
142 classification: Some("Ausschuss".into()),
143 start_date: Some(date!(2012 - 07 - 17).into()),
144 end_date: None,
145 website: None,
146 location: Some(Location {
147 id: "https://oparl.example.org/location/0"
148 .parse()
149 .expect("value must be parseable as id"),
150 namespace: LocationNamespaceUrl::Identifier,
151 description: Some(
152 "Rathaus der Beispielstadt, Ratshausplatz 1, 12345 Beispielstadt".to_string(),
153 ),
154 geojson: Some(geojson_feature.into()),
155 street_address: None,
156 room: None,
157 postal_code: None,
158 sub_locality: None,
159 locality: None,
160 bodies: vec![],
161 organizations: vec![],
162 persons: vec![],
163 meetings: vec![],
164 papers: vec![],
165 license: None,
166 keyword: vec![],
167 created: datetime!(2012-01-06 12:01:00 +01:00).into(),
168 modified: datetime!(2012-01-08 14:05:27 +01:00).into(),
169 web: None,
170 deleted: None,
171 }),
172 external_body: None,
173 license: None,
174 keyword: vec!["finanzen".into(), "haushalt".into()],
175 created: datetime!(2012-07-16 00:00:00 +02:00).into(),
176 modified: datetime!(2012-08-16 12:34:56 +02:00).into(),
177 web: None,
178 deleted: None,
179 }
180 }
181
182 fn example_organization_json() -> serde_json::Value {
183 json!({
184 "id": "https://oparl.example.org/organization/34",
185 "type": "https://schema.oparl.org/1.1/Organization",
186 "body": "https://oparl.example.org/bodies/1",
187 "name": "Ausschuss für Haushalt und Finanzen",
188 "shortName": "Finanzausschuss",
189 "startDate": "2012-07-17",
190 "organizationType": "Gremium",
191 "location": {
192 "id": "https://oparl.example.org/location/0",
193 "type": "https://schema.oparl.org/1.1/Location",
194 "description": "Rathaus der Beispielstadt, Ratshausplatz 1, 12345 Beispielstadt",
195 "created": "2012-01-06T12:01:00+01:00",
196 "modified": "2012-01-08T14:05:27+01:00",
197 "geojson": {
198 "type": "Feature",
199 "geometry": {
200 "type": "Point",
201 "coordinates": [
202 50.1234,
203 10.4321
204 ]
205 },
206 "properties": {
207 "name": "Rathausplatz"
208 }
209 }
210 },
211 "post": [
212 "Vorsitzender",
213 "1. Stellvertreter",
214 "Mitglied"
215 ],
216 "meeting": "https://oparl.example.org/organization/34/meetings",
217 "membership": [
218 "https://oparl.example.org/membership/27",
219 "https://oparl.example.org/membership/48",
220 "https://oparl.example.org/membership/57"
221 ],
222 "classification": "Ausschuss",
223 "keyword": [
224 "finanzen",
225 "haushalt"
226 ],
227 "created": "2012-07-16T00:00:00+02:00",
228 "modified": "2012-08-16T12:34:56+02:00"
229 })
230 }
231
232 #[test]
233 fn serialize() {
234 assert_eq!(json!(example_organization()), example_organization_json());
235 }
236
237 #[test]
238 fn deserialize_good() {
239 let deserialized: Organization = serde_json::from_value(example_organization_json())
240 .expect("value must be deserializable as Organization");
241 assert_eq!(deserialized, example_organization());
242 }
243
244 #[test]
245 fn deserialize_bad() {
246 assert!(serde_json::from_value::<Organization>(json!("xyzabcd")).is_err());
247 assert!(serde_json::from_value::<Organization>(json!(true)).is_err());
248 assert!(serde_json::from_value::<Organization>(json!(123)).is_err());
249 }
250}