Skip to main content

fiberplane_models/
proxies.rs

1use super::data_sources::{DataSource, DataSourceStatus};
2use super::names::{InvalidName, Name};
3use super::providers::Error;
4use crate::blobs::Blob;
5use crate::notebooks::Cell;
6use crate::providers::{ConfigSchema, ProviderConfig, SupportedQueryType};
7use crate::timestamps::Timestamp;
8use base64uuid::{Base64Uuid, InvalidId};
9#[cfg(feature = "fp-bindgen")]
10use fp_bindgen::prelude::Serializable;
11use serde::{Deserialize, Serialize};
12use std::fmt::{self, Debug, Formatter};
13use std::{convert::TryFrom, str::FromStr};
14use strum_macros::Display;
15use typed_builder::TypedBuilder;
16
17#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, TypedBuilder)]
18#[non_exhaustive]
19#[serde(rename_all = "camelCase")]
20pub struct Proxy {
21    #[builder(setter(into))]
22    pub id: Base64Uuid,
23
24    pub name: Name,
25
26    pub status: ProxyStatus,
27
28    #[builder(default)]
29    pub data_sources: Vec<DataSource>,
30
31    #[builder(default, setter(into, strip_option))]
32    #[serde(default, skip_serializing_if = "Option::is_none")]
33    pub token: Option<ProxyToken>,
34
35    #[builder(default, setter(into, strip_option))]
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub description: Option<String>,
38
39    #[builder(setter(into))]
40    pub created_at: Timestamp,
41
42    #[builder(setter(into))]
43    pub updated_at: Timestamp,
44}
45
46#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, TypedBuilder)]
47#[cfg_attr(
48    feature = "fp-bindgen",
49    derive(Serializable),
50    fp(rust_module = "fiberplane_models::proxies")
51)]
52#[non_exhaustive]
53#[serde(rename_all = "camelCase")]
54pub struct ProxySummary {
55    #[builder(setter(into))]
56    pub id: Base64Uuid,
57
58    pub name: Name,
59
60    pub status: ProxyStatus,
61}
62
63impl From<Proxy> for ProxySummary {
64    fn from(proxy: Proxy) -> Self {
65        Self {
66            id: proxy.id,
67            name: proxy.name,
68            status: proxy.status,
69        }
70    }
71}
72
73#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy, Display)]
74#[cfg_attr(
75    feature = "fp-bindgen",
76    derive(Serializable),
77    fp(rust_module = "fiberplane_models::proxies")
78)]
79#[non_exhaustive]
80#[serde(rename_all = "snake_case")]
81pub enum ProxyStatus {
82    Connected,
83    Disconnected,
84}
85
86#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, TypedBuilder)]
87#[cfg_attr(
88    feature = "fp-bindgen",
89    derive(Serializable),
90    fp(rust_module = "fiberplane_models::proxies")
91)]
92#[non_exhaustive]
93#[serde(rename_all = "camelCase")]
94pub struct NewProxy {
95    pub name: Name,
96
97    #[builder(default, setter(into, strip_option))]
98    pub description: Option<String>,
99}
100
101#[derive(Debug, thiserror::Error, PartialEq, Eq)]
102#[cfg_attr(
103    feature = "fp-bindgen",
104    derive(Serializable),
105    fp(rust_module = "fiberplane_models::proxies")
106)]
107#[non_exhaustive]
108pub enum InvalidProxyToken {
109    #[error("Invalid workspace ID")]
110    InvalidWorkspaceId(#[from] InvalidId),
111    #[error("Invalid proxy name")]
112    InvalidProxyName(#[from] InvalidName),
113    #[error("Missing token")]
114    MissingToken,
115}
116
117/// This represents the auth token that is generated by the API and used
118/// by the proxy to authenticate its websocket connection.
119#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, TypedBuilder)]
120#[cfg_attr(
121    feature = "fp-bindgen",
122    derive(Serializable),
123    fp(rust_module = "fiberplane_models::proxies")
124)]
125#[non_exhaustive]
126#[serde(try_from = "&str", into = "String")]
127pub struct ProxyToken {
128    #[builder(setter(into))]
129    pub workspace_id: Base64Uuid,
130
131    pub proxy_name: Name,
132
133    #[builder(default, setter(into))]
134    pub token: String,
135}
136
137impl Debug for ProxyToken {
138    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
139        f.debug_struct("ProxyToken")
140            .field("workspace_id", &self.workspace_id)
141            .field("proxy_name", &self.proxy_name)
142            .field("token", &"[REDACTED]")
143            .finish()
144    }
145}
146
147impl From<ProxyToken> for String {
148    fn from(token: ProxyToken) -> Self {
149        format!(
150            "{}:{}:{}",
151            token.workspace_id, token.proxy_name, token.token
152        )
153    }
154}
155
156impl FromStr for ProxyToken {
157    type Err = InvalidProxyToken;
158
159    fn from_str(s: &str) -> Result<Self, Self::Err> {
160        let mut parts = s.split(':');
161
162        let workspace_id = parts.next().unwrap_or_default().parse::<Base64Uuid>()?;
163        let proxy_name = Name::new(parts.next().unwrap_or_default())?;
164        let token = parts
165            .next()
166            .ok_or(InvalidProxyToken::MissingToken)?
167            .to_string();
168
169        Ok(ProxyToken {
170            workspace_id,
171            proxy_name,
172            token,
173        })
174    }
175}
176
177impl TryFrom<&str> for ProxyToken {
178    type Error = InvalidProxyToken;
179
180    fn try_from(s: &str) -> Result<Self, Self::Error> {
181        Self::from_str(s)
182    }
183}
184
185#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, TypedBuilder)]
186#[cfg_attr(
187    feature = "fp-bindgen",
188    derive(Serializable),
189    fp(rust_module = "fiberplane_models::proxies")
190)]
191#[non_exhaustive]
192#[serde(rename_all = "camelCase")]
193pub struct CreateCellsApiRequest {
194    pub response: Blob,
195
196    #[builder(setter(into))]
197    pub query_type: String,
198}
199
200#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, TypedBuilder)]
201#[cfg_attr(
202    feature = "fp-bindgen",
203    derive(Serializable),
204    fp(rust_module = "fiberplane_models::proxies")
205)]
206#[non_exhaustive]
207#[serde(rename_all = "camelCase")]
208pub struct ExtractDataApiRequest {
209    pub response: Blob,
210
211    #[builder(setter(into))]
212    pub mime_type: String,
213
214    #[builder(default, setter(into, strip_option))]
215    #[serde(default, skip_serializing_if = "Option::is_none")]
216    pub query: Option<String>,
217}
218
219/// Messages sent to the Proxy
220#[derive(Debug, Deserialize, Serialize, TypedBuilder)]
221#[cfg_attr(
222    feature = "fp-bindgen",
223    derive(Serializable),
224    fp(rust_module = "fiberplane_models::proxies")
225)]
226#[non_exhaustive]
227#[serde(rename_all = "camelCase")]
228pub struct ServerMessage {
229    #[builder(setter(into))]
230    pub op_id: Base64Uuid,
231
232    pub data_source_name: Name,
233
234    pub protocol_version: u8,
235
236    #[serde(flatten)]
237    pub payload: ServerMessagePayload,
238}
239
240impl ServerMessage {
241    pub fn deserialize_msgpack(
242        input: impl AsRef<[u8]>,
243    ) -> Result<ServerMessage, rmp_serde::decode::Error> {
244        rmp_serde::from_slice(input.as_ref())
245    }
246
247    pub fn serialize_msgpack(&self) -> Vec<u8> {
248        rmp_serde::to_vec(&self).expect("MessgePack serialization error")
249    }
250
251    pub fn op_id(&self) -> Option<Base64Uuid> {
252        Some(self.op_id)
253    }
254
255    fn payload_with_header(
256        payload: ServerMessagePayload,
257        data_source_name: Name,
258        protocol_version: u8,
259        op_id: Base64Uuid,
260    ) -> Self {
261        Self {
262            op_id,
263            data_source_name,
264            protocol_version,
265            payload,
266        }
267    }
268
269    pub fn new_invoke_proxy_request(
270        data: Vec<u8>,
271        data_source_name: Name,
272        protocol_version: u8,
273        op_id: Base64Uuid,
274    ) -> Self {
275        Self::payload_with_header(
276            ServerMessagePayload::Invoke(InvokeRequest { data }),
277            data_source_name,
278            protocol_version,
279            op_id,
280        )
281    }
282
283    pub fn new_create_cells_request(
284        data: Blob,
285        query_type: String,
286        data_source_name: Name,
287        protocol_version: u8,
288        op_id: Base64Uuid,
289    ) -> Self {
290        Self::payload_with_header(
291            ServerMessagePayload::CreateCells(CreateCellsRequest {
292                response: data,
293                query_type,
294            }),
295            data_source_name,
296            protocol_version,
297            op_id,
298        )
299    }
300
301    pub fn new_extract_data_request(
302        data: Blob,
303        mime_type: String,
304        query: Option<String>,
305        data_source_name: Name,
306        protocol_version: u8,
307        op_id: Base64Uuid,
308    ) -> Self {
309        Self::payload_with_header(
310            ServerMessagePayload::ExtractData(ExtractDataRequest {
311                response: data,
312                mime_type,
313                query,
314            }),
315            data_source_name,
316            protocol_version,
317            op_id,
318        )
319    }
320
321    pub fn new_get_config_schema_request(
322        data_source_name: Name,
323        protocol_version: u8,
324        op_id: Base64Uuid,
325    ) -> Self {
326        Self::payload_with_header(
327            ServerMessagePayload::GetConfigSchema(GetConfigSchemaRequest {}),
328            data_source_name,
329            protocol_version,
330            op_id,
331        )
332    }
333
334    pub fn new_get_supported_query_types_request(
335        config: ProviderConfig,
336        data_source_name: Name,
337        protocol_version: u8,
338        op_id: Base64Uuid,
339    ) -> Self {
340        Self::payload_with_header(
341            ServerMessagePayload::GetSupportedQueryTypes(GetSupportedQueryTypesRequest { config }),
342            data_source_name,
343            protocol_version,
344            op_id,
345        )
346    }
347}
348
349/// Messages sent to the Proxy
350#[derive(Debug, Deserialize, Serialize)]
351#[cfg_attr(
352    feature = "fp-bindgen",
353    derive(Serializable),
354    fp(rust_module = "fiberplane_models::proxies")
355)]
356#[non_exhaustive]
357#[serde(tag = "type", rename_all = "camelCase")]
358pub enum ServerMessagePayload {
359    /// A request to call the `invoke` or `invoke2` exported binding
360    #[serde(rename = "invokeProxy")] // Backwards compatibility alias
361    Invoke(InvokeRequest),
362    /// A request to call the `create_cells` exported binding
363    CreateCells(CreateCellsRequest),
364    /// A request to call the `extract_data` exported binding
365    ExtractData(ExtractDataRequest),
366    /// A request to call the `get_config_schema` exported binding
367    GetConfigSchema(GetConfigSchemaRequest),
368    /// A request to call the `get_supported_query_types` exported binding
369    GetSupportedQueryTypes(GetSupportedQueryTypesRequest),
370}
371
372#[derive(Deserialize, Serialize, TypedBuilder)]
373#[cfg_attr(
374    feature = "fp-bindgen",
375    derive(Serializable),
376    fp(rust_module = "fiberplane_models::proxies")
377)]
378#[non_exhaustive]
379#[serde(rename_all = "camelCase")]
380pub struct InvokeRequest {
381    #[serde(with = "serde_bytes")]
382    pub data: Vec<u8>,
383}
384
385impl Debug for InvokeRequest {
386    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
387        f.debug_struct("InvokeRequest")
388            .field("data", &format!("[{} bytes]", self.data.len()))
389            .finish()
390    }
391}
392
393#[derive(Deserialize, Serialize, TypedBuilder)]
394#[cfg_attr(
395    feature = "fp-bindgen",
396    derive(Serializable),
397    fp(rust_module = "fiberplane_models::proxies")
398)]
399#[non_exhaustive]
400#[serde(rename_all = "camelCase")]
401pub struct CreateCellsRequest {
402    pub response: Blob,
403
404    #[builder(setter(into))]
405    pub query_type: String,
406}
407
408impl Debug for CreateCellsRequest {
409    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
410        f.debug_struct("CreateCellsRequest")
411            .field("query_type", &self.query_type)
412            .field("response", &format!("[{} bytes]", self.response.data.len()))
413            .finish()
414    }
415}
416
417#[derive(Deserialize, Serialize, TypedBuilder)]
418#[cfg_attr(
419    feature = "fp-bindgen",
420    derive(Serializable),
421    fp(rust_module = "fiberplane_models::proxies")
422)]
423#[non_exhaustive]
424#[serde(rename_all = "camelCase")]
425pub struct ExtractDataRequest {
426    pub response: Blob,
427
428    #[builder(setter(into))]
429    pub mime_type: String,
430
431    #[builder(default, setter(into, strip_option))]
432    #[serde(default, skip_serializing_if = "Option::is_none")]
433    pub query: Option<String>,
434}
435
436impl Debug for ExtractDataRequest {
437    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
438        f.debug_struct("ExtractDataRequest")
439            .field("mime_type", &self.mime_type)
440            .field("query", &self.query)
441            .field("response", &format!("[{} bytes]", self.response.data.len()))
442            .finish()
443    }
444}
445
446#[derive(Deserialize, Serialize)]
447#[cfg_attr(
448    feature = "fp-bindgen",
449    derive(Serializable),
450    fp(rust_module = "fiberplane_models::proxies")
451)]
452#[non_exhaustive]
453#[serde(rename_all = "camelCase")]
454pub struct GetConfigSchemaRequest {}
455
456impl Debug for GetConfigSchemaRequest {
457    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
458        f.debug_struct("ConfigSchemaRequest").finish()
459    }
460}
461
462#[derive(Deserialize, Serialize, Debug, TypedBuilder)]
463#[cfg_attr(
464    feature = "fp-bindgen",
465    derive(Serializable),
466    fp(rust_module = "fiberplane_models::proxies")
467)]
468#[non_exhaustive]
469#[serde(rename_all = "camelCase")]
470pub struct GetSupportedQueryTypesRequest {
471    pub config: ProviderConfig,
472}
473
474/// Messages sent from the Proxy
475#[derive(Debug, Deserialize, Serialize, TypedBuilder)]
476#[cfg_attr(
477    feature = "fp-bindgen",
478    derive(Serializable),
479    fp(rust_module = "fiberplane_models::proxies")
480)]
481#[non_exhaustive]
482#[serde(rename_all = "camelCase")]
483pub struct ProxyMessage {
484    #[builder(default, setter(into, strip_option))]
485    pub op_id: Option<Base64Uuid>,
486
487    #[serde(flatten)]
488    pub payload: ProxyMessagePayload,
489}
490
491impl ProxyMessage {
492    fn response(payload: ProxyMessagePayload, op_id: Base64Uuid) -> Self {
493        Self {
494            op_id: Some(op_id),
495            payload,
496        }
497    }
498
499    fn notification(payload: ProxyMessagePayload) -> Self {
500        Self {
501            op_id: None,
502            payload,
503        }
504    }
505
506    pub fn new_error_response(error: Error, op_id: Base64Uuid) -> Self {
507        Self::response(ProxyMessagePayload::Error(ErrorMessage { error }), op_id)
508    }
509    pub fn new_invoke_proxy_response(data: Vec<u8>, op_id: Base64Uuid) -> Self {
510        Self::response(
511            ProxyMessagePayload::InvokeProxyResponse(InvokeProxyResponseMessage { data }),
512            op_id,
513        )
514    }
515    pub fn new_create_cells_response(cells: Result<Vec<Cell>, Error>, op_id: Base64Uuid) -> Self {
516        Self::response(
517            ProxyMessagePayload::CreateCellsResponse(CreateCellsResponseMessage { cells }),
518            op_id,
519        )
520    }
521    pub fn new_extract_data_response(data: Result<Blob, Error>, op_id: Base64Uuid) -> Self {
522        Self::response(
523            ProxyMessagePayload::ExtractDataResponse(ExtractDataResponseMessage { data }),
524            op_id,
525        )
526    }
527    pub fn new_config_schema_response(schema: ConfigSchema, op_id: Base64Uuid) -> Self {
528        Self::response(
529            ProxyMessagePayload::GetConfigSchemaResponse(GetConfigSchemaResponseMessage { schema }),
530            op_id,
531        )
532    }
533    pub fn new_supported_query_types_response(
534        queries: Vec<SupportedQueryType>,
535        op_id: Base64Uuid,
536    ) -> Self {
537        Self::response(
538            ProxyMessagePayload::GetSupportedQueryTypesResponse(
539                GetSupportedQueryTypesResponseMessage { queries },
540            ),
541            op_id,
542        )
543    }
544    pub fn new_set_data_sources_notification(data_sources: Vec<UpsertProxyDataSource>) -> Self {
545        Self::notification(ProxyMessagePayload::SetDataSources(SetDataSourcesMessage {
546            data_sources,
547        }))
548    }
549}
550
551/// Messages sent from the Proxy
552#[derive(Debug, Deserialize, Serialize)]
553#[cfg_attr(
554    feature = "fp-bindgen",
555    derive(Serializable),
556    fp(rust_module = "fiberplane_models::proxies")
557)]
558#[non_exhaustive]
559#[serde(tag = "type", rename_all = "camelCase")]
560pub enum ProxyMessagePayload {
561    SetDataSources(SetDataSourcesMessage),
562    InvokeProxyResponse(InvokeProxyResponseMessage),
563    CreateCellsResponse(CreateCellsResponseMessage),
564    ExtractDataResponse(ExtractDataResponseMessage),
565    GetConfigSchemaResponse(GetConfigSchemaResponseMessage),
566    GetSupportedQueryTypesResponse(GetSupportedQueryTypesResponseMessage),
567    Error(ErrorMessage),
568}
569
570impl From<(ErrorMessage, Base64Uuid)> for ProxyMessage {
571    fn from((message, op_id): (ErrorMessage, Base64Uuid)) -> Self {
572        Self::response(ProxyMessagePayload::Error(message), op_id)
573    }
574}
575
576#[derive(Deserialize, Serialize, TypedBuilder)]
577#[cfg_attr(
578    feature = "fp-bindgen",
579    derive(Serializable),
580    fp(rust_module = "fiberplane_models::proxies")
581)]
582#[non_exhaustive]
583#[serde(rename_all = "camelCase")]
584pub struct InvokeProxyResponseMessage {
585    #[serde(with = "serde_bytes")]
586    pub data: Vec<u8>,
587}
588
589impl Debug for InvokeProxyResponseMessage {
590    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
591        f.debug_struct("InvokeProxyResponseMessage")
592            .field("data", &format!("[{} bytes]", self.data.len()))
593            .finish()
594    }
595}
596
597#[derive(Deserialize, Serialize, Debug, TypedBuilder)]
598#[cfg_attr(
599    feature = "fp-bindgen",
600    derive(Serializable),
601    fp(rust_module = "fiberplane_models::proxies")
602)]
603#[non_exhaustive]
604#[serde(rename_all = "camelCase")]
605pub struct ExtractDataResponseMessage {
606    pub data: Result<Blob, Error>,
607}
608
609#[derive(Deserialize, Serialize, Debug, TypedBuilder)]
610#[cfg_attr(
611    feature = "fp-bindgen",
612    derive(Serializable),
613    fp(rust_module = "fiberplane_models::proxies")
614)]
615#[non_exhaustive]
616#[serde(rename_all = "camelCase")]
617pub struct CreateCellsResponseMessage {
618    pub cells: Result<Vec<Cell>, Error>,
619}
620
621#[derive(Deserialize, Serialize, Debug, TypedBuilder)]
622#[cfg_attr(
623    feature = "fp-bindgen",
624    derive(Serializable),
625    fp(rust_module = "fiberplane_models::proxies")
626)]
627#[non_exhaustive]
628#[serde(rename_all = "camelCase")]
629pub struct GetConfigSchemaResponseMessage {
630    pub schema: ConfigSchema,
631}
632
633#[derive(Deserialize, Serialize, Debug, TypedBuilder)]
634#[cfg_attr(
635    feature = "fp-bindgen",
636    derive(Serializable),
637    fp(rust_module = "fiberplane_models::proxies")
638)]
639#[non_exhaustive]
640#[serde(rename_all = "camelCase")]
641pub struct GetSupportedQueryTypesResponseMessage {
642    pub queries: Vec<SupportedQueryType>,
643}
644
645#[derive(Debug, Deserialize, Serialize, TypedBuilder)]
646#[cfg_attr(
647    feature = "fp-bindgen",
648    derive(Serializable),
649    fp(rust_module = "fiberplane_models::proxies")
650)]
651#[non_exhaustive]
652#[serde(rename_all = "camelCase")]
653pub struct ErrorMessage {
654    pub error: Error,
655}
656
657impl ProxyMessage {
658    pub fn deserialize_msgpack(
659        input: impl AsRef<[u8]>,
660    ) -> Result<ProxyMessage, rmp_serde::decode::Error> {
661        rmp_serde::from_slice(input.as_ref())
662    }
663
664    pub fn serialize_msgpack(&self) -> Vec<u8> {
665        rmp_serde::to_vec_named(&self).expect("MessgePack serialization error")
666    }
667
668    pub fn op_id(&self) -> Option<Base64Uuid> {
669        self.op_id
670    }
671}
672
673#[derive(Debug, Deserialize, Serialize, TypedBuilder)]
674#[cfg_attr(
675    feature = "fp-bindgen",
676    derive(Serializable),
677    fp(rust_module = "fiberplane_models::proxies")
678)]
679#[non_exhaustive]
680#[serde(rename_all = "camelCase")]
681pub struct SetDataSourcesMessage {
682    pub data_sources: Vec<UpsertProxyDataSource>,
683}
684
685#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone, TypedBuilder)]
686#[cfg_attr(
687    feature = "fp-bindgen",
688    derive(Serializable),
689    fp(rust_module = "fiberplane_models::proxies")
690)]
691#[non_exhaustive]
692#[serde(tag = "type", rename_all = "camelCase")]
693pub struct UpsertProxyDataSource {
694    pub name: Name,
695
696    #[builder(default, setter(into, strip_option))]
697    pub description: Option<String>,
698
699    #[builder(setter(into))]
700    pub provider_type: String,
701
702    #[builder(default)]
703    #[serde(default)]
704    pub protocol_version: u8,
705
706    #[serde(flatten)]
707    pub status: DataSourceStatus,
708}
709
710#[cfg(test)]
711mod tests {
712    use super::*;
713    use crate::providers::Error;
714
715    #[test]
716    fn serialization_deserialization() {
717        let data_sources = vec![
718            UpsertProxyDataSource {
719                name: Name::from_static("prometheus-prod"),
720                provider_type: "prometheus".to_string(),
721                protocol_version: 2,
722                description: Some("Production Prometheus".to_string()),
723                status: DataSourceStatus::Connected,
724            },
725            UpsertProxyDataSource {
726                name: Name::from_static("elasticsearch-prod"),
727                provider_type: "elasticsearch".to_string(),
728                protocol_version: 1,
729                description: None,
730                status: DataSourceStatus::Error(Error::NotFound),
731            },
732        ];
733        let message = ProxyMessage::new_set_data_sources_notification(data_sources.clone());
734        let serialized = message.serialize_msgpack();
735        let deserialized = ProxyMessage::deserialize_msgpack(serialized).unwrap();
736        if let ProxyMessage {
737            op_id: None,
738            payload: ProxyMessagePayload::SetDataSources(set_data_sources),
739        } = deserialized
740        {
741            assert_eq!(set_data_sources.data_sources, data_sources)
742        } else {
743            panic!("Unexpected message type");
744        }
745    }
746
747    #[test]
748    fn backwards_compatibility() {
749        // The test checks that an old message can be deserialized into a new one
750        mod old {
751            use crate::names::Name;
752            use base64uuid::Base64Uuid;
753            use serde::{Deserialize, Serialize};
754
755            #[derive(Debug, Deserialize, Serialize)]
756            #[serde(tag = "type", rename_all = "camelCase")]
757            pub enum ServerMessage {
758                InvokeProxy(InvokeProxyMessage),
759            }
760
761            #[derive(Debug, Deserialize, Serialize, Clone)]
762            #[serde(rename_all = "camelCase")]
763            pub struct InvokeProxyMessage {
764                pub op_id: Base64Uuid,
765                pub data_source_name: Name,
766                #[serde(with = "serde_bytes")]
767                pub data: Vec<u8>,
768                pub protocol_version: u8,
769            }
770        }
771
772        let op_id = Base64Uuid::parse_str("34edc58d-f8ec-4c95-bce0-c2ae8800e6ef").unwrap();
773        let data_source_name = Name::from_static("test-name");
774        let data = b"aieu".to_vec();
775        let old_message = old::InvokeProxyMessage {
776            op_id,
777            data_source_name,
778            protocol_version: 12,
779            data,
780        };
781
782        let new_message: ServerMessage = rmp_serde::from_slice(
783            &rmp_serde::to_vec_named(&old::ServerMessage::InvokeProxy(old_message.clone()))
784                .unwrap(),
785        )
786        .unwrap();
787
788        assert_eq!(new_message.op_id, old_message.op_id);
789        assert_eq!(new_message.data_source_name, old_message.data_source_name);
790        assert_eq!(new_message.protocol_version, old_message.protocol_version);
791
792        if let ServerMessagePayload::Invoke(response) = new_message.payload {
793            assert_eq!(response.data, old_message.data)
794        } else {
795            panic!("Wrong variant of ServerMessage deserialized. Expecting Invoke")
796        }
797    }
798}