1use serde::{Deserialize, Serialize};
2use serde_json::{Map, Value};
3use std::collections::HashSet;
4use thiserror::Error;
5use uuid::Uuid;
6
7use crate::{
8 Capability, CapabilitySet, DataSourceDescriptor, DataSourceKind, FrameworkGeneration,
9 FrameworkGenerationRange, InteractionDescriptor, InteractionId, InteractionKind,
10 InteractionTransport, ProviderKind, Scope, SlotValidationError, SurfaceDescriptor, SurfaceId,
11 SurfaceNode, SurfaceSlotDef, Targeting, validate_slot_id, validate_surface_identifier,
12};
13
14#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
15pub struct SurfaceRegistration {
16 pub provider: ProviderIdentity,
17 pub framework_generation: FrameworkGeneration,
18 pub capabilities: CapabilitySet,
19 pub effective_tenant_binding: EffectiveTenantBinding,
20 #[serde(default, skip_serializing_if = "Vec::is_empty")]
21 pub surfaces: Vec<RegisteredSurface>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
23 pub encryption_metadata: Option<ProviderEncryptionMetadata>,
24}
25
26impl SurfaceRegistration {
27 pub fn validate_against(
36 &self,
37 policy: &SurfaceRegistrationPolicy,
38 ) -> Result<(), SurfaceRegistrationError> {
39 validate_supported_generation(self, policy)?;
40 validate_required_registration_capabilities(self, policy)?;
41 validate_registered_surfaces(self)?;
42 Ok(())
43 }
44}
45
46fn validate_supported_generation(
47 registration: &SurfaceRegistration,
48 policy: &SurfaceRegistrationPolicy,
49) -> Result<(), SurfaceRegistrationError> {
50 if policy
51 .supported_generation
52 .includes(registration.framework_generation)
53 {
54 return Ok(());
55 }
56
57 Err(registration_error(
58 SurfaceRegistrationErrorCode::UnsupportedGeneration,
59 format!(
60 "framework generation {}.{} is outside supported range {}.{}..={}.{}",
61 registration.framework_generation.major,
62 registration.framework_generation.minor,
63 policy.supported_generation.min.major,
64 policy.supported_generation.min.minor,
65 policy.supported_generation.max.major,
66 policy.supported_generation.max.minor,
67 ),
68 ))
69}
70
71fn validate_required_registration_capabilities(
72 registration: &SurfaceRegistration,
73 policy: &SurfaceRegistrationPolicy,
74) -> Result<(), SurfaceRegistrationError> {
75 if registration
76 .capabilities
77 .contains_all(&policy.required_capabilities)
78 {
79 return Ok(());
80 }
81
82 Err(missing_capability(
83 "registration is missing one or more required capabilities",
84 ))
85}
86
87fn validate_registered_surfaces(
88 registration: &SurfaceRegistration,
89) -> Result<(), SurfaceRegistrationError> {
90 RegisteredSurfacesValidator::new(registration).validate()
91}
92
93struct RegisteredSurfacesValidator<'a> {
94 provider_kind: ProviderKind,
95 capabilities: &'a CapabilitySet,
96 surfaces: &'a [RegisteredSurface],
97 surface_ids: HashSet<&'a str>,
98 single_entry_slots: HashSet<&'static str>,
99}
100
101impl<'a> RegisteredSurfacesValidator<'a> {
102 fn new(registration: &'a SurfaceRegistration) -> Self {
103 Self {
104 provider_kind: registration.provider.provider_kind,
105 capabilities: ®istration.capabilities,
106 surfaces: ®istration.surfaces,
107 surface_ids: HashSet::new(),
108 single_entry_slots: HashSet::new(),
109 }
110 }
111
112 fn validate(mut self) -> Result<(), SurfaceRegistrationError> {
113 for surface in self.surfaces {
114 self.validate_surface(surface)?;
115 }
116
117 Ok(())
118 }
119
120 fn validate_surface(
121 &mut self,
122 surface: &'a RegisteredSurface,
123 ) -> Result<(), SurfaceRegistrationError> {
124 validate_surface_descriptor_rules(
125 surface,
126 self.provider_kind,
127 self.capabilities,
128 &mut self.surface_ids,
129 &mut self.single_entry_slots,
130 )?;
131
132 let interaction_ids = validate_surface_interaction_rules(surface)?;
133 validate_workflow_step_references(
134 &surface.descriptor.surface_id,
135 &surface.interactions,
136 &interaction_ids,
137 )?;
138
139 let data_source_ids = validate_surface_data_source_rules(surface)?;
140 validate_root_node_references(
141 &surface.descriptor.surface_id,
142 &surface.descriptor.root_node,
143 &interaction_ids,
144 &data_source_ids,
145 )
146 }
147}
148
149fn validate_surface_descriptor_rules<'a>(
150 surface: &'a RegisteredSurface,
151 registration_provider_kind: ProviderKind,
152 capabilities: &CapabilitySet,
153 surface_ids: &mut HashSet<&'a str>,
154 single_entry_slots: &mut HashSet<&'static str>,
155) -> Result<(), SurfaceRegistrationError> {
156 validate_unique_surface_id(surface, surface_ids)?;
157 validate_surface_provider_kind(surface, registration_provider_kind)?;
158 let slot_def = validate_surface_slot(surface)?;
159 validate_single_entry_slot_occupancy(slot_def, single_entry_slots)?;
160 validate_surface_priority_range(surface, slot_def)?;
161 validate_surface_required_capabilities(surface, capabilities)?;
162 validate_surface_usage_capabilities(
163 &surface.descriptor.surface_id,
164 capabilities,
165 &surface.descriptor.root_node,
166 &surface.descriptor.targeting,
167 &surface.interactions,
168 &surface.data_sources,
169 )
170}
171
172fn validate_unique_surface_id<'a>(
173 surface: &'a RegisteredSurface,
174 surface_ids: &mut HashSet<&'a str>,
175) -> Result<(), SurfaceRegistrationError> {
176 if surface_ids.insert(surface.descriptor.surface_id.as_str()) {
177 return Ok(());
178 }
179
180 Err(invalid_contract(format!(
181 "duplicate surface_id `{}` within registration batch",
182 surface.descriptor.surface_id
183 )))
184}
185
186fn validate_surface_provider_kind(
187 surface: &RegisteredSurface,
188 registration_provider_kind: ProviderKind,
189) -> Result<(), SurfaceRegistrationError> {
190 if surface.descriptor.provider_kind == registration_provider_kind {
191 return Ok(());
192 }
193
194 Err(invalid_contract(format!(
195 "surface `{}` provider_kind does not match registration provider_kind",
196 surface.descriptor.surface_id
197 )))
198}
199
200fn validate_surface_slot(
201 surface: &RegisteredSurface,
202) -> Result<&'static SurfaceSlotDef, SurfaceRegistrationError> {
203 validate_slot_id(&surface.descriptor.slot).map_err(map_slot_validation_error)
204}
205
206fn validate_single_entry_slot_occupancy(
207 slot_def: &'static SurfaceSlotDef,
208 single_entry_slots: &mut HashSet<&'static str>,
209) -> Result<(), SurfaceRegistrationError> {
210 if slot_def.multi_entry || single_entry_slots.insert(slot_def.id) {
211 return Ok(());
212 }
213
214 Err(invalid_contract(format!(
215 "slot `{}` is single-entry and cannot accept multiple surfaces in one registration batch",
216 slot_def.id
217 )))
218}
219
220fn validate_surface_priority_range(
221 surface: &RegisteredSurface,
222 slot_def: &'static SurfaceSlotDef,
223) -> Result<(), SurfaceRegistrationError> {
224 if surface.descriptor.provider_kind == ProviderKind::BuiltIn {
225 return Ok(());
226 }
227
228 if surface.descriptor.priority >= slot_def.provider_priority_min
229 && surface.descriptor.priority <= slot_def.provider_priority_max
230 {
231 return Ok(());
232 }
233
234 Err(invalid_contract(format!(
235 "surface `{}` priority {} is outside slot `{}` provider range {}..={}",
236 surface.descriptor.surface_id,
237 surface.descriptor.priority,
238 slot_def.id,
239 slot_def.provider_priority_min,
240 slot_def.provider_priority_max
241 )))
242}
243
244fn validate_surface_required_capabilities(
245 surface: &RegisteredSurface,
246 capabilities: &CapabilitySet,
247) -> Result<(), SurfaceRegistrationError> {
248 if capabilities.contains_all(&surface.descriptor.required_capabilities) {
249 return Ok(());
250 }
251
252 Err(missing_capability(format!(
253 "surface `{}` requires capabilities not advertised by registration",
254 surface.descriptor.surface_id
255 )))
256}
257
258fn validate_surface_interaction_rules(
259 surface: &RegisteredSurface,
260) -> Result<HashSet<&str>, SurfaceRegistrationError> {
261 let mut interaction_ids: HashSet<&str> = HashSet::new();
262 for interaction in &surface.interactions {
263 validate_unique_interaction_id(surface, interaction, &mut interaction_ids)?;
264 validate_interaction_provider_rules(surface, interaction)?;
265 }
266
267 Ok(interaction_ids)
268}
269
270fn validate_unique_interaction_id<'a>(
271 surface: &'a RegisteredSurface,
272 interaction: &'a InteractionDescriptor,
273 interaction_ids: &mut HashSet<&'a str>,
274) -> Result<(), SurfaceRegistrationError> {
275 if interaction_ids.insert(interaction.interaction_id.as_str()) {
276 return Ok(());
277 }
278
279 Err(invalid_contract(format!(
280 "duplicate interaction_id `{}` within surface `{}`",
281 interaction.interaction_id, surface.descriptor.surface_id
282 )))
283}
284
285fn validate_interaction_provider_rules(
286 surface: &RegisteredSurface,
287 interaction: &InteractionDescriptor,
288) -> Result<(), SurfaceRegistrationError> {
289 interaction
290 .validate_for_provider(surface.descriptor.provider_kind)
291 .map_err(|err| invalid_contract(err.to_string()))
292}
293
294fn validate_surface_data_source_rules(
295 surface: &RegisteredSurface,
296) -> Result<HashSet<&str>, SurfaceRegistrationError> {
297 let mut data_source_ids: HashSet<&str> = HashSet::new();
298 for data_source in &surface.data_sources {
299 validate_unique_data_source_id(surface, data_source, &mut data_source_ids)?;
300 validate_data_source_provider_rules(surface, data_source)?;
301 }
302
303 Ok(data_source_ids)
304}
305
306fn validate_unique_data_source_id<'a>(
307 surface: &'a RegisteredSurface,
308 data_source: &'a DataSourceDescriptor,
309 data_source_ids: &mut HashSet<&'a str>,
310) -> Result<(), SurfaceRegistrationError> {
311 if data_source_ids.insert(data_source.data_source_id.as_str()) {
312 return Ok(());
313 }
314
315 Err(invalid_contract(format!(
316 "duplicate data_source_id `{}` within surface `{}`",
317 data_source.data_source_id, surface.descriptor.surface_id
318 )))
319}
320
321fn validate_data_source_provider_rules(
322 surface: &RegisteredSurface,
323 data_source: &DataSourceDescriptor,
324) -> Result<(), SurfaceRegistrationError> {
325 data_source
326 .validate_for_provider(surface.descriptor.provider_kind)
327 .map_err(|err| invalid_contract(err.to_string()))
328}
329
330fn map_slot_validation_error(err: SlotValidationError) -> SurfaceRegistrationError {
331 let code = match err {
332 SlotValidationError::UnknownSlot(_) => SurfaceRegistrationErrorCode::InvalidSlot,
333 SlotValidationError::InvalidIdentifier(_) => SurfaceRegistrationErrorCode::InvalidContract,
334 };
335
336 registration_error(code, err.to_string())
337}
338
339fn registration_error(
340 code: SurfaceRegistrationErrorCode,
341 message: impl Into<String>,
342) -> SurfaceRegistrationError {
343 SurfaceRegistrationError::new(code, message.into())
344}
345
346fn invalid_contract(message: impl Into<String>) -> SurfaceRegistrationError {
347 registration_error(SurfaceRegistrationErrorCode::InvalidContract, message)
348}
349
350fn missing_capability(message: impl Into<String>) -> SurfaceRegistrationError {
351 registration_error(SurfaceRegistrationErrorCode::MissingCapability, message)
352}
353
354fn validate_surface_usage_capabilities(
355 surface_id: &SurfaceId,
356 capabilities: &CapabilitySet,
357 root_node: &SurfaceNode,
358 targeting: &Targeting,
359 interactions: &[InteractionDescriptor],
360 data_sources: &[DataSourceDescriptor],
361) -> Result<(), SurfaceRegistrationError> {
362 validate_node_capabilities(surface_id, capabilities, root_node)?;
363
364 let targeting_capability = match targeting {
365 Targeting::Universal => Capability::UniversalTargeting,
366 Targeting::Targeted => Capability::TargetedTargeting,
367 };
368 require_capability(
369 capabilities,
370 targeting_capability,
371 surface_id,
372 "targeting mode",
373 )?;
374
375 for interaction in interactions {
376 let kind_capability = match interaction.kind {
377 InteractionKind::MutationAction => Capability::MutationAction,
378 InteractionKind::FormSubmit => Capability::FormSubmit,
379 InteractionKind::Workflow => Capability::Workflow,
380 InteractionKind::Navigate => Capability::Navigate,
381 InteractionKind::DataLoad => Capability::DataLoad,
382 InteractionKind::ConfirmableAction => Capability::ConfirmableAction,
383 };
384 require_capability(
385 capabilities,
386 kind_capability,
387 surface_id,
388 "interaction kind",
389 )?;
390
391 if matches!(
392 &interaction.transport,
393 InteractionTransport::ProviderProxied
394 ) {
395 require_capability(
396 capabilities,
397 Capability::ProviderInitiatedActions,
398 surface_id,
399 "interaction transport",
400 )?;
401 }
402
403 if !interaction.sensitive_fields.is_empty() {
404 require_capability(
405 capabilities,
406 Capability::SensitiveFields,
407 surface_id,
408 "sensitive fields",
409 )?;
410 }
411 }
412
413 for data_source in data_sources {
414 let kind_capability = match &data_source.kind {
415 DataSourceKind::Static { .. } => Capability::StaticDataSource,
416 DataSourceKind::ControllerQuery { .. } => Capability::ControllerQueryDataSource,
417 DataSourceKind::ProviderQuery { .. } => Capability::ProviderQueryDataSource,
418 };
419 require_capability(
420 capabilities,
421 kind_capability,
422 surface_id,
423 "data source kind",
424 )?;
425 }
426
427 Ok(())
428}
429
430fn validate_node_capabilities(
431 surface_id: &SurfaceId,
432 capabilities: &CapabilitySet,
433 node: &SurfaceNode,
434) -> Result<(), SurfaceRegistrationError> {
435 let node_capability = match node {
436 SurfaceNode::Section { children, .. } => {
437 for child in children {
438 validate_node_capabilities(surface_id, capabilities, child)?;
439 }
440 Capability::SectionNode
441 }
442 SurfaceNode::TextBlock { .. } => Capability::TextBlockNode,
443 SurfaceNode::KeyValue { .. } => Capability::KeyValueNode,
444 SurfaceNode::Table { .. } => Capability::TableNode,
445 SurfaceNode::Form { .. } => Capability::FormNode,
446 SurfaceNode::ActionBar { .. } => Capability::ActionBarNode,
447 SurfaceNode::Tabs { tabs } => {
448 for tab in tabs {
449 validate_node_capabilities(surface_id, capabilities, &tab.root)?;
450 }
451 Capability::TabsNode
452 }
453 SurfaceNode::Callout { .. } => Capability::CalloutNode,
454 SurfaceNode::EmptyState { .. } => Capability::EmptyStateNode,
455 SurfaceNode::ModalTrigger { modal_nodes, .. } => {
456 for child in modal_nodes {
457 validate_node_capabilities(surface_id, capabilities, child)?;
458 }
459 Capability::ModalTriggerNode
460 }
461 SurfaceNode::WorkflowTrigger { step_nodes, .. } => {
462 for child in step_nodes {
463 validate_node_capabilities(surface_id, capabilities, child)?;
464 }
465 Capability::WorkflowTriggerNode
466 }
467 };
468
469 require_capability(capabilities, node_capability, surface_id, "root_node kind")
470}
471
472fn require_capability(
473 capabilities: &CapabilitySet,
474 required: Capability,
475 surface_id: &SurfaceId,
476 usage: &str,
477) -> Result<(), SurfaceRegistrationError> {
478 if capabilities.0.contains(&required) {
479 return Ok(());
480 }
481
482 Err(missing_capability(format!(
483 "surface `{}` uses {} that requires capability `{}`",
484 surface_id,
485 usage,
486 serde_json::to_string(&required)
487 .unwrap_or_else(|_| "\"unknown\"".to_owned())
488 .trim_matches('"')
489 )))
490}
491
492fn validate_root_node_references(
493 surface_id: &SurfaceId,
494 node: &SurfaceNode,
495 interaction_ids: &HashSet<&str>,
496 data_source_ids: &HashSet<&str>,
497) -> Result<(), SurfaceRegistrationError> {
498 RootNodeReferenceValidator::new(surface_id, interaction_ids, data_source_ids).validate(node)
499}
500
501struct RootNodeReferenceValidator<'a> {
502 surface_id: &'a SurfaceId,
503 interaction_ids: &'a HashSet<&'a str>,
504 data_source_ids: &'a HashSet<&'a str>,
505}
506
507impl<'a> RootNodeReferenceValidator<'a> {
508 fn new(
509 surface_id: &'a SurfaceId,
510 interaction_ids: &'a HashSet<&'a str>,
511 data_source_ids: &'a HashSet<&'a str>,
512 ) -> Self {
513 Self {
514 surface_id,
515 interaction_ids,
516 data_source_ids,
517 }
518 }
519
520 fn validate(&self, node: &SurfaceNode) -> Result<(), SurfaceRegistrationError> {
521 match node {
522 SurfaceNode::Section { children, .. } => self.validate_children(children),
523 SurfaceNode::TextBlock { .. } => Ok(()),
524 SurfaceNode::KeyValue { data_source_id } => {
525 self.require_data_source_reference(data_source_id.as_str())
526 }
527 SurfaceNode::Table {
528 data_source_id,
529 row_actions,
530 ..
531 } => {
532 self.require_data_source_reference(data_source_id.as_str())?;
533 self.validate_table_row_actions(row_actions)
534 }
535 SurfaceNode::Form { interaction_id } => {
536 self.require_root_interaction_reference(interaction_id.as_str())
537 }
538 SurfaceNode::ActionBar { action_ids } => self.validate_action_bar(action_ids),
539 SurfaceNode::Tabs { tabs } => self.validate_tabs(tabs),
540 SurfaceNode::Callout { .. } | SurfaceNode::EmptyState { .. } => Ok(()),
541 SurfaceNode::ModalTrigger {
542 interaction_id,
543 modal_nodes,
544 } => {
545 self.require_root_interaction_reference(interaction_id.as_str())?;
546 self.validate_children(modal_nodes)
547 }
548 SurfaceNode::WorkflowTrigger {
549 interaction_id,
550 step_nodes,
551 } => {
552 self.require_root_interaction_reference(interaction_id.as_str())?;
553 self.validate_children(step_nodes)
554 }
555 }
556 }
557
558 fn validate_children(&self, nodes: &[SurfaceNode]) -> Result<(), SurfaceRegistrationError> {
559 for child in nodes {
560 self.validate(child)?;
561 }
562 Ok(())
563 }
564
565 fn validate_table_row_actions(
566 &self,
567 row_actions: &[crate::SurfaceTableRowAction],
568 ) -> Result<(), SurfaceRegistrationError> {
569 for row_action in row_actions {
570 self.require_interaction_reference(row_action.interaction_id.as_str(), || {
571 format!(
572 "surface `{}` table references unknown row-action interaction_id `{}`",
573 self.surface_id, row_action.interaction_id
574 )
575 })?;
576 }
577 Ok(())
578 }
579
580 fn validate_action_bar(
581 &self,
582 action_ids: &[InteractionId],
583 ) -> Result<(), SurfaceRegistrationError> {
584 for action_id in action_ids {
585 self.require_root_interaction_reference(action_id.as_str())?;
586 }
587 Ok(())
588 }
589
590 fn validate_tabs(&self, tabs: &[crate::SurfaceTab]) -> Result<(), SurfaceRegistrationError> {
591 let mut tab_ids: HashSet<&str> = HashSet::new();
592
593 for tab in tabs {
594 validate_surface_identifier(tab.id.as_str()).map_err(|err| {
595 invalid_contract(format!(
596 "surface `{}` root_node contains invalid tab id `{}`: {}",
597 self.surface_id, tab.id, err
598 ))
599 })?;
600
601 if !tab_ids.insert(tab.id.as_str()) {
602 return Err(invalid_contract(format!(
603 "surface `{}` root_node contains duplicate tab id `{}` within one tabs node",
604 self.surface_id, tab.id
605 )));
606 }
607
608 self.validate(&tab.root)?;
609 }
610
611 Ok(())
612 }
613
614 fn require_root_interaction_reference(
615 &self,
616 interaction_id: &str,
617 ) -> Result<(), SurfaceRegistrationError> {
618 self.require_interaction_reference(interaction_id, || {
619 format!(
620 "surface `{}` root_node references unknown interaction_id `{}`",
621 self.surface_id, interaction_id
622 )
623 })
624 }
625
626 fn require_data_source_reference(
627 &self,
628 data_source_id: &str,
629 ) -> Result<(), SurfaceRegistrationError> {
630 ensure_known_reference(self.data_source_ids, data_source_id, || {
631 format!(
632 "surface `{}` root_node references unknown data_source_id `{}`",
633 self.surface_id, data_source_id
634 )
635 })
636 }
637
638 fn require_interaction_reference(
639 &self,
640 interaction_id: &str,
641 error_message: impl FnOnce() -> String,
642 ) -> Result<(), SurfaceRegistrationError> {
643 ensure_known_reference(self.interaction_ids, interaction_id, error_message)
644 }
645}
646
647fn validate_workflow_step_references(
648 surface_id: &SurfaceId,
649 interactions: &[InteractionDescriptor],
650 interaction_ids: &HashSet<&str>,
651) -> Result<(), SurfaceRegistrationError> {
652 WorkflowStepReferenceValidator::new(surface_id, interaction_ids).validate(interactions)
653}
654
655struct WorkflowStepReferenceValidator<'a> {
656 surface_id: &'a SurfaceId,
657 interaction_ids: &'a HashSet<&'a str>,
658}
659
660impl<'a> WorkflowStepReferenceValidator<'a> {
661 fn new(surface_id: &'a SurfaceId, interaction_ids: &'a HashSet<&'a str>) -> Self {
662 Self {
663 surface_id,
664 interaction_ids,
665 }
666 }
667
668 fn validate(
669 &self,
670 interactions: &[InteractionDescriptor],
671 ) -> Result<(), SurfaceRegistrationError> {
672 for interaction in interactions {
673 if interaction.kind != InteractionKind::Workflow {
674 continue;
675 }
676 self.validate_workflow_interaction_steps(interaction)?;
677 }
678
679 Ok(())
680 }
681
682 fn validate_workflow_interaction_steps(
683 &self,
684 interaction: &InteractionDescriptor,
685 ) -> Result<(), SurfaceRegistrationError> {
686 for step in &interaction.workflow_steps {
687 if let Some(submit_interaction_id) = &step.submit_interaction_id {
688 ensure_known_reference(
689 self.interaction_ids,
690 submit_interaction_id.as_str(),
691 || {
692 format!(
693 "surface `{}` workflow interaction `{}` references unknown submit_interaction_id `{}` in step `{}`",
694 self.surface_id,
695 interaction.interaction_id,
696 submit_interaction_id,
697 step.step_id
698 )
699 },
700 )?;
701 }
702
703 if let Some(form_ui) = &step.form_ui
704 && let Some(pre_load_interaction_id) = &form_ui.pre_load_interaction_id
705 {
706 ensure_known_reference(
707 self.interaction_ids,
708 pre_load_interaction_id.as_str(),
709 || {
710 format!(
711 "surface `{}` workflow interaction `{}` references unknown pre_load_interaction_id `{}` in step `{}`",
712 self.surface_id,
713 interaction.interaction_id,
714 pre_load_interaction_id,
715 step.step_id
716 )
717 },
718 )?;
719 }
720 }
721
722 Ok(())
723 }
724}
725
726fn ensure_known_reference(
727 known_ids: &HashSet<&str>,
728 reference_id: &str,
729 error_message: impl FnOnce() -> String,
730) -> Result<(), SurfaceRegistrationError> {
731 if known_ids.contains(reference_id) {
732 return Ok(());
733 }
734
735 Err(invalid_contract(error_message()))
736}
737
738#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
739pub struct ProviderIdentity {
740 pub provider_id: String,
741 pub provider_kind: ProviderKind,
742 pub provider_namespace: String,
743}
744
745#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
746pub struct EffectiveTenantBinding {
747 pub scope: Scope,
748 #[serde(default, skip_serializing_if = "Option::is_none")]
749 pub tenant_id: Option<String>,
750}
751
752#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
753pub struct ProviderEncryptionMetadata {
754 pub key_id: String,
755 pub algorithm: ProviderEncryptionAlgorithm,
756 pub public_key: String,
757}
758
759#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
760#[serde(rename_all = "snake_case")]
761pub enum ProviderEncryptionAlgorithm {
762 EciesP256,
763}
764
765#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
766pub struct RegisteredSurface {
767 pub descriptor: SurfaceDescriptor,
768 #[serde(default, skip_serializing_if = "Vec::is_empty")]
769 pub interactions: Vec<InteractionDescriptor>,
770 #[serde(default, skip_serializing_if = "Vec::is_empty")]
771 pub data_sources: Vec<DataSourceDescriptor>,
772}
773
774#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
775pub struct SurfaceRegistrationPolicy {
776 pub supported_generation: FrameworkGenerationRange,
777 pub required_capabilities: CapabilitySet,
778}
779
780#[derive(Debug, Clone, PartialEq, Eq, Error)]
781#[error("{code:?}: {message}")]
782pub struct SurfaceRegistrationError {
783 pub code: SurfaceRegistrationErrorCode,
784 pub message: String,
785}
786
787impl SurfaceRegistrationError {
788 pub fn new(code: SurfaceRegistrationErrorCode, message: String) -> Self {
789 Self { code, message }
790 }
791}
792
793#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
794#[serde(rename_all = "snake_case")]
795pub enum SurfaceRegistrationErrorCode {
796 UnsupportedGeneration,
797 MissingCapability,
798 InvalidSlot,
799 InvalidContract,
800}
801
802#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
803pub struct SurfaceActionRequest {
804 pub request_id: Uuid,
805 pub tenant_id: String,
806 pub surface_id: SurfaceId,
807 pub interaction_id: InteractionId,
808 pub idempotency_key: String,
809 #[serde(default, skip_serializing_if = "Option::is_none")]
810 pub target_provider_id: Option<String>,
811 pub caller_origin: CallerOrigin,
812 #[serde(default, skip_serializing_if = "Map::is_empty")]
813 pub params: Map<String, Value>,
814 #[serde(default, skip_serializing_if = "Option::is_none")]
815 pub encrypted_sensitive_params: Option<EncryptedSensitiveParams>,
816}
817
818#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
819#[serde(rename_all = "snake_case", tag = "kind")]
820pub enum CallerOrigin {
821 UserSession { user_id: String, session_id: String },
822 BuiltInSystem { principal: String },
823 Provider { provider_id: String },
824}
825
826#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
827pub struct EncryptedSensitiveParams {
828 pub key_id: String,
829 pub algorithm: ProviderEncryptionAlgorithm,
830 pub ciphertext_b64: String,
831}
832
833#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
834pub struct SurfaceActionCancel {
835 pub request_id: Uuid,
836 pub target_provider_id: String,
837 pub reason: SurfaceActionCancelReason,
838}
839
840#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
841#[serde(rename_all = "snake_case")]
842pub enum SurfaceActionCancelReason {
843 Timeout,
844 RequestCancelled,
845 ProviderDisconnected,
846}
847
848#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
849pub struct SurfaceActionResponse {
850 pub request_id: Uuid,
851 pub success: bool,
852 #[serde(default, skip_serializing_if = "Option::is_none")]
853 pub result: Option<Value>,
854 #[serde(default, skip_serializing_if = "Option::is_none")]
855 pub error: Option<SurfaceActionError>,
856}
857
858#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
859pub struct SurfaceActionError {
860 pub code: SurfaceActionErrorCode,
861 pub message: String,
862 #[serde(default, skip_serializing_if = "Option::is_none")]
863 pub details: Option<Value>,
864}
865
866#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
867#[serde(rename_all = "snake_case")]
868pub enum SurfaceActionErrorCode {
869 PermissionDenied,
870 InvalidRequest,
871 SchemaValidationFailed,
872 UnsupportedCapability,
873 ProviderUnavailable,
874 Timeout,
875 DuplicateRequest,
876 InternalError,
877}