|
15 | 15 | // SPDX-License-Identifier: Apache-2.0 |
16 | 16 | // |
17 | 17 |
|
18 | | -use std::{sync::Arc, time::Duration}; |
| 18 | +use std::{collections::HashMap, sync::Arc, time::Duration}; |
19 | 19 |
|
20 | 20 | use crate::{ |
21 | 21 | firebolt::rpc::register_aliases, |
@@ -50,6 +50,33 @@ use ripple_sdk::{ |
50 | 50 | }; |
51 | 51 | use serde_json::{Map, Value}; |
52 | 52 |
|
| 53 | +// The hardcoded JSON string remains a static global variable. |
| 54 | +static APP_INJECTION_METHOD_LIST_JSON: &str = r#" |
| 55 | + { |
| 56 | + "Discovery.userInterest": "Content.onUserInterest" |
| 57 | + } |
| 58 | +"#; |
| 59 | + |
| 60 | +fn load_app_injection_method_list() -> HashMap<String, String> { |
| 61 | + let parse_result: Result<HashMap<String, String>, serde_json::Error> = |
| 62 | + serde_json::from_str(APP_INJECTION_METHOD_LIST_JSON); |
| 63 | + |
| 64 | + match parse_result { |
| 65 | + // If parsing is successful (Ok), return the map. |
| 66 | + Ok(map) => { |
| 67 | + info!("APP_INJECTION_METHOD_LIST JSON successfully loaded."); |
| 68 | + map |
| 69 | + } |
| 70 | + Err(e) => { |
| 71 | + error!( |
| 72 | + "ERROR: Failed to parse APP_INJECTION_METHOD_LIST JSON string: {}", |
| 73 | + e |
| 74 | + ); |
| 75 | + HashMap::new() // Default value: an empty HashMap |
| 76 | + } |
| 77 | + } |
| 78 | +} |
| 79 | + |
53 | 80 | // TODO: Add to config |
54 | 81 | const DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS: u64 = 15000; |
55 | 82 |
|
@@ -308,41 +335,42 @@ impl ProviderRegistrar { |
308 | 335 |
|
309 | 336 | let result_value = match event_data { |
310 | 337 | Value::Object(ref event_data_map) => { |
311 | | - if let Some(event_schema_map) = context |
312 | | - .platform_state |
313 | | - .open_rpc_state |
314 | | - .get_openrpc_validator() |
315 | | - .get_closest_result_properties_schema(event, event_data_map) |
316 | | - { |
317 | | - // Populate the event result, injecting the app ID if the field exists in the event schema |
318 | | - |
319 | | - let mut result_map = Map::new(); |
320 | | - |
321 | | - for key in event_schema_map.keys() { |
322 | | - if let Some(event_value) = event_data_map.get(key) { |
323 | | - result_map.insert(key.clone(), event_value.clone()); |
324 | | - } else if key.eq("appId") { |
325 | | - if let Some(context) = call_context.clone() { |
326 | | - result_map.insert(key.clone(), Value::String(context.app_id)); |
327 | | - } else { |
328 | | - error!("callback_app_event_emitter: Missing call context, could not determine app ID"); |
329 | | - result_map.insert(key.clone(), Value::Null); |
| 338 | + let method_map = load_app_injection_method_list(); |
| 339 | + let search_term = context.method.clone().to_lowercase(); |
| 340 | + |
| 341 | + let mut result_map = Map::new(); |
| 342 | + for (key, value) in event_data_map { |
| 343 | + result_map.insert(key.clone(), value.clone()); |
| 344 | + } |
| 345 | + |
| 346 | + if method_map.is_empty() { |
| 347 | + info!("The map is currently empty. Cannot search for method."); |
| 348 | + } else { |
| 349 | + for (method, _handler_method) in method_map.iter() { |
| 350 | + if method.to_lowercase().contains(&search_term) { |
| 351 | + info!( |
| 352 | + "callback_app_event_emitter: Injection method found: {}", |
| 353 | + method |
| 354 | + ); |
| 355 | + |
| 356 | + if !result_map.contains_key("appId") { |
| 357 | + if let Some(context) = call_context.clone() { |
| 358 | + result_map.insert( |
| 359 | + "appId".to_string(), |
| 360 | + Value::String(context.app_id), |
| 361 | + ); |
| 362 | + } else { |
| 363 | + error!("callback_app_event_emitter: Missing call context, could not determine app ID"); |
| 364 | + result_map.insert("appId".to_string(), Value::Null); |
| 365 | + } |
330 | 366 | } |
331 | | - } else { |
332 | | - error!( |
333 | | - "callback_app_event_emitter: Missing field in event data: field={}", |
334 | | - key); |
335 | | - |
336 | | - // Assume the field in the schema holds the contents of the event. This |
337 | | - // is the fragile part that should probably be addressed by a schema change. |
338 | | - result_map.insert(key.clone(), event_data.clone()); |
| 367 | + |
| 368 | + break; |
339 | 369 | } |
340 | 370 | } |
341 | | - |
342 | | - Value::Object(result_map) |
343 | | - } else { |
344 | | - event_data.clone() |
345 | 371 | } |
| 372 | + |
| 373 | + Value::Object(result_map) |
346 | 374 | } |
347 | 375 | _ => event_data.clone(), |
348 | 376 | }; |
@@ -529,73 +557,31 @@ impl ProviderRegistrar { |
529 | 557 | } |
530 | 558 | }; |
531 | 559 |
|
532 | | - let result_properties_map = match context |
533 | | - .platform_state |
534 | | - .open_rpc_state |
535 | | - .get_openrpc_validator() |
536 | | - .get_closest_result_properties_schema( |
537 | | - &context.method, |
538 | | - provider_response_value_map, |
539 | | - ) { |
540 | | - Some(result_properties_map) => result_properties_map, |
541 | | - None => { |
542 | | - error!("callback_provider_invoker: Result schema not found"); |
543 | | - return Err(Error::Custom(String::from("Result schema not found"))); |
544 | | - } |
545 | | - }; |
546 | | - |
547 | | - // Inject the provider app ID if the field exists in the provided-to response schema, the other field will be |
548 | | - // the provider response. The firebolt spec is not ideal in that the provider response data is captured |
549 | | - // within a field of the provided-to's response object, hence the somewhat arbritrary logic here. Ideally |
550 | | - // the provided-to response object would be identical to the provider response object aside from an optional |
551 | | - // appId field. |
| 560 | + let method_map = load_app_injection_method_list(); |
| 561 | + let search_term = context.method.clone().to_lowercase(); |
552 | 562 |
|
553 | 563 | let mut response_map = Map::new(); |
554 | | - for key in result_properties_map.keys() { |
555 | | - if let Some(field) = provider_response_value_map.get(key) { |
556 | | - response_map.insert(key.clone(), field.clone()); |
557 | | - } else if key.eq("appId") { |
558 | | - response_map.insert( |
559 | | - key.clone(), |
560 | | - Value::String(provider_app_id.clone().unwrap_or_default()), |
561 | | - ); |
562 | | - } else if let Some(Value::Object(result_property_map)) = |
563 | | - result_properties_map.get(key) |
564 | | - { |
565 | | - let reference_path = match result_property_map.get("$ref") { |
566 | | - Some(Value::String(path)) => path, |
567 | | - _ => { |
568 | | - error!("callback_provider_invoker: $ref not found: key={}", key); |
569 | | - continue; |
570 | | - } |
571 | | - }; |
572 | | - |
573 | | - if let Some(ref_properties_map) = context |
574 | | - .platform_state |
575 | | - .open_rpc_state |
576 | | - .get_openrpc_validator() |
577 | | - .get_result_ref_schema(reference_path) |
578 | | - { |
579 | | - // If any (!) of the keys match, assume the field in the schema holds the contents of the provider response. This |
580 | | - // is the fragile part that should be addressed by a spec change, as Ripple can only guess at intention. |
581 | | - |
582 | | - if provider_response_value_map |
583 | | - .keys() |
584 | | - .any(|key| ref_properties_map.contains_key(key)) |
585 | | - { |
586 | | - response_map.insert(key.clone(), provider_response_value.clone()); |
587 | | - |
588 | | - // Just bail now, we're dumping the complete provider response into the response map |
589 | | - // under some key in the schema, so it's either right or wrong but continuing to iterate |
590 | | - // would be wrong-er. |
591 | | - |
592 | | - return Ok(Value::Object(response_map)); |
| 564 | + for (key, value) in provider_response_value_map { |
| 565 | + response_map.insert(key.clone(), value.clone()); |
| 566 | + } |
| 567 | + |
| 568 | + if method_map.is_empty() { |
| 569 | + info!("The map is currently empty. Cannot search for method."); |
| 570 | + } else { |
| 571 | + for (method, _handler_method) in method_map.iter() { |
| 572 | + if method.to_lowercase().contains(&search_term) { |
| 573 | + info!( |
| 574 | + "callback_provider_invoker: Injection method found: {}", |
| 575 | + method |
| 576 | + ); |
| 577 | + |
| 578 | + if !response_map.contains_key("appId") { |
| 579 | + if let Some(app_id) = provider_app_id.clone() { |
| 580 | + response_map.insert("appId".to_string(), Value::String(app_id)); |
| 581 | + } |
593 | 582 | } |
594 | | - } else { |
595 | | - error!("callback_provider_invoker: ref_properties_map not found"); |
| 583 | + break; |
596 | 584 | } |
597 | | - } else { |
598 | | - error!("callback_provider_invoker: Not an object: key={}", key); |
599 | 585 | } |
600 | 586 | } |
601 | 587 | Ok(Value::Object(response_map)) |
@@ -1049,4 +1035,22 @@ mod tests { |
1049 | 1035 | assert!(c.message.eq("The Player with 'ipa' id does not exist")) |
1050 | 1036 | } |
1051 | 1037 | } |
| 1038 | + |
| 1039 | + #[test] |
| 1040 | + fn test_load_app_injection_method_list() { |
| 1041 | + let method_map = load_app_injection_method_list(); |
| 1042 | + assert!(!method_map.is_empty()); |
| 1043 | + } |
| 1044 | + |
| 1045 | + #[test] |
| 1046 | + fn test_load_app_injection_method_list_values() { |
| 1047 | + let method_map = load_app_injection_method_list(); |
| 1048 | + for (key, value) in method_map.iter() { |
| 1049 | + if key.eq("Discovery.userInterest") { |
| 1050 | + assert!(value.eq("Content.onUserInterest")); |
| 1051 | + } |
| 1052 | + |
| 1053 | + assert!(method_map.contains_key("Discovery.userInterest")); |
| 1054 | + } |
| 1055 | + } |
1052 | 1056 | } |
0 commit comments