validation.rs (6360B)
1 use serde::Serialize; 2 use serde_json::{Value, json}; 3 4 use crate::ops::{ 5 OperationAdapterError, OperationRequest, OperationRequestData, OperationRequestPayload, 6 OperationResult, OperationResultData, OperationService, ValidationReceiptGetRequest, 7 ValidationReceiptGetResult, ValidationReceiptListRequest, ValidationReceiptListResult, 8 ValidationReceiptVerifyRequest, ValidationReceiptVerifyResult, 9 }; 10 use crate::runtime::config::RuntimeConfig; 11 use crate::runtime::validation_receipt::{ 12 ValidationReceiptEventArgs, ValidationReceiptInspectionView, ValidationReceiptListArgs, 13 ValidationReceiptListView, 14 }; 15 use crate::view::runtime::CommandDisposition; 16 17 pub struct ValidationOperationService<'a> { 18 config: &'a RuntimeConfig, 19 } 20 21 impl<'a> ValidationOperationService<'a> { 22 pub fn new(config: &'a RuntimeConfig) -> Self { 23 Self { config } 24 } 25 } 26 27 impl OperationService<ValidationReceiptGetRequest> for ValidationOperationService<'_> { 28 type Result = ValidationReceiptGetResult; 29 30 fn execute( 31 &self, 32 request: OperationRequest<ValidationReceiptGetRequest>, 33 ) -> Result<OperationResult<Self::Result>, OperationAdapterError> { 34 let args = validation_receipt_event_args(&request)?; 35 let view = crate::runtime::validation_receipt::get(self.config, &args); 36 validation_receipt_inspection_result::<ValidationReceiptGetResult>( 37 "validation.receipt.get", 38 &view, 39 ) 40 } 41 } 42 43 impl OperationService<ValidationReceiptListRequest> for ValidationOperationService<'_> { 44 type Result = ValidationReceiptListResult; 45 46 fn execute( 47 &self, 48 request: OperationRequest<ValidationReceiptListRequest>, 49 ) -> Result<OperationResult<Self::Result>, OperationAdapterError> { 50 let args = validation_receipt_list_args(&request)?; 51 let view = crate::runtime::validation_receipt::list(self.config, &args); 52 validation_receipt_list_result(&view) 53 } 54 } 55 56 impl OperationService<ValidationReceiptVerifyRequest> for ValidationOperationService<'_> { 57 type Result = ValidationReceiptVerifyResult; 58 59 fn execute( 60 &self, 61 request: OperationRequest<ValidationReceiptVerifyRequest>, 62 ) -> Result<OperationResult<Self::Result>, OperationAdapterError> { 63 let args = validation_receipt_event_args(&request)?; 64 let view = crate::runtime::validation_receipt::verify(self.config, &args); 65 validation_receipt_inspection_result::<ValidationReceiptVerifyResult>( 66 "validation.receipt.verify", 67 &view, 68 ) 69 } 70 } 71 72 fn validation_receipt_event_args<P>( 73 request: &OperationRequest<P>, 74 ) -> Result<ValidationReceiptEventArgs, OperationAdapterError> 75 where 76 P: OperationRequestPayload + OperationRequestData, 77 { 78 Ok(ValidationReceiptEventArgs { 79 receipt_event_id: required_string(request, "receipt_event_id")?, 80 }) 81 } 82 83 fn validation_receipt_list_args<P>( 84 request: &OperationRequest<P>, 85 ) -> Result<ValidationReceiptListArgs, OperationAdapterError> 86 where 87 P: OperationRequestPayload + OperationRequestData, 88 { 89 Ok(ValidationReceiptListArgs { 90 order_id: required_string(request, "order_id")?, 91 }) 92 } 93 94 fn validation_receipt_inspection_result<R>( 95 operation_id: &str, 96 view: &ValidationReceiptInspectionView, 97 ) -> Result<OperationResult<R>, OperationAdapterError> 98 where 99 R: OperationResultData, 100 { 101 match view.disposition() { 102 CommandDisposition::Success => serialized_operation_result::<R, _>(view), 103 disposition => Err(validation_receipt_view_error( 104 operation_id, 105 disposition, 106 view, 107 view.reason.as_deref(), 108 )), 109 } 110 } 111 112 fn validation_receipt_list_result( 113 view: &ValidationReceiptListView, 114 ) -> Result<OperationResult<ValidationReceiptListResult>, OperationAdapterError> { 115 match view.disposition() { 116 CommandDisposition::Success => { 117 serialized_operation_result::<ValidationReceiptListResult, _>(view) 118 } 119 disposition => Err(validation_receipt_view_error( 120 "validation.receipt.list", 121 disposition, 122 view, 123 view.reason.as_deref(), 124 )), 125 } 126 } 127 128 fn validation_receipt_view_error<T>( 129 operation_id: &str, 130 disposition: CommandDisposition, 131 view: &T, 132 reason: Option<&str>, 133 ) -> OperationAdapterError 134 where 135 T: Serialize, 136 { 137 let detail = serde_json::to_value(view).unwrap_or_else(|_| json!({})); 138 let message = reason 139 .map(str::to_owned) 140 .unwrap_or_else(|| format!("`{operation_id}` validation receipt operation failed")); 141 match disposition { 142 CommandDisposition::NotFound => { 143 OperationAdapterError::not_found_with_detail(operation_id, message, detail) 144 } 145 CommandDisposition::ValidationFailed => { 146 OperationAdapterError::validation_failed_with_detail(operation_id, message, detail) 147 } 148 CommandDisposition::Unconfigured => { 149 OperationAdapterError::operation_unavailable_with_detail(operation_id, message, detail) 150 } 151 CommandDisposition::ExternalUnavailable => { 152 OperationAdapterError::network_unavailable_with_detail(operation_id, message, detail) 153 } 154 CommandDisposition::Unsupported => OperationAdapterError::InvalidInput { 155 operation_id: operation_id.to_owned(), 156 message, 157 }, 158 CommandDisposition::InternalError | CommandDisposition::Success => { 159 OperationAdapterError::Runtime(message) 160 } 161 } 162 } 163 164 fn required_string<P>( 165 request: &OperationRequest<P>, 166 key: &str, 167 ) -> Result<String, OperationAdapterError> 168 where 169 P: OperationRequestPayload + OperationRequestData, 170 { 171 request 172 .payload 173 .input() 174 .get(key) 175 .and_then(Value::as_str) 176 .map(str::trim) 177 .filter(|value| !value.is_empty()) 178 .map(str::to_owned) 179 .ok_or_else(|| OperationAdapterError::InvalidInput { 180 operation_id: request.operation_id().to_owned(), 181 message: format!("missing required `{key}` input"), 182 }) 183 } 184 185 fn serialized_operation_result<R, T>(value: &T) -> Result<OperationResult<R>, OperationAdapterError> 186 where 187 R: OperationResultData, 188 T: Serialize, 189 { 190 OperationResult::new(R::from_serializable(value)?) 191 }