1use std::collections::{HashMap, HashSet};
10use std::sync::atomic::{AtomicBool, Ordering};
11use std::sync::{Arc, Condvar, Mutex};
12use std::time::{Duration, SystemTime};
13
14use dex_protocol::dex::{
15 AttributeWrite, ChannelInfo, ChannelMessage, ConditionResults, ConditionStatus,
16 Context as ProtoContext, Kv, Value as ProtoValue, WriteStreamRequest,
17 flow_service_client::FlowServiceClient, value,
18};
19use tokio::runtime::Handle;
20use tonic::transport::Channel as TransportChannel;
21
22use crate::persistence::PersistenceKind;
23use crate::registry::{RegisteredFlow, decode_instance, physical_name};
24use crate::value_mapper;
25use crate::{
26 Attribute, AttributeMap, Channel, ChannelMap, FlowResult, HandlerError, HandlerResult, Stream,
27 Value,
28};
29
30#[derive(Clone, Copy, Eq, PartialEq)]
31pub(crate) enum InvocationMethod {
32 WaitFor,
33 Execute,
34 Rpc,
35}
36
37pub(crate) struct ContextInput {
38 pub(crate) method: InvocationMethod,
39 pub(crate) flow: RegisteredFlow,
40 pub(crate) metadata: ProtoContext,
41 pub(crate) attributes: Vec<Kv>,
42 pub(crate) locals: Vec<Kv>,
43 pub(crate) condition_results: Option<ConditionResults>,
44 pub(crate) channel_infos: HashMap<String, ChannelInfo>,
45}
46
47pub struct Context {
53 method: InvocationMethod,
54 flow: RegisteredFlow,
55 runtime_handle: Handle,
56 flow_service: FlowServiceClient<TransportChannel>,
57 metadata: ProtoContext,
58 attributes: HashMap<String, ProtoValue>,
59 locals: HashMap<String, ProtoValue>,
60 condition_results: Option<ConditionResults>,
61 channel_infos: HashMap<String, ChannelInfo>,
62 attribute_writes: HashMap<String, AttributeWrite>,
63 local_writes: HashMap<String, Kv>,
64 events: Vec<Kv>,
65 event_names: HashSet<String>,
66 publications: Vec<ChannelMessage>,
67 stream_writes: HashSet<usize>,
68 cancellation: InvocationCancellation,
69}
70
71impl Context {
72 pub(crate) fn new(
73 input: ContextInput,
74 runtime_handle: Handle,
75 flow_service: FlowServiceClient<TransportChannel>,
76 ) -> HandlerResult<Self> {
77 Ok(Self {
78 method: input.method,
79 flow: input.flow,
80 runtime_handle,
81 flow_service,
82 metadata: input.metadata,
83 attributes: map_values("Attribute", input.attributes)?,
84 locals: map_values("step-execution local", input.locals)?,
85 condition_results: input.condition_results,
86 channel_infos: input.channel_infos,
87 attribute_writes: HashMap::new(),
88 local_writes: HashMap::new(),
89 events: Vec::new(),
90 event_names: HashSet::new(),
91 publications: Vec::new(),
92 stream_writes: HashSet::new(),
93 cancellation: InvocationCancellation::new(),
94 })
95 }
96
97 pub fn flow_id(&self) -> &str {
99 &self.metadata.flow_id
100 }
101
102 pub fn run_id(&self) -> &str {
104 &self.metadata.run_id
105 }
106
107 pub fn flow_started_at(&self) -> SystemTime {
109 system_time(self.metadata.flow_started_timestamp)
110 }
111
112 pub fn step_execution_id(&self) -> &str {
114 &self.metadata.step_execution_id
115 }
116
117 pub fn from_step_execution_id(&self) -> &str {
119 &self.metadata.from_step_execution_id
120 }
121
122 pub fn first_attempt_at(&self) -> SystemTime {
124 system_time(self.metadata.first_attempt_timestamp)
125 }
126
127 pub fn attempt(&self) -> u32 {
129 u32::try_from(self.metadata.attempt).unwrap_or_default()
130 }
131
132 pub fn has_any_timer_fired(&self) -> bool {
134 self.condition_results.as_ref().is_some_and(|results| {
135 results
136 .timer_results
137 .iter()
138 .any(|timer| timer.condition_status == ConditionStatus::Completed as i32)
139 })
140 }
141
142 pub fn has_timer_fired(&self, index: usize) -> bool {
146 self.condition_results
147 .as_ref()
148 .and_then(|results| results.timer_results.get(index))
149 .is_some_and(|timer| timer.condition_status == ConditionStatus::Completed as i32)
150 }
151
152 pub fn wait_for_method_failed(&self) -> bool {
154 self.condition_results
155 .as_ref()
156 .is_some_and(|results| results.wait_for_failed)
157 }
158
159 pub(crate) fn sub_flow_result(&self, index: usize) -> HandlerResult<FlowResult> {
160 if self.method != InvocationMethod::Execute {
161 return Err(HandlerError::new(
162 "dex_sdk::HandlerError",
163 "SubFlow results are available only during execute",
164 ));
165 }
166 let result = self
167 .condition_results
168 .as_ref()
169 .and_then(|results| results.sub_flow_results.get(index))
170 .ok_or_else(|| {
171 HandlerError::new(
172 "dex_sdk::HandlerError",
173 format!("SubFlow result index is unavailable: {index}"),
174 )
175 })?;
176 FlowResult::from_proto(result).map_err(HandlerError::from_error)
177 }
178
179 pub(crate) fn sub_flow_id(&self, index: usize) -> HandlerResult<String> {
180 self.sub_flow_result(index)?;
181 Ok(format!(
182 "SubFlow:{}-{}-{index}",
183 self.flow_id(),
184 self.step_execution_id()
185 ))
186 }
187
188 pub fn wait_for_cancellation(&self) {
192 self.cancellation.wait();
193 }
194
195 pub fn is_cancelled(&self) -> bool {
197 self.cancellation.is_cancelled()
198 }
199
200 pub fn set_step_execution_local<T: Value>(&mut self, key: &str, value: T) -> HandlerResult<()> {
206 require_name(key, "step-execution local key")?;
207 let value = value_mapper::encode_handler(&value)?;
208 self.local_writes.insert(
209 key.to_string(),
210 Kv {
211 key: key.to_string(),
212 value: Some(value),
213 },
214 );
215 Ok(())
216 }
217
218 pub fn step_execution_local<T: Value>(&self, key: &str) -> HandlerResult<T> {
224 require_name(key, "step-execution local key")?;
225 let value = self
226 .local_writes
227 .get(key)
228 .and_then(|entry| entry.value.as_ref())
229 .or_else(|| self.locals.get(key))
230 .ok_or_else(|| {
231 HandlerError::new(
232 "dex_sdk::HandlerError",
233 format!("step-execution local {key} is missing"),
234 )
235 })?;
236 value_mapper::decode_handler(value)
237 }
238
239 pub fn record_event<T: Value>(&mut self, name: &str, value: T) -> HandlerResult<()> {
247 require_name(name, "event name")?;
248 if !self.event_names.insert(name.to_string()) {
249 return Err(HandlerError::new(
250 "dex_sdk::HandlerError",
251 format!("event was already recorded: {name}"),
252 ));
253 }
254 self.events.push(Kv {
255 key: name.to_string(),
256 value: Some(value_mapper::encode_handler(&value)?),
257 });
258 Ok(())
259 }
260
261 pub fn get_attribute<T: Value>(&self, attribute: &Attribute<T>) -> HandlerResult<Option<T>> {
263 self.get_attribute_value(attribute.name(), PersistenceKind::Attribute, None)
264 }
265
266 pub fn get_attribute_map<T: Value>(
268 &self,
269 attribute: &AttributeMap<T>,
270 instance: &str,
271 ) -> HandlerResult<Option<T>> {
272 self.get_attribute_value(
273 attribute.name(),
274 PersistenceKind::AttributeMap,
275 Some(instance),
276 )
277 }
278
279 pub fn set_attribute<T: Value>(
281 &mut self,
282 attribute: &Attribute<T>,
283 value: T,
284 ) -> HandlerResult<()> {
285 self.set_attribute_value(
286 attribute.name(),
287 PersistenceKind::Attribute,
288 None,
289 value_mapper::encode_handler(&value)?,
290 attribute.index().map(|index| index.proto_config(false)),
291 attribute.sync_config(),
292 )
293 }
294
295 pub fn set_attribute_map<T: Value>(
297 &mut self,
298 attribute: &AttributeMap<T>,
299 instance: &str,
300 value: T,
301 ) -> HandlerResult<()> {
302 self.set_attribute_value(
303 attribute.name(),
304 PersistenceKind::AttributeMap,
305 Some(instance),
306 value_mapper::encode_handler(&value)?,
307 attribute.index().map(|index| index.proto_config(true)),
308 attribute.sync_config(),
309 )
310 }
311
312 pub fn delete_attribute<T>(&mut self, attribute: &Attribute<T>) -> HandlerResult<()> {
314 self.set_attribute_value(
315 attribute.name(),
316 PersistenceKind::Attribute,
317 None,
318 value_mapper::deletion(),
319 attribute.index().map(|index| index.proto_config(false)),
320 attribute.sync_config(),
321 )
322 }
323
324 pub fn delete_attribute_map<T>(
326 &mut self,
327 attribute: &AttributeMap<T>,
328 instance: &str,
329 ) -> HandlerResult<()> {
330 self.set_attribute_value(
331 attribute.name(),
332 PersistenceKind::AttributeMap,
333 Some(instance),
334 value_mapper::deletion(),
335 attribute.index().map(|index| index.proto_config(true)),
336 attribute.sync_config(),
337 )
338 }
339
340 pub(crate) fn attribute_map_keys<T>(
341 &self,
342 attribute: &AttributeMap<T>,
343 ) -> HandlerResult<Vec<String>> {
344 self.registered_name(
345 attribute.name(),
346 PersistenceKind::AttributeMap,
347 Some("key-check"),
348 )?;
349 let prefix = format!("{}/", attribute.name());
350 let mut physical_keys = self
351 .attributes
352 .keys()
353 .filter(|key| key.starts_with(&prefix))
354 .cloned()
355 .collect::<HashSet<_>>();
356 for (key, write) in &self.attribute_writes {
357 if !key.starts_with(&prefix) {
358 continue;
359 }
360 if matches!(
361 write.value.as_ref().and_then(|value| value.kind.as_ref()),
362 Some(value::Kind::NullValue(_))
363 ) {
364 physical_keys.remove(key);
365 } else {
366 physical_keys.insert(key.clone());
367 }
368 }
369 sorted_instance_keys(&prefix, physical_keys)
370 }
371
372 pub fn publish<T: Value>(&mut self, channel: &Channel<T>, value: T) -> HandlerResult<()> {
374 self.publish_value(
375 channel.name(),
376 PersistenceKind::Channel,
377 None,
378 value_mapper::encode_handler(&value)?,
379 )
380 }
381
382 pub fn publish_map<T: Value>(
384 &mut self,
385 channel: &ChannelMap<T>,
386 instance: &str,
387 value: T,
388 ) -> HandlerResult<()> {
389 self.publish_value(
390 channel.name(),
391 PersistenceKind::ChannelMap,
392 Some(instance),
393 value_mapper::encode_handler(&value)?,
394 )
395 }
396
397 pub fn channel_size<T>(&self, channel: &Channel<T>) -> HandlerResult<usize> {
399 self.channel_size_value(channel.name(), PersistenceKind::Channel, None)
400 }
401
402 pub fn channel_map_size<T>(
404 &self,
405 channel: &ChannelMap<T>,
406 instance: &str,
407 ) -> HandlerResult<usize> {
408 self.channel_size_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
409 }
410
411 pub(crate) fn channel_map_keys<T>(
412 &self,
413 channel: &ChannelMap<T>,
414 ) -> HandlerResult<Vec<String>> {
415 if self.method != InvocationMethod::Rpc {
416 return Err(HandlerError::new(
417 "dex_sdk::HandlerError",
418 "ChannelMap introspection requires an RPC invocation",
419 ));
420 }
421 self.registered_name(
422 channel.name(),
423 PersistenceKind::ChannelMap,
424 Some("key-check"),
425 )?;
426 let prefix = format!("{}/", channel.name());
427 let physical_keys = self
428 .channel_infos
429 .iter()
430 .filter(|(key, info)| key.starts_with(&prefix) && info.size > 0)
431 .map(|(key, _)| key.clone())
432 .collect::<HashSet<_>>();
433 sorted_instance_keys(&prefix, physical_keys)
434 }
435
436 pub fn channel_results<T: Value>(&self, channel: &Channel<T>) -> HandlerResult<Vec<T>> {
438 self.channel_results_value(channel.name(), PersistenceKind::Channel, None)
439 }
440
441 pub fn channel_map_results<T: Value>(
443 &self,
444 channel: &ChannelMap<T>,
445 instance: &str,
446 ) -> HandlerResult<Vec<T>> {
447 self.channel_results_value(channel.name(), PersistenceKind::ChannelMap, Some(instance))
448 }
449
450 pub fn write_stream<T: Value>(&mut self, stream: &Stream<T>, value: T) -> HandlerResult<()> {
457 if self.method == InvocationMethod::Rpc {
458 return Err(HandlerError::new(
459 "dex_sdk::HandlerError",
460 "Stream writes require a Step Context",
461 ));
462 }
463 let definition = self
464 .flow
465 .persistence
466 .get(stream.name())
467 .filter(|definition| {
468 definition.kind == PersistenceKind::Stream
469 && definition.stream_identity == Some(stream.identity())
470 })
471 .ok_or_else(|| {
472 HandlerError::new(
473 "dex_sdk::HandlerError",
474 format!("Stream does not belong to Flow: {}", stream.name()),
475 )
476 })?;
477 if definition.max_estimated_bytes != Some(stream.max_estimated_bytes()) {
478 return Err(HandlerError::new(
479 "dex_sdk::HandlerError",
480 format!(
481 "Stream capacity does not match its registered definition: {}",
482 stream.name()
483 ),
484 ));
485 }
486 if self.stream_writes.contains(&stream.identity()) {
487 return Err(HandlerError::new(
488 "dex_sdk::HandlerError",
489 format!(
490 "Stream {} was already written by this Step execution",
491 stream.name()
492 ),
493 ));
494 }
495 let request = WriteStreamRequest {
496 flow_id: self.flow_id().to_string(),
497 flow_type: self.flow.name.to_string(),
498 stream_name: stream.name().to_string(),
499 max_estimated_bytes: stream.max_estimated_bytes(),
500 value: Some(value_mapper::encode_handler(&value)?),
501 idempotency_key: format!("{}#{}", self.run_id(), self.step_execution_id()),
502 };
503 let mut service = self.flow_service.clone();
504 self.runtime_handle
505 .block_on(async move { service.write_stream(request).await })
506 .map_err(|status| {
507 HandlerError::new("tonic::Status", format!("WriteStream failed: {status}"))
508 })?;
509 self.stream_writes.insert(stream.identity());
510 Ok(())
511 }
512
513 pub(crate) fn cancellation(&self) -> InvocationCancellation {
514 self.cancellation.clone()
515 }
516
517 pub(crate) fn take_outputs(
518 self,
519 ) -> (Vec<AttributeWrite>, Vec<Kv>, Vec<Kv>, Vec<ChannelMessage>) {
520 (
521 self.attribute_writes.into_values().collect(),
522 self.local_writes.into_values().collect(),
523 self.events,
524 self.publications,
525 )
526 }
527
528 fn get_attribute_value<T: Value>(
529 &self,
530 name: &str,
531 kind: PersistenceKind,
532 instance: Option<&str>,
533 ) -> HandlerResult<Option<T>> {
534 let key = self.registered_name(name, kind, instance)?;
535 if let Some(write) = self.attribute_writes.get(&key) {
536 let value = write.value.as_ref().ok_or_else(|| {
537 HandlerError::new(
538 "dex_sdk::HandlerError",
539 format!("Attribute write {key} has no Value"),
540 )
541 })?;
542 if matches!(value.kind, Some(value::Kind::NullValue(_))) {
543 return Ok(None);
544 }
545 return value_mapper::decode_handler(value).map(Some);
546 }
547 self.attributes
548 .get(&key)
549 .map(value_mapper::decode_handler)
550 .transpose()
551 }
552
553 fn set_attribute_value(
554 &mut self,
555 name: &str,
556 kind: PersistenceKind,
557 instance: Option<&str>,
558 value: ProtoValue,
559 index_config: Option<dex_protocol::dex::IndexConfig>,
560 sync_config: Option<dex_protocol::dex::AttributeSyncConfig>,
561 ) -> HandlerResult<()> {
562 let key = self.registered_name(name, kind, instance)?;
563 self.attribute_writes.insert(
564 key.clone(),
565 AttributeWrite {
566 key,
567 value: Some(value),
568 index_config,
569 sync_config,
570 },
571 );
572 Ok(())
573 }
574
575 fn publish_value(
576 &mut self,
577 name: &str,
578 kind: PersistenceKind,
579 instance: Option<&str>,
580 value: ProtoValue,
581 ) -> HandlerResult<()> {
582 let channel_name = self.registered_name(name, kind, instance)?;
583 self.publications.push(ChannelMessage {
584 channel_name: channel_name.clone(),
585 value: Some(value),
586 });
587 if self.method == InvocationMethod::Rpc {
588 self.channel_infos
589 .entry(channel_name)
590 .and_modify(|info| info.size += 1)
591 .or_insert(ChannelInfo { size: 1 });
592 }
593 Ok(())
594 }
595
596 fn channel_size_value(
597 &self,
598 name: &str,
599 kind: PersistenceKind,
600 instance: Option<&str>,
601 ) -> HandlerResult<usize> {
602 let channel_name = self.registered_name(name, kind, instance)?;
603 Ok(self
604 .channel_infos
605 .get(&channel_name)
606 .map_or(0, |info| usize::try_from(info.size).unwrap_or_default()))
607 }
608
609 fn channel_results_value<T: Value>(
610 &self,
611 name: &str,
612 kind: PersistenceKind,
613 instance: Option<&str>,
614 ) -> HandlerResult<Vec<T>> {
615 let channel_name = self.registered_name(name, kind, instance)?;
616 let Some(results) = &self.condition_results else {
617 return Ok(Vec::new());
618 };
619 let mut decoded = Vec::new();
620 for result in &results.channel_results {
621 if result.channel_name == channel_name
622 && result.condition_status == ConditionStatus::Completed as i32
623 {
624 for value in &result.values {
625 decoded.push(value_mapper::decode_handler(value)?);
626 }
627 }
628 }
629 Ok(decoded)
630 }
631
632 fn registered_name(
633 &self,
634 name: &str,
635 kind: PersistenceKind,
636 instance: Option<&str>,
637 ) -> HandlerResult<String> {
638 let definition = self.flow.persistence.get(name).ok_or_else(|| {
639 HandlerError::new(
640 "dex_sdk::HandlerError",
641 format!("persistence definition does not belong to Flow: {name}"),
642 )
643 })?;
644 if definition.kind != kind {
645 return Err(HandlerError::new(
646 "dex_sdk::HandlerError",
647 format!("persistence definition kind does not match: {name}"),
648 ));
649 }
650 match kind {
651 PersistenceKind::AttributeMap | PersistenceKind::ChannelMap => instance
652 .map(|instance| physical_name(name, instance))
653 .ok_or_else(|| {
654 HandlerError::new(
655 "dex_sdk::HandlerError",
656 format!("{name} requires an instance"),
657 )
658 }),
659 PersistenceKind::Attribute | PersistenceKind::Channel if instance.is_none() => {
660 Ok(name.to_string())
661 }
662 _ => Err(HandlerError::new(
663 "dex_sdk::HandlerError",
664 format!("static persistence definition cannot use an instance: {name}"),
665 )),
666 }
667 }
668}
669
670fn sorted_instance_keys(
671 prefix: &str,
672 physical_keys: HashSet<String>,
673) -> HandlerResult<Vec<String>> {
674 let mut keys = physical_keys
675 .iter()
676 .map(|key| {
677 decode_instance(key, prefix)
678 .map_err(|error| HandlerError::new("dex_sdk::HandlerError", error))
679 })
680 .collect::<HandlerResult<Vec<_>>>()?;
681 keys.sort();
682 Ok(keys)
683}
684
685#[derive(Clone)]
686pub(crate) struct InvocationCancellation {
687 inner: Arc<InvocationCancellationInner>,
688}
689
690struct InvocationCancellationInner {
691 cancelled: AtomicBool,
692 mutex: Mutex<()>,
693 condition: Condvar,
694}
695
696impl InvocationCancellation {
697 fn new() -> Self {
698 Self {
699 inner: Arc::new(InvocationCancellationInner {
700 cancelled: AtomicBool::new(false),
701 mutex: Mutex::new(()),
702 condition: Condvar::new(),
703 }),
704 }
705 }
706
707 fn wait(&self) {
708 let mut guard = self.inner.mutex.lock().expect("cancellation lock");
709 while !self.is_cancelled() {
710 guard = self.inner.condition.wait(guard).expect("cancellation lock");
711 }
712 }
713
714 fn is_cancelled(&self) -> bool {
715 self.inner.cancelled.load(Ordering::Acquire)
716 }
717
718 pub(crate) fn cancel(&self) {
719 let _guard = self.inner.mutex.lock().expect("cancellation lock");
720 self.inner.cancelled.store(true, Ordering::Release);
721 self.inner.condition.notify_all();
722 }
723}
724
725fn system_time(timestamp: i64) -> SystemTime {
726 if timestamp >= 0 {
727 SystemTime::UNIX_EPOCH + Duration::from_secs(timestamp as u64)
728 } else {
729 SystemTime::UNIX_EPOCH - Duration::from_secs(timestamp.unsigned_abs())
730 }
731}
732
733fn map_values(kind: &str, entries: Vec<Kv>) -> HandlerResult<HashMap<String, ProtoValue>> {
734 let mut mapped = HashMap::new();
735 for entry in entries {
736 require_name(&entry.key, kind)?;
737 let value = entry.value.ok_or_else(|| {
738 HandlerError::new(
739 "dex_sdk::HandlerError",
740 format!("{kind} {} has no Value", entry.key),
741 )
742 })?;
743 if mapped.insert(entry.key.clone(), value).is_some() {
744 return Err(HandlerError::new(
745 "dex_sdk::HandlerError",
746 format!("duplicate {kind} {}", entry.key),
747 ));
748 }
749 }
750 Ok(mapped)
751}
752
753fn require_name(value: &str, kind: &str) -> HandlerResult<()> {
754 if value.is_empty() {
755 Err(HandlerError::new(
756 "dex_sdk::HandlerError",
757 format!("{kind} is required"),
758 ))
759 } else {
760 Ok(())
761 }
762}
763
764#[cfg(test)]
765mod tests {
766 use super::{Context, ContextInput, InvocationMethod};
767 use crate::{
768 AttributeMap, ChannelMap, Flow, PersistenceSchema, Registry, registry::physical_name,
769 value_mapper,
770 };
771 use dex_protocol::dex::{
772 ChannelInfo, Context as ProtoContext, Kv, flow_service_client::FlowServiceClient,
773 };
774 use std::collections::HashMap;
775 use tokio::runtime::Runtime;
776 use tonic::transport::Endpoint;
777
778 struct MapFlow {
779 attributes: AttributeMap<String>,
780 channels: ChannelMap<String>,
781 }
782
783 impl Flow for MapFlow {
784 type StartInput = ();
785
786 fn persistence(&self) -> PersistenceSchema {
787 PersistenceSchema::new()
788 .attribute_map(&self.attributes)
789 .channel_map(&self.channels)
790 }
791 }
792
793 #[test]
794 fn map_introspection_tracks_buffered_changes() {
795 let flow = MapFlow {
796 attributes: AttributeMap::new("items"),
797 channels: ChannelMap::new("messages"),
798 };
799 let registry = Registry::new().register(flow).expect("register map Flow");
800 let registered = registry.flow("MapFlow").expect("lookup map Flow").clone();
801 let attributes = AttributeMap::<String>::new("items");
802 let channels = ChannelMap::<String>::new("messages");
803 let special = "special / key";
804 let runtime = Runtime::new().expect("create test runtime");
805 let flow_service = {
806 let _runtime_guard = runtime.enter();
807 FlowServiceClient::new(Endpoint::from_static("http://127.0.0.1:1").connect_lazy())
808 };
809 let mut context = Context::new(
810 ContextInput {
811 method: InvocationMethod::Rpc,
812 flow: registered,
813 metadata: ProtoContext::default(),
814 attributes: vec![
815 Kv {
816 key: physical_name("items", special),
817 value: Some(value_mapper::encode_handler(&"initial".to_string()).unwrap()),
818 },
819 Kv {
820 key: physical_name("items", "z"),
821 value: Some(value_mapper::encode_handler(&"remove".to_string()).unwrap()),
822 },
823 ],
824 locals: Vec::new(),
825 condition_results: None,
826 channel_infos: HashMap::from([
827 (physical_name("messages", special), ChannelInfo { size: 1 }),
828 (physical_name("messages", "empty"), ChannelInfo { size: 0 }),
829 ]),
830 },
831 runtime.handle().clone(),
832 flow_service,
833 )
834 .expect("create RPC Context");
835
836 assert_eq!(
837 vec![special.to_string(), "z".to_string()],
838 attributes.all_instance_keys(&context).unwrap()
839 );
840 attributes
841 .set(&mut context, "a", "added".to_string())
842 .unwrap();
843 attributes.delete(&mut context, "z").unwrap();
844 assert_eq!(
845 vec!["a".to_string(), special.to_string()],
846 attributes.all_instance_keys(&context).unwrap()
847 );
848 assert_eq!(2, attributes.map_size(&context).unwrap());
849
850 assert_eq!(
851 vec![special.to_string()],
852 channels.all_instance_keys(&context).unwrap()
853 );
854 channels
855 .publish(&mut context, "a", "published".to_string())
856 .unwrap();
857 assert_eq!(
858 vec!["a".to_string(), special.to_string()],
859 channels.all_instance_keys(&context).unwrap()
860 );
861 assert_eq!(2, channels.map_size(&context).unwrap());
862 }
863}