Skip to main content

cognite/dto/
identity.rs

1use std::collections::HashMap;
2
3use serde::{ser::SerializeSeq, Deserialize, Serialize};
4
5use crate::{
6    models::instances::InstanceId, ApiErrorDetail, Chunkable, FromErrorDetail,
7    IntegerStringOrObject,
8};
9
10#[derive(Serialize, Deserialize, Debug, Hash, PartialEq, Eq, Clone)]
11#[serde(untagged, rename_all_fields = "camelCase")]
12/// An Identity represents a CDF resource either by internal ID or external ID.
13pub enum Identity {
14    /// Identity by CDF internal ID.
15    Id {
16        /// Numerical internal ID.
17        id: i64,
18    },
19    /// Identity by CDF external ID.
20    ExternalId {
21        /// External ID, unique for the given resource.
22        external_id: String,
23    },
24}
25
26impl Identity {
27    /// Create an identity using a CDF internal ID.
28    pub fn id(id: i64) -> Self {
29        Identity::Id { id }
30    }
31
32    /// Create an identity using a CDF external ID.
33    pub fn external_id(external_id: impl Into<String>) -> Self {
34        Identity::ExternalId {
35            external_id: external_id.into(),
36        }
37    }
38
39    /// Consume self and return Some if this is an external ID.
40    pub fn into_external_id(self) -> Option<String> {
41        match self {
42            Identity::ExternalId { external_id } => Some(external_id),
43            _ => None,
44        }
45    }
46
47    /// Consume self and return Some if this is an internal ID.
48    pub fn into_id(self) -> Option<i64> {
49        match self {
50            Identity::Id { id } => Some(id),
51            _ => None,
52        }
53    }
54
55    /// Get self as external ID.
56    pub fn as_external_id(&self) -> Option<&String> {
57        match self {
58            Identity::ExternalId { external_id } => Some(external_id),
59            _ => None,
60        }
61    }
62
63    /// Get self as internal ID.
64    pub fn as_id(&self) -> Option<i64> {
65        match self {
66            Identity::Id { id } => Some(*id),
67            _ => None,
68        }
69    }
70}
71
72impl Default for Identity {
73    fn default() -> Self {
74        Identity::Id { id: 0 }
75    }
76}
77
78impl From<i64> for Identity {
79    fn from(id: i64) -> Self {
80        Identity::Id { id }
81    }
82}
83
84impl From<&String> for Identity {
85    fn from(value: &String) -> Self {
86        Self::from(value.clone())
87    }
88}
89
90impl From<String> for Identity {
91    fn from(external_id: String) -> Self {
92        Identity::ExternalId { external_id }
93    }
94}
95
96impl From<&str> for Identity {
97    fn from(external_id: &str) -> Self {
98        Identity::ExternalId {
99            external_id: external_id.to_owned(),
100        }
101    }
102}
103
104impl FromErrorDetail for Identity {
105    fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
106        ApiErrorDetail::get_integer(detail, "id")
107            .map(|id| Identity::Id { id })
108            .or_else(|| {
109                ApiErrorDetail::get_string(detail, "externalId").map(|id| Identity::ExternalId {
110                    external_id: id.to_owned(),
111                })
112            })
113    }
114}
115
116#[derive(Serialize, Deserialize, Debug, Clone)]
117#[serde(rename_all = "camelCase")]
118/// Wrapper around a cognite internal ID.
119pub struct CogniteId {
120    /// Internal ID.
121    id: i64,
122}
123
124impl FromErrorDetail for CogniteId {
125    fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
126        ApiErrorDetail::get_integer(detail, "id").map(|id| CogniteId { id })
127    }
128}
129
130#[derive(Serialize, Deserialize, Debug, Clone, Default)]
131#[serde(rename_all = "camelCase")]
132/// Wrapper around a cognite external ID.
133pub struct CogniteExternalId {
134    /// External ID.
135    pub external_id: String,
136}
137
138impl FromErrorDetail for CogniteExternalId {
139    fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
140        ApiErrorDetail::get_string(detail, "externalId").map(|external_id| CogniteExternalId {
141            external_id: external_id.to_owned(),
142        })
143    }
144}
145
146/// Trait indicating that a type can be compared to an identity.
147pub trait EqIdentity {
148    /// Return true if the identity given by `id` points to self.
149    fn eq(&self, id: &Identity) -> bool;
150}
151
152impl From<String> for CogniteExternalId {
153    fn from(external_id: String) -> Self {
154        CogniteExternalId { external_id }
155    }
156}
157
158impl<'a> From<&'a str> for CogniteExternalId {
159    fn from(external_id: &'a str) -> Self {
160        CogniteExternalId {
161            external_id: external_id.to_owned(),
162        }
163    }
164}
165
166impl From<i64> for CogniteId {
167    fn from(id: i64) -> Self {
168        CogniteId { id }
169    }
170}
171
172#[derive(Debug, Deserialize, Serialize, Clone, Hash, Eq, PartialEq)]
173#[serde(rename_all_fields = "camelCase")]
174#[serde(untagged)]
175/// Identity or instance ID.
176pub enum IdentityOrInstance {
177    /// Identity, external ID or internal ID.
178    Identity(Identity),
179    /// Instance ID, refering to a node in data modeling.
180    InstanceId {
181        /// Instance id.
182        instance_id: InstanceId,
183    },
184}
185
186impl IdentityOrInstance {
187    /// Create a new IdentityOrInstance using an internal ID.
188    pub fn id(id: i64) -> Self {
189        Self::Identity(Identity::id(id))
190    }
191
192    /// Create a new IdentityOrInstance using an external ID.
193    pub fn external_id(external_id: impl Into<String>) -> Self {
194        Self::Identity(Identity::external_id(external_id))
195    }
196
197    /// Create a new IdentityOrInstance using an instance ID.
198    pub fn instance_id(instance_id: impl Into<InstanceId>) -> Self {
199        Self::InstanceId {
200            instance_id: instance_id.into(),
201        }
202    }
203
204    /// Get self as internal ID.
205    pub fn as_id(&self) -> Option<i64> {
206        match self {
207            Self::Identity(i) => i.as_id(),
208            _ => None,
209        }
210    }
211
212    /// Get self as external ID.
213    pub fn as_external_id(&self) -> Option<&String> {
214        match self {
215            Self::Identity(i) => i.as_external_id(),
216            _ => None,
217        }
218    }
219
220    /// Get self as identity.
221    pub fn as_identity(&self) -> Option<&Identity> {
222        match self {
223            Self::Identity(i) => Some(i),
224            _ => None,
225        }
226    }
227
228    /// Get self as instance id.
229    pub fn as_instance_id(&self) -> Option<&InstanceId> {
230        match self {
231            Self::InstanceId { instance_id: i } => Some(i),
232            _ => None,
233        }
234    }
235}
236
237// Default impl is not super meaningful, but is useful in some cases
238impl Default for IdentityOrInstance {
239    fn default() -> Self {
240        Self::Identity(Default::default())
241    }
242}
243
244impl From<&str> for IdentityOrInstance {
245    fn from(value: &str) -> Self {
246        Self::Identity(Identity::from(value))
247    }
248}
249
250impl From<&String> for IdentityOrInstance {
251    fn from(value: &String) -> Self {
252        Self::Identity(Identity::from(value))
253    }
254}
255
256impl From<String> for IdentityOrInstance {
257    fn from(value: String) -> Self {
258        Self::Identity(Identity::from(value))
259    }
260}
261
262impl From<i64> for IdentityOrInstance {
263    fn from(value: i64) -> Self {
264        Self::Identity(Identity::from(value))
265    }
266}
267
268impl From<Identity> for IdentityOrInstance {
269    fn from(value: Identity) -> Self {
270        Self::Identity(value)
271    }
272}
273
274impl From<InstanceId> for IdentityOrInstance {
275    fn from(value: InstanceId) -> Self {
276        Self::InstanceId { instance_id: value }
277    }
278}
279
280impl PartialEq<i64> for IdentityOrInstance {
281    fn eq(&self, other: &i64) -> bool {
282        self.as_id().as_ref() == Some(other)
283    }
284}
285
286impl PartialEq<str> for IdentityOrInstance {
287    fn eq(&self, other: &str) -> bool {
288        self.as_external_id().map(|a| a.as_str()) == Some(other)
289    }
290}
291
292impl PartialEq<InstanceId> for IdentityOrInstance {
293    fn eq(&self, other: &InstanceId) -> bool {
294        self.as_instance_id() == Some(other)
295    }
296}
297
298impl PartialEq<Identity> for IdentityOrInstance {
299    fn eq(&self, other: &Identity) -> bool {
300        self.as_identity() == Some(other)
301    }
302}
303
304impl FromErrorDetail for IdentityOrInstance {
305    fn from_detail(detail: &HashMap<String, Box<IntegerStringOrObject>>) -> Option<Self> {
306        if let Some(object) = ApiErrorDetail::get_object(detail, "instanceId") {
307            match (
308                ApiErrorDetail::get_string(object, "space"),
309                ApiErrorDetail::get_string(object, "externalId"),
310            ) {
311                (Some(space), Some(external_id)) => Some(Self::InstanceId {
312                    instance_id: InstanceId {
313                        space: space.to_owned(),
314                        external_id: external_id.to_owned(),
315                    },
316                }),
317                _ => None,
318            }
319        } else {
320            Identity::from_detail(detail).map(Self::Identity)
321        }
322    }
323}
324
325/// Serializable wrapper around a list of identities.
326/// This implements serialize for individual strings or integers.
327/// as well as lists of these. This is useful for the many endpoints that
328/// accept lists of identities.
329pub struct IdentityList<R>(R);
330
331impl<'a, R> Chunkable<'a> for IdentityList<R>
332where
333    R: Chunkable<'a>,
334{
335    type Chunk = IdentityList<R::Chunk>;
336
337    fn as_chunks(&'a self, chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
338        self.0.as_chunks(chunk_size).map(IdentityList)
339    }
340}
341
342impl<R> From<R> for IdentityList<R> {
343    fn from(value: R) -> Self {
344        IdentityList(value)
345    }
346}
347
348/// Serializable wrapper around a list of identity or instance IDs.
349/// This implements serialize for individual strings, integers or instance IDs,
350/// as well as lists of these.
351/// This is useful for the many endpoints that accept lists of identities.
352pub struct IdentityOrInstanceList<R>(R);
353
354impl<T> Serialize for IdentityOrInstanceList<T>
355where
356    IdentityList<T>: Serialize,
357    T: Copy,
358{
359    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
360    where
361        S: serde::Serializer,
362    {
363        IdentityList(self.0).serialize(serializer)
364    }
365}
366
367impl<'a, R> Chunkable<'a> for IdentityOrInstanceList<R>
368where
369    R: Chunkable<'a>,
370{
371    type Chunk = IdentityOrInstanceList<R::Chunk>;
372
373    fn as_chunks(&'a self, chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
374        self.0.as_chunks(chunk_size).map(IdentityOrInstanceList)
375    }
376}
377
378impl<R> From<R> for IdentityOrInstanceList<R> {
379    fn from(value: R) -> Self {
380        IdentityOrInstanceList(value)
381    }
382}
383
384macro_rules! identity_list_ser_directly {
385    ($r:ident, $t:ty) => {
386        impl Serialize for $r<$t> {
387            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
388            where
389                S: serde::Serializer,
390            {
391                self.0.serialize(serializer)
392            }
393        }
394    };
395    ($r:ident, $t:ty, $n:ident) => {
396        impl<const $n: usize> Serialize for $r<$t> {
397            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
398            where
399                S: serde::Serializer,
400            {
401                self.0.serialize(serializer)
402            }
403        }
404    };
405}
406
407identity_list_ser_directly!(IdentityList, &Vec<Identity>);
408identity_list_ser_directly!(IdentityOrInstanceList, &Vec<IdentityOrInstance>);
409identity_list_ser_directly!(IdentityList, &Vec<CogniteExternalId>);
410identity_list_ser_directly!(IdentityList, &Vec<CogniteId>);
411identity_list_ser_directly!(IdentityList, &Vec<&Identity>);
412identity_list_ser_directly!(IdentityOrInstanceList, &Vec<&IdentityOrInstance>);
413identity_list_ser_directly!(IdentityList, &Vec<&CogniteExternalId>);
414identity_list_ser_directly!(IdentityList, &Vec<&CogniteId>);
415identity_list_ser_directly!(IdentityList, &[Identity]);
416identity_list_ser_directly!(IdentityOrInstanceList, &[IdentityOrInstance]);
417identity_list_ser_directly!(IdentityList, &[CogniteExternalId]);
418identity_list_ser_directly!(IdentityList, &[CogniteId]);
419identity_list_ser_directly!(IdentityList, &[&Identity]);
420identity_list_ser_directly!(IdentityOrInstanceList, &[&IdentityOrInstance]);
421identity_list_ser_directly!(IdentityList, &[&CogniteExternalId]);
422identity_list_ser_directly!(IdentityList, &[&CogniteId]);
423identity_list_ser_directly!(IdentityList, &[Identity; N], N);
424identity_list_ser_directly!(IdentityOrInstanceList, &[IdentityOrInstance; N], N);
425identity_list_ser_directly!(IdentityList, &[CogniteExternalId; N], N);
426identity_list_ser_directly!(IdentityList, &[CogniteId; N], N);
427identity_list_ser_directly!(IdentityList, &[&Identity; N], N);
428identity_list_ser_directly!(IdentityOrInstanceList, &[&IdentityOrInstance; N], N);
429identity_list_ser_directly!(IdentityList, &[&CogniteExternalId; N], N);
430identity_list_ser_directly!(IdentityList, &[&CogniteId; N], N);
431
432#[derive(Serialize)]
433#[serde(rename_all = "camelCase")]
434struct ExternalIdRef<'a, T> {
435    external_id: &'a T,
436}
437
438macro_rules! identity_list_ser_external_id {
439    ($t:ty) => {
440        impl Serialize for IdentityList<$t> {
441            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
442            where
443                S: serde::Serializer,
444            {
445                let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
446                for id in self.0.iter() {
447                    seq.serialize_element(&ExternalIdRef { external_id: id })?;
448                }
449                seq.end()
450            }
451        }
452    };
453
454    ($t:ty, $n:ident) => {
455        impl<const $n: usize> Serialize for IdentityList<$t> {
456            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
457            where
458                S: serde::Serializer,
459            {
460                let mut seq = serializer.serialize_seq(Some($n))?;
461                for id in self.0.iter() {
462                    seq.serialize_element(&ExternalIdRef { external_id: id })?;
463                }
464                seq.end()
465            }
466        }
467    };
468}
469identity_list_ser_external_id!(&Vec<String>);
470identity_list_ser_external_id!(&[String]);
471identity_list_ser_external_id!(&[String; N], N);
472identity_list_ser_external_id!(&Vec<&String>);
473identity_list_ser_external_id!(&[&String]);
474identity_list_ser_external_id!(&[&String; N], N);
475identity_list_ser_external_id!(&Vec<&str>);
476identity_list_ser_external_id!(&[&str]);
477identity_list_ser_external_id!(&[&str; N], N);
478
479macro_rules! identity_list_ser_id {
480    ($t:ty) => {
481        impl Serialize for IdentityList<$t> {
482            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
483            where
484                S: serde::Serializer,
485            {
486                let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
487                for id in self.0.iter() {
488                    seq.serialize_element(&CogniteId { id: *id })?;
489                }
490                seq.end()
491            }
492        }
493    };
494
495    ($t:ty, $n:ident) => {
496        impl<const N: usize> Serialize for IdentityList<$t> {
497            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
498            where
499                S: serde::Serializer,
500            {
501                let mut seq = serializer.serialize_seq(Some(N))?;
502                for id in self.0.iter() {
503                    seq.serialize_element(&CogniteId { id: *id })?;
504                }
505                seq.end()
506            }
507        }
508    };
509}
510
511identity_list_ser_id!(&Vec<i64>);
512identity_list_ser_id!(&[i64]);
513identity_list_ser_id!(&[i64; N], N);
514
515#[derive(Serialize)]
516#[serde(rename_all = "camelCase")]
517struct InstanceIdRef<'a> {
518    instance_id: &'a InstanceId,
519}
520
521macro_rules! identity_list_ser_instance_id {
522    ($t:ty) => {
523        impl Serialize for IdentityOrInstanceList<$t> {
524            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
525            where
526                S: serde::Serializer,
527            {
528                let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
529                for id in self.0.iter() {
530                    seq.serialize_element(&InstanceIdRef { instance_id: id })?;
531                }
532                seq.end()
533            }
534        }
535    };
536
537    ($t:ty, $n:ident) => {
538        impl<const N: usize> Serialize for IdentityOrInstanceList<$t> {
539            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
540            where
541                S: serde::Serializer,
542            {
543                let mut seq = serializer.serialize_seq(Some(N))?;
544                for id in self.0.iter() {
545                    seq.serialize_element(&InstanceIdRef { instance_id: id })?;
546                }
547                seq.end()
548            }
549        }
550    };
551}
552
553identity_list_ser_instance_id!(&Vec<InstanceId>);
554identity_list_ser_instance_id!(&[InstanceId]);
555identity_list_ser_instance_id!(&[InstanceId; N], N);
556identity_list_ser_instance_id!(&Vec<&InstanceId>);
557identity_list_ser_instance_id!(&[&InstanceId]);
558identity_list_ser_instance_id!(&[&InstanceId; N], N);
559
560macro_rules! identity_list_ser_single {
561    ($r:ident, $t:ty) => {
562        impl Serialize for $r<$t> {
563            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
564            where
565                S: serde::Serializer,
566            {
567                Serialize::serialize(&$r(&[self.0]), serializer)
568            }
569        }
570    };
571}
572
573macro_rules! impl_chunk_single {
574    ($t:ty) => {
575        impl<'a> Chunkable<'a> for $t {
576            type Chunk = &'a $t;
577            fn as_chunks(&'a self, _chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
578                std::iter::once(self)
579            }
580        }
581    };
582}
583
584identity_list_ser_single!(IdentityList, i64);
585
586impl Chunkable<'_> for i64 {
587    type Chunk = i64;
588    fn as_chunks(&self, _chunk_size: usize) -> impl Iterator<Item = Self::Chunk> {
589        std::iter::once(*self)
590    }
591}
592
593identity_list_ser_single!(IdentityList, &str);
594impl_chunk_single!(&'a str);
595identity_list_ser_single!(IdentityList, &String);
596impl_chunk_single!(&'a String);
597identity_list_ser_single!(IdentityOrInstanceList, &InstanceId);
598impl_chunk_single!(&'a InstanceId);
599identity_list_ser_single!(IdentityList, &Identity);
600impl_chunk_single!(&'a Identity);
601identity_list_ser_single!(IdentityOrInstanceList, &IdentityOrInstance);
602impl_chunk_single!(&'a IdentityOrInstance);
603identity_list_ser_single!(IdentityList, &CogniteExternalId);
604impl_chunk_single!(&'a CogniteExternalId);
605identity_list_ser_single!(IdentityList, &CogniteId);
606impl_chunk_single!(&'a CogniteId);
607
608#[cfg(test)]
609mod tests {
610    use crate::{
611        models::instances::InstanceId, Chunkable, CogniteExternalId, CogniteId, IdentityOrInstance,
612        IdentityOrInstanceList,
613    };
614
615    use super::{Identity, IdentityList};
616
617    macro_rules! test_identity_list {
618        ($v:expr) => {
619            let v = $v;
620            let list = IdentityList::from(&v);
621            let serialized = serde_json::to_string(&list).unwrap();
622            let deserialized: Vec<Identity> = serde_json::from_str(&serialized).unwrap();
623            let reserialized = serde_json::to_string(&deserialized).unwrap();
624            assert_eq!(serialized, reserialized);
625
626            let chunks = list.as_chunks(2).collect::<Vec<_>>();
627            assert_eq!(chunks.len(), 2);
628            for chunk in chunks {
629                assert_eq!(chunk.0.len(), 2);
630            }
631        };
632    }
633
634    macro_rules! test_identity_or_instance_list {
635        ($v:expr) => {
636            let v = $v;
637            let list = IdentityOrInstanceList::from(&v);
638            let serialized = serde_json::to_string(&list).unwrap();
639            let deserialized: Vec<IdentityOrInstance> = serde_json::from_str(&serialized).unwrap();
640            let reserialized = serde_json::to_string(&deserialized).unwrap();
641            assert_eq!(serialized, reserialized);
642
643            let chunks = list.as_chunks(2).collect::<Vec<_>>();
644            assert_eq!(chunks.len(), 2);
645            for chunk in chunks {
646                assert_eq!(chunk.0.len(), 2);
647            }
648        };
649    }
650
651    #[test]
652    fn test_identity_list() {
653        test_identity_list!(vec![
654            Identity::external_id("ext1"),
655            Identity::id(2),
656            Identity::external_id("ext3"),
657            Identity::id(4)
658        ]);
659        test_identity_list!(vec![
660            CogniteExternalId::from("ext1"),
661            CogniteExternalId::from("ext2"),
662            CogniteExternalId::from("ext3"),
663            CogniteExternalId::from("ext4")
664        ]);
665        test_identity_list!(vec![
666            CogniteId::from(1),
667            CogniteId::from(2),
668            CogniteId::from(3),
669            CogniteId::from(4)
670        ]);
671        test_identity_list!([
672            Identity::external_id("ext1"),
673            Identity::id(2),
674            Identity::external_id("ext3"),
675            Identity::id(4)
676        ]);
677
678        test_identity_list!(vec!["ext1", "ext2", "ext3", "ext4"]);
679        test_identity_list!(vec![1i64, 2, 3, 4]);
680        test_identity_list!(["ext1", "ext2", "ext3", "ext4"]);
681        test_identity_list!([1i64, 2, 3, 4]);
682    }
683
684    #[test]
685    fn test_identity_or_instance_list() {
686        use crate::models::instances::InstanceId;
687        test_identity_or_instance_list!(vec![
688            IdentityOrInstance::external_id("ext1"),
689            IdentityOrInstance::id(2),
690            IdentityOrInstance::instance_id(InstanceId {
691                space: "space1".to_owned(),
692                external_id: "inst1".to_owned()
693            }),
694            IdentityOrInstance::id(4)
695        ]);
696        test_identity_or_instance_list!([
697            IdentityOrInstance::external_id("ext1"),
698            IdentityOrInstance::id(2),
699            IdentityOrInstance::instance_id(InstanceId {
700                space: "space1".to_owned(),
701                external_id: "inst1".to_owned()
702            }),
703            IdentityOrInstance::id(4)
704        ]);
705        test_identity_or_instance_list!(vec![
706            InstanceId {
707                space: "space1".to_owned(),
708                external_id: "inst1".to_owned()
709            },
710            InstanceId {
711                space: "space2".to_owned(),
712                external_id: "inst2".to_owned()
713            },
714            InstanceId {
715                space: "space3".to_owned(),
716                external_id: "inst3".to_owned()
717            },
718            InstanceId {
719                space: "space4".to_owned(),
720                external_id: "inst4".to_owned()
721            }
722        ]);
723        test_identity_or_instance_list!(["ext1", "ext2", "ext3", "ext4"]);
724        test_identity_or_instance_list!([1i64, 2, 3, 4]);
725    }
726
727    #[test]
728    fn test_identity_list_single() {
729        let x = "extId";
730        let list = IdentityList::from(x);
731        let serialized = serde_json::to_string(&list).unwrap();
732        assert_eq!(serialized, r#"[{"externalId":"extId"}]"#);
733
734        let x = 42i64;
735        let list = IdentityList::from(x);
736        let serialized = serde_json::to_string(&list).unwrap();
737        assert_eq!(serialized, r#"[{"id":42}]"#);
738
739        let x = InstanceId {
740            space: "space1".to_owned(),
741            external_id: "inst1".to_owned(),
742        };
743        let list = IdentityOrInstanceList::from(&x);
744        let serialized = serde_json::to_string(&list).unwrap();
745        assert_eq!(
746            serialized,
747            r#"[{"instanceId":{"space":"space1","externalId":"inst1"}}]"#
748        );
749    }
750}