From 7171a60cecd3754c0119485bc65aa97ec171b014 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 10 Jul 2024 22:21:46 -0400 Subject: [PATCH 01/24] x-provided-by Pull pattern: WIP --- .../src/bootstrap/start_fbgateway_step.rs | 2 + core/main/src/firebolt/firebolt_gateway.rs | 4 + core/main/src/firebolt/firebolt_ws.rs | 5 + .../firebolt/handlers/provider_registrar.rs | 4 +- core/main/src/firebolt/rpc_router.rs | 28 +- core/main/src/state/cap/generic_cap_state.rs | 1 + core/main/src/state/firebolt-open-rpc.json | 12818 +++++++++------- core/main/src/state/openrpc_state.rs | 122 +- core/sdk/src/api/firebolt/fb_openrpc.rs | 4 + 9 files changed, 7405 insertions(+), 5583 deletions(-) diff --git a/core/main/src/bootstrap/start_fbgateway_step.rs b/core/main/src/bootstrap/start_fbgateway_step.rs index 739b9ece0..52c55972f 100644 --- a/core/main/src/bootstrap/start_fbgateway_step.rs +++ b/core/main/src/bootstrap/start_fbgateway_step.rs @@ -49,6 +49,8 @@ impl FireboltGatewayStep { async fn init_handlers(&self, state: PlatformState, extn_methods: Methods) -> Methods { let mut methods = Methods::new(); + // This loads up all of the firebolt methods + // TODO: Ultimately this should be able to register all provider below, for now just does // AcknowledgeChallenge and PinChallenge. ProviderRegistrar::register(&state, &mut methods); diff --git a/core/main/src/firebolt/firebolt_gateway.rs b/core/main/src/firebolt/firebolt_gateway.rs index 0e423e440..39cba57d4 100644 --- a/core/main/src/firebolt/firebolt_gateway.rs +++ b/core/main/src/firebolt/firebolt_gateway.rs @@ -214,6 +214,10 @@ impl FireboltGateway { .handle_brokerage(request_c.clone(), extn_msg.clone()) { // Route + println!( + "*** _DEBUG: route: request.clone().ctx.protocol={:?}", + request.clone().ctx.protocol + ); match request.clone().ctx.protocol { ApiProtocol::Extn => { if let Some(extn_msg) = extn_msg { diff --git a/core/main/src/firebolt/firebolt_ws.rs b/core/main/src/firebolt/firebolt_ws.rs index 5fe2fec69..2bdee57b9 100644 --- a/core/main/src/firebolt/firebolt_ws.rs +++ b/core/main/src/firebolt/firebolt_ws.rs @@ -265,6 +265,10 @@ impl FireboltWs { "firebolt_ws Received Firebolt request {} {} {}", connection_id, request.ctx.request_id, request.method ); + debug!( + "*** _DEBUG: firebolt_ws Received Firebolt request {} {} {}", + connection_id, request.ctx.request_id, request.method + ); let msg = FireboltGatewayCommand::HandleRpc { request }; if let Err(e) = client.clone().send_gateway_command(msg) { error!("failed to send request {:?}", e); @@ -289,6 +293,7 @@ impl FireboltWs { } } } + error!("*** _DEBUG: invalid message {}", req_text); error!("invalid message {}", req_text) } } diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 0373abc21..56a1e1690 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -93,8 +93,8 @@ impl ProviderRegistrar { pub fn register(platform_state: &PlatformState, methods: &mut Methods) { let provider_map = platform_state.open_rpc_state.get_provider_map(); - for provides in provider_map.clone().keys() { - if let Some(provider_set) = provider_map.get(provides) { + for method_name in provider_map.clone().keys() { + if let Some(provider_set) = provider_map.get(method_name) { if let Some(attributes) = provider_set.attributes { let rpc_module_context = RpcModuleContext::new(platform_state.clone()); let mut rpc_module = RpcModule::new(rpc_module_context.clone()); diff --git a/core/main/src/firebolt/rpc_router.rs b/core/main/src/firebolt/rpc_router.rs index 23aad5f50..d7b5c5b1b 100644 --- a/core/main/src/firebolt/rpc_router.rs +++ b/core/main/src/firebolt/rpc_router.rs @@ -42,7 +42,7 @@ use std::sync::{Arc, RwLock}; use crate::{ service::telemetry_builder::TelemetryBuilder, - state::{platform_state::PlatformState, session_state::Session}, + state::{openrpc_state::ProviderSet, platform_state::PlatformState, session_state::Session}, utils::router_utils::{return_api_message_for_transport, return_extn_response}, }; @@ -82,8 +82,17 @@ async fn resolve_route( methods: Methods, resources: Resources, req: RpcRequest, + // + provider_set: Option, + // ) -> Result { info!("Routing {}", req.method); + // resolve_route + println!( + "*** _DEBUG: req.method={}, provider_set={:?}", + req.method, provider_set + ); + // YAH: Check if it's a provider and pass request to ProviderBroker? let id = Id::Number(req.ctx.call_id); let (sink_tx, mut sink_rx) = futures_channel::mpsc::unbounded::(); let sink = MethodSink::new_with_limit(sink_tx, TEN_MB_SIZE_BYTES); @@ -139,12 +148,23 @@ impl RpcRouter { ) { let methods = state.router_state.get_methods(); let resources = state.router_state.resources.clone(); + // + println!("*** _DEBUG: route: req.method={}", req.method); + let provider_map = state.open_rpc_state.get_provider_map(); + let provider_set = match provider_map.get(&req.method) { + Some(ps) => Some(ps.clone()), + None => None, + }; + // tokio::spawn(async move { let method = req.method.clone(); let app_id = req.ctx.app_id.clone(); let start = Utc::now().timestamp_millis(); - let resp = resolve_route(methods, resources, req.clone()).await; + // + //let resp = resolve_route(methods, resources, req.clone()).await; + let resp = resolve_route(methods, resources, req.clone(), provider_set).await; + // let status = match resp.clone() { Ok(msg) => { @@ -195,7 +215,9 @@ impl RpcRouter { let methods = state.router_state.get_methods(); let resources = state.router_state.resources.clone(); tokio::spawn(async move { - if let Ok(msg) = resolve_route(methods, resources, req).await { + // + //if let Ok(msg) = resolve_route(methods, resources, req).await { + if let Ok(msg) = resolve_route(methods, resources, req, None).await { return_extn_response(msg, extn_msg); } }); diff --git a/core/main/src/state/cap/generic_cap_state.rs b/core/main/src/state/cap/generic_cap_state.rs index 7653b69db..48af66cf5 100644 --- a/core/main/src/state/cap/generic_cap_state.rs +++ b/core/main/src/state/cap/generic_cap_state.rs @@ -49,6 +49,7 @@ impl GenericCapState { FireboltCap::Short("token:platform".to_owned()), FireboltCap::Short("usergrant:acknowledgechallenge".to_owned()), FireboltCap::Short("usergrant:pinchallenge".to_owned()), + // Do we need to add "discovery:interest" etc. here or just in the manifests? ]; cap_state.ingest_availability(caps, false); cap_state diff --git a/core/main/src/state/firebolt-open-rpc.json b/core/main/src/state/firebolt-open-rpc.json index 2490d74c8..7743a2c51 100644 --- a/core/main/src/state/firebolt-open-rpc.json +++ b/core/main/src/state/firebolt-open-rpc.json @@ -456,22 +456,22 @@ "negotiable": false } }, - "xrn:firebolt:capability:accessibility:audiodescriptions": { - "level": "must", + "xrn:firebolt:capability:account:uid": { + "level": "should", "use": { "public": true, "negotiable": true }, "manage": { - "public": true, - "negotiable": true + "public": false, + "negotiable": false }, "provide": { "public": false, "negotiable": false } }, - "xrn:firebolt:capability:accessibility:closedcaptions": { + "xrn:firebolt:capability:accessibility:audiodescriptions": { "level": "must", "use": { "public": true, @@ -486,7 +486,7 @@ "negotiable": false } }, - "xrn:firebolt:capability:accessibility:voiceguidance": { + "xrn:firebolt:capability:accessibility:closedcaptions": { "level": "must", "use": { "public": true, @@ -501,15 +501,15 @@ "negotiable": false } }, - "xrn:firebolt:capability:account:uid": { + "xrn:firebolt:capability:accessibility:voiceguidance": { "level": "must", "use": { "public": true, "negotiable": true }, "manage": { - "public": false, - "negotiable": false + "public": true, + "negotiable": true }, "provide": { "public": false, @@ -696,6 +696,21 @@ "negotiable": true } }, + "xrn:firebolt:capability:discovery:interest": { + "level": "must", + "use": { + "public": true, + "negotiable": true + }, + "manage": { + "public": false, + "negotiable": false + }, + "provide": { + "public": true, + "negotiable": true + } + }, "xrn:firebolt:capability:discovery:navigate-to": { "level": "must", "use": { @@ -741,6 +756,21 @@ "negotiable": true } }, + "xrn:firebolt:capability:inputs:hdmi": { + "level": "must", + "use": { + "public": true, + "negotiable": true + }, + "manage": { + "public": true, + "negotiable": true + }, + "provide": { + "public": false, + "negotiable": false + } + }, "xrn:firebolt:capability:lifecycle:launch": { "level": "must", "use": { @@ -897,7 +927,7 @@ "openrpc": "1.2.4", "info": { "title": "Firebolt JSON-RPC API", - "version": "1.0.0", + "version": "1.2.0", "x-module-descriptions": { "Internal": "Internal methods for SDK / FEE integration", "Accessibility": "The `Accessibility` module provides access to the user/device settings for closed captioning and voice guidance.\n\nApps **SHOULD** attempt o respect these settings, rather than manage and persist seprate settings, which would be different per-app.", @@ -910,6 +940,7 @@ "ClosedCaptions": "A module for managing closed-captions Settings.", "Device": "A module for querying about the device and it's capabilities.", "Discovery": "Your App likely wants to integrate with the Platform's discovery capabilities. For example to add a \"Watch Next\" tile that links to your app from the platform's home screen.\n\nGetting access to this information requires to connect to lower level APIs made available by the platform. Since implementations differ between operators and platforms, the Firebolt SDK offers a Discovery module, that exposes a generic, agnostic interface to the developer.\n\nUnder the hood, an underlaying transport layer will then take care of calling the right APIs for the actual platform implementation that your App is running on.\n\nThe Discovery plugin is used to _send_ information to the Platform.\n\n### Localization\nApps should provide all user-facing strings in the device's language, as specified by the Firebolt `Localization.language` property.\n\nApps should provide prices in the same currency presented in the app. If multiple currencies are supported in the app, the app should provide prices in the user's current default currency.", + "HDMIInput": "Methods for managing HDMI inputs on an HDMI sink device.", "Keyboard": "Methods for prompting users to enter text with task-oriented UX", "Lifecycle": "Methods and events for responding to lifecycle changes in your app", "Localization": "Methods for accessessing location and language preferences", @@ -1257,7 +1288,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "closedCaptionsSettings" + "x-subscriber-for": "Accessibility.closedCaptionsSettings" }, { "name": "event", @@ -1335,7 +1366,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "voiceGuidanceSettings" + "x-subscriber-for": "Accessibility.voiceGuidanceSettings" }, { "name": "event", @@ -1396,7 +1427,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "audioDescriptionSettings" + "x-subscriber-for": "Accessibility.audioDescriptionSettings" }, { "name": "event", @@ -1688,23 +1719,18 @@ "summary": "Internal API for Challenge Provider to send back response.", "params": [ { - "name": "response", - "required": true, + "name": "correlationId", "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/GrantResult" - } - } - } - ] - } + "type": "string" + }, + "required": true + }, + { + "name": "result", + "schema": { + "$ref": "#/components/schemas/GrantResult" + }, + "required": true } ], "tags": [ @@ -1729,12 +1755,13 @@ "name": "Example #1", "params": [ { - "name": "response", + "name": "correlationId", + "value": "123" + }, + { + "name": "result", "value": { - "correlationId": "123", - "result": { - "granted": true - } + "granted": true } } ], @@ -1747,12 +1774,13 @@ "name": "Example #2", "params": [ { - "name": "response", + "name": "correlationId", + "value": "123" + }, + { + "name": "result", "value": { - "correlationId": "123", - "result": { - "granted": false - } + "granted": false } } ], @@ -1765,12 +1793,13 @@ "name": "Example #3", "params": [ { - "name": "response", + "name": "correlationId", + "value": "123" + }, + { + "name": "result", "value": { - "correlationId": "123", - "result": { - "granted": null - } + "granted": null } } ], @@ -1785,45 +1814,40 @@ "name": "AcknowledgeChallenge.challengeError", "summary": "Internal API for Challenge Provider to send back error.", "params": [ + { + "name": "correlationId", + "schema": { + "type": "string" + }, + "required": true + }, { "name": "error", - "required": true, "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - } + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." } - ] - } + } + }, + "required": true } ], "tags": [ @@ -1847,14 +1871,15 @@ { "name": "Example 1", "params": [ + { + "name": "correlationId", + "value": "123" + }, { "name": "error", "value": { - "correlationId": "123", - "result": { - "code": 1, - "message": "Error" - } + "code": 1, + "message": "Error" } } ], @@ -2202,7 +2227,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "skipRestriction" + "x-subscriber-for": "Advertising.skipRestriction" }, { "name": "event", @@ -2281,7 +2306,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "policy" + "x-subscriber-for": "Advertising.policy" }, { "name": "event", @@ -2432,7 +2457,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "enabled" + "x-subscriber-for": "AudioDescriptions.enabled" }, { "name": "event", @@ -4394,7 +4419,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "enabled" + "x-subscriber-for": "ClosedCaptions.enabled" }, { "name": "event", @@ -4464,7 +4489,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "fontFamily" + "x-subscriber-for": "ClosedCaptions.fontFamily" }, { "name": "event", @@ -4547,7 +4572,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "fontSize" + "x-subscriber-for": "ClosedCaptions.fontSize" }, { "name": "event", @@ -4630,7 +4655,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "fontColor" + "x-subscriber-for": "ClosedCaptions.fontColor" }, { "name": "event", @@ -4713,7 +4738,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "fontEdge" + "x-subscriber-for": "ClosedCaptions.fontEdge" }, { "name": "event", @@ -4796,7 +4821,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "fontEdgeColor" + "x-subscriber-for": "ClosedCaptions.fontEdgeColor" }, { "name": "event", @@ -4879,7 +4904,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "fontOpacity" + "x-subscriber-for": "ClosedCaptions.fontOpacity" }, { "name": "event", @@ -4962,7 +4987,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "backgroundColor" + "x-subscriber-for": "ClosedCaptions.backgroundColor" }, { "name": "event", @@ -5045,7 +5070,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "backgroundOpacity" + "x-subscriber-for": "ClosedCaptions.backgroundOpacity" }, { "name": "event", @@ -5128,7 +5153,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "textAlign" + "x-subscriber-for": "ClosedCaptions.textAlign" }, { "name": "event", @@ -5211,7 +5236,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "textAlignVertical" + "x-subscriber-for": "ClosedCaptions.textAlignVertical" }, { "name": "event", @@ -5294,7 +5319,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "windowColor" + "x-subscriber-for": "ClosedCaptions.windowColor" }, { "name": "event", @@ -5377,7 +5402,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "windowOpacity" + "x-subscriber-for": "ClosedCaptions.windowOpacity" }, { "name": "event", @@ -5470,7 +5495,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "preferredLanguages" + "x-subscriber-for": "ClosedCaptions.preferredLanguages" }, { "name": "event", @@ -6528,74 +6553,235 @@ ] }, { - "name": "Device.id", - "summary": "Get the platform back-office device identifier", - "params": [], + "name": "Content.requestUserInterest", "tags": [ - { - "name": "property:immutable" - }, { "name": "capabilities", + "x-provided-by": "Discovery.onRequestUserInterest", "x-uses": [ - "xrn:firebolt:capability:device:id" + "xrn:firebolt:capability:discovery:interest" ] } ], + "summary": "Provide information about the entity currently displayed or selected on the screen.", + "description": "Provide information about the entity currently displayed or selected on the screen.", + "params": [ + { + "name": "type", + "required": true, + "schema": { + "$ref": "#/x-schemas/Discovery/InterestType" + } + }, + { + "name": "reason", + "required": true, + "schema": { + "$ref": "#/x-schemas/Discovery/InterestReason" + } + } + ], "result": { - "name": "id", - "summary": "the id", + "name": "interest", "schema": { - "type": "string" - } + "$ref": "#/components/schemas/InterestResult" + }, + "summary": "The EntityDetails data." }, "examples": [ { "name": "Default Example", - "params": [], + "params": [ + { + "name": "type", + "value": "interest" + }, + { + "name": "reason", + "value": "playlist" + } + ], "result": { - "name": "Default Result", - "value": "123" + "name": "interest", + "value": { + "appId": "cool-app", + "entity": { + "identifiers": { + "entityId": "345", + "entityType": "program", + "programType": "movie" + }, + "info": { + "title": "Cool Runnings", + "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pulvinar sapien et ligula ullamcorper malesuada proin libero nunc.", + "releaseDate": "1993-01-01T00:00:00.000Z", + "contentRatings": [ + { + "scheme": "US-Movie", + "rating": "PG" + }, + { + "scheme": "CA-Movie", + "rating": "G" + } + ] + } + } + } } } ] }, { - "name": "Device.distributor", - "summary": "Get the distributor ID for this device", - "params": [], + "name": "Content.onUserInterest", "tags": [ { - "name": "property:immutable" + "name": "event" }, { "name": "capabilities", + "x-provided-by": "Discovery.userInterest", "x-uses": [ - "xrn:firebolt:capability:device:distributor" + "xrn:firebolt:capability:discovery:interest" ] } ], + "summary": "Provide information about the entity currently displayed or selected on the screen.", + "description": "Provide information about the entity currently displayed or selected on the screen.", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "result": { - "name": "distributorId", - "summary": "the distributor ID", + "name": "interest", "schema": { - "type": "string" - } + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/components/schemas/InterestEvent" + } + ] + }, + "summary": "The EntityDetails data." }, "examples": [ { - "name": "Getting the distributor ID", - "params": [], + "name": "Default Example", + "params": [ + { + "name": "listen", + "value": true + } + ], "result": { - "name": "Default Result", - "value": "Company" + "name": "interest", + "value": { + "appId": "cool-app", + "type": "interest", + "reason": "playlist", + "entity": { + "identifiers": { + "entityId": "345", + "entityType": "program", + "programType": "movie" + }, + "info": { + "title": "Cool Runnings", + "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pulvinar sapien et ligula ullamcorper malesuada proin libero nunc.", + "releaseDate": "1993-01-01T00:00:00.000Z", + "contentRatings": [ + { + "scheme": "US-Movie", + "rating": "PG" + }, + { + "scheme": "CA-Movie", + "rating": "G" + } + ] + } + } + } } } ] }, { - "name": "Device.platform", - "summary": "Get the platform ID for this device", + "name": "Device.id", + "summary": "Get the platform back-office device identifier", + "params": [], + "tags": [ + { + "name": "property:immutable" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:device:id" + ] + } + ], + "result": { + "name": "id", + "summary": "the id", + "schema": { + "type": "string" + } + }, + "examples": [ + { + "name": "Default Example", + "params": [], + "result": { + "name": "Default Result", + "value": "123" + } + } + ] + }, + { + "name": "Device.distributor", + "summary": "Get the distributor ID for this device", + "params": [], + "tags": [ + { + "name": "property:immutable" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:device:distributor" + ] + } + ], + "result": { + "name": "distributorId", + "summary": "the distributor ID", + "schema": { + "type": "string" + } + }, + "examples": [ + { + "name": "Getting the distributor ID", + "params": [], + "result": { + "name": "Default Result", + "value": "Company" + } + } + ] + }, + { + "name": "Device.platform", + "summary": "Get the platform ID for this device", "params": [], "tags": [ { @@ -6837,7 +7023,6 @@ } }, "required": [ - "sdk", "api", "firmware", "os" @@ -7312,7 +7497,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "name" + "x-subscriber-for": "Device.name" }, { "name": "event", @@ -7383,7 +7568,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "hdcp" + "x-subscriber-for": "Device.hdcp" }, { "name": "event", @@ -7444,7 +7629,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "hdr" + "x-subscriber-for": "Device.hdr" }, { "name": "event", @@ -7507,7 +7692,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "audio" + "x-subscriber-for": "Device.audio" }, { "name": "event", @@ -7570,7 +7755,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "screenResolution" + "x-subscriber-for": "Device.screenResolution" }, { "name": "event", @@ -7631,7 +7816,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "videoResolution" + "x-subscriber-for": "Device.videoResolution" }, { "name": "event", @@ -7692,7 +7877,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "network" + "x-subscriber-for": "Device.network" }, { "name": "event", @@ -7856,6 +8041,9 @@ { "name": "capabilities", "x-provides": "xrn:firebolt:capability:discovery:entity-info" + }, + { + "name": "deprecated" } ], "summary": "Provide information about a program entity and its available watchable assets, such as entitlement status and price, via either a push or pull call flow.", @@ -8213,7 +8401,7 @@ } ], "result": { - "name": "success", + "name": "result", "value": true } } @@ -8228,6 +8416,9 @@ { "name": "capabilities", "x-provides": "xrn:firebolt:capability:discovery:purchased-content" + }, + { + "name": "deprecated" } ], "summary": "Provide a list of purchased content for the authenticated account, such as rentals and electronic sell through purchases.", @@ -8440,7 +8631,7 @@ "name": "identifiers", "summary": "A set of content identifiers for this call to action", "schema": { - "$ref": "#/x-schemas/Entertainment/ContentIdentifiers" + "$ref": "#/x-schemas/Entity/Entity" }, "required": true }, @@ -9332,6 +9523,200 @@ } ] }, + { + "name": "Discovery.userInterest", + "summary": "Send an entity that the user has expressed interest in to the platform.", + "tags": [ + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:discovery:interest" + } + ], + "params": [ + { + "name": "type", + "required": true, + "schema": { + "$ref": "#/x-schemas/Discovery/InterestType" + } + }, + { + "name": "reason", + "required": true, + "schema": { + "$ref": "#/x-schemas/Discovery/InterestReason" + } + }, + { + "name": "entity", + "required": true, + "schema": { + "$ref": "#/x-schemas/Entity/EntityDetails" + } + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + } + }, + "examples": [ + { + "name": "Default Example", + "params": [ + { + "name": "type", + "value": "interest" + }, + { + "name": "reason", + "value": "playlist" + }, + { + "name": "entity", + "value": { + "identifiers": { + "entityId": "345", + "entityType": "program", + "programType": "movie" + }, + "info": {} + } + } + ], + "result": { + "name": "result", + "value": null + } + } + ] + }, + { + "name": "Discovery.onRequestUserInterest", + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "event", + "x-response-name": "entity", + "x-response": { + "$ref": "#/x-schemas/Entity/EntityDetails", + "examples": [ + { + "identifiers": { + "entityId": "345", + "entityType": "program", + "programType": "movie" + }, + "info": { + "title": "Cool Runnings", + "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pulvinar sapien et ligula ullamcorper malesuada proin libero nunc.", + "releaseDate": "1993-01-01T00:00:00.000Z", + "contentRatings": [ + { + "scheme": "US-Movie", + "rating": "PG" + }, + { + "scheme": "CA-Movie", + "rating": "G" + } + ] + } + } + ] + }, + "x-error": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + } + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:discovery:interest" + } + ], + "summary": "Provide information about the entity currently displayed or selected on the screen.", + "description": "Provide information about the entity currently displayed or selected on the screen.", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], + "result": { + "name": "request", + "schema": { + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "object", + "required": [ + "correlationId", + "parameters" + ], + "properties": { + "correlationId": { + "type": "string" + }, + "parameters": { + "$ref": "#/components/schemas/UserInterestProviderParameters" + } + }, + "additionalProperties": false + } + ] + } + }, + "examples": [ + { + "name": "Default Example", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "request", + "value": { + "correlationId": "xyz", + "parameters": { + "type": "interest", + "reason": "playlist" + } + } + } + } + ] + }, { "name": "Discovery.onPolicyChanged", "summary": "get the discovery policy", @@ -9347,7 +9732,7 @@ "tags": [ { "name": "subscriber", - "x-subscriber-for": "policy" + "x-subscriber-for": "Discovery.policy" }, { "name": "event", @@ -9407,6 +9792,9 @@ { "name": "capabilities", "x-provides": "xrn:firebolt:capability:discovery:entity-info" + }, + { + "name": "deprecated" } ], "summary": "Provide information about a program entity and its available watchable assets, such as entitlement status and price, via either a push or pull call flow.", @@ -9504,6 +9892,9 @@ { "name": "capabilities", "x-provides": "xrn:firebolt:capability:discovery:purchased-content" + }, + { + "name": "deprecated" } ], "summary": "Provide a list of purchased content for the authenticated account, such as rentals and electronic sell through purchases.", @@ -9553,325 +9944,383 @@ "description": "Return content purchased by the user, such as rentals and electronic sell through purchases.\n\nThe app should return the user's 100 most recent purchases in `entries`. The total count of purchases must be provided in `count`. If `count` is greater than the total number of `entries`, the UI may provide a link into the app to see the complete purchase list.\n\nThe `EntityInfo` object returned is not required to have `waysToWatch` populated, but it is recommended that it do so in case the UI wants to surface additional information on the purchases screen.\n\nThe app should implement both Push and Pull methods for `purchasedContent`.\n\nThe app should actively push `purchasedContent` when:\n\n* The app becomes Active.\n* When the state of the purchasedContent set has changed.\n* The app goes into Inactive or Background state, if there is a chance a change event has been missed." }, { - "name": "Keyboard.email", + "name": "Discovery.userInterestResponse", "tags": [ + { + "name": "rpc-only" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:input:keyboard" - ] + "x-provides": "xrn:firebolt:capability:discovery:interest", + "x-response-for": "Discovery.onRequestUserInterest" } ], - "summary": "Prompt the user for their email address with a simplified list of choices.", + "summary": "Internal API for .onRequestUserInterest Provider to send back response.", + "description": "Provide information about the entity currently displayed or selected on the screen.", "params": [ { - "name": "type", - "summary": "Why the email is being requested, e.g. sign on or sign up", - "required": true, + "name": "correlationId", "schema": { - "$ref": "#/components/schemas/EmailUsage" - } + "type": "string" + }, + "required": true }, { - "name": "message", - "summary": "The message to display while prompting", - "required": false, + "name": "result", "schema": { - "type": "string" - } + "$ref": "#/x-schemas/Entity/EntityDetails", + "examples": [ + { + "identifiers": { + "entityId": "345", + "entityType": "program", + "programType": "movie" + }, + "info": { + "title": "Cool Runnings", + "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pulvinar sapien et ligula ullamcorper malesuada proin libero nunc.", + "releaseDate": "1993-01-01T00:00:00.000Z", + "contentRatings": [ + { + "scheme": "US-Movie", + "rating": "PG" + }, + { + "scheme": "CA-Movie", + "rating": "G" + } + ] + } + } + ] + }, + "required": true } ], "result": { - "name": "email", - "summary": "the selected or entered email", + "name": "result", "schema": { - "type": "string" + "type": "null" } }, "examples": [ { - "name": "Prompt the user to select or type an email address", + "name": "Example", "params": [ { - "name": "type", - "value": "signIn" + "name": "correlationId", + "value": "123" }, { - "name": "message", - "value": "Enter your email to sign into this app" + "name": "result", + "value": { + "identifiers": { + "entityId": "345", + "entityType": "program", + "programType": "movie" + }, + "info": { + "title": "Cool Runnings", + "synopsis": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Pulvinar sapien et ligula ullamcorper malesuada proin libero nunc.", + "releaseDate": "1993-01-01T00:00:00.000Z", + "contentRatings": [ + { + "scheme": "US-Movie", + "rating": "PG" + }, + { + "scheme": "CA-Movie", + "rating": "G" + } + ] + } + } } ], "result": { - "name": "Default Result", - "value": "user@domain.com" + "name": "result", + "value": null } + } + ] + }, + { + "name": "Discovery.userInterestError", + "tags": [ + { + "name": "rpc-only" }, { - "name": "Prompt the user to type an email address to sign up", + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:discovery:interest", + "x-error-for": "Discovery.onRequestUserInterest" + } + ], + "summary": "Internal API for .onRequestUserInterest Provider to send back error.", + "description": "Provide information about the entity currently displayed or selected on the screen.", + "params": [ + { + "name": "correlationId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "error", + "schema": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + } + }, + "examples": [ + { + "name": "Example 1", "params": [ { - "name": "type", - "value": "signUp" + "name": "correlationId", + "value": "123" }, { - "name": "message", - "value": "Enter your email to sign up for this app" + "name": "error", + "value": { + "code": 1, + "message": "Error" + } } ], "result": { - "name": "Default Result", - "value": "user@domain.com" + "name": "result", + "value": null } } ] }, { - "name": "Keyboard.password", + "name": "HDMIInput.ports", "tags": [ { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:input:keyboard" + "xrn:firebolt:capability:inputs:hdmi" ] } ], - "summary": "Show the password entry keyboard, with typing obfuscated from visibility", - "params": [ - { - "name": "message", - "summary": "The message to display while prompting", - "required": false, - "schema": { - "type": "string" - } - } - ], + "summary": "Retrieve a list of HDMI input ports.", + "params": [], "result": { - "name": "value", - "summary": "the selected or entered password", + "name": "ports", "schema": { - "type": "string" + "type": "array", + "items": { + "$ref": "#/components/schemas/HDMIInputPort" + } } }, "examples": [ { - "name": "Prompt the user to enter their password", - "params": [ - { - "name": "message", - "value": "Enter your password" - } - ], + "name": "Default Example", + "params": [], "result": { - "name": "Default Result", - "value": "abc123" + "name": "ports", + "value": [ + { + "port": "HDMI1", + "connected": true, + "signal": "stable", + "arcCapable": true, + "arcConnected": true, + "edidVersion": "2.0", + "autoLowLatencyModeCapable": true, + "autoLowLatencyModeSignalled": true + } + ] } } ] }, { - "name": "Keyboard.standard", + "name": "HDMIInput.port", "tags": [ { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:input:keyboard" + "xrn:firebolt:capability:inputs:hdmi" ] } ], - "summary": "Show the standard platform keyboard, and return the submitted value", + "summary": "Retrieve a specific HDMI input port.", "params": [ { - "name": "message", - "summary": "The message to display while prompting", - "required": true, + "name": "portId", "schema": { - "type": "string" - } + "$ref": "#/components/schemas/HDMIPortId" + }, + "required": true } ], "result": { - "name": "value", - "summary": "the selected or entered text", + "name": "port", "schema": { - "type": "string" + "$ref": "#/components/schemas/HDMIInputPort" } }, "examples": [ { - "name": "Prompt the user for an arbitrary string", + "name": "Default Example", "params": [ { - "name": "message", - "value": "Enter the name you'd like to associate with this device" + "name": "portId", + "value": "HDMI1" } ], "result": { - "name": "Default Result", - "value": "Living Room" + "name": "ports", + "value": { + "port": "HDMI1", + "connected": true, + "signal": "stable", + "arcCapable": true, + "arcConnected": true, + "edidVersion": "2.0", + "autoLowLatencyModeCapable": true, + "autoLowLatencyModeSignalled": true + } } } ] }, { - "name": "Keyboard.onRequestStandard", - "summary": "Registers as a provider for when the user should be shown a standard keyboard.", - "params": [ + "name": "HDMIInput.open", + "tags": [ { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:inputs:hdmi" + ] } ], - "tags": [ - { - "name": "rpc-only" - }, + "summary": "Opens the HDMI Port allowing it to be the active source device. Incase there is a different HDMI portId already set as the active source, this call would stop the older portId before opening the given portId.", + "params": [ { - "name": "event", - "x-response": { - "$ref": "#/components/schemas/KeyboardResult", - "examples": [ - { - "text": "username" - } - ] + "name": "portId", + "schema": { + "$ref": "#/components/schemas/HDMIPortId" }, - "x-error": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - }, - { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true + "required": true } ], "result": { - "name": "sessionRequest", - "summary": "The request to start a keyboard session", + "name": "port", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "$ref": "#/components/schemas/KeyboardProviderRequest" - } - ] + "const": null } }, "examples": [ { - "name": "Default Example", + "name": "Default Example for open", "params": [ { - "name": "listen", - "value": true + "name": "portId", + "value": "HDMI1" } ], "result": { - "name": "Default Result", - "value": { - "correlationId": "abc", - "parameters": { - "message": "Enter your user name." - } - } + "name": "port", + "value": null } } ] }, { - "name": "Keyboard.onRequestPassword", - "summary": "Registers as a provider for when the user should be shown a password keyboard, with dots for each character entered.", - "params": [ + "name": "HDMIInput.close", + "tags": [ { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:inputs:hdmi" + ] } ], - "tags": [ - { - "name": "rpc-only" - }, + "summary": "Closes the given HDMI Port if it is the current active source for HDMI Input. If there was no active source, then there would no action taken on the device.", + "params": [], + "result": { + "name": "port", + "schema": { + "const": null + } + }, + "examples": [ { - "name": "event", - "x-response": { - "$ref": "#/components/schemas/KeyboardResult", - "examples": [ - { - "text": "password" - } - ] - }, - "x-error": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } + "name": "Default Example for stop", + "params": [], + "result": { + "name": "port", + "value": null } + } + ] + }, + { + "name": "HDMIInput.onConnectionChanged", + "tags": [ + { + "name": "event" }, { "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] + } + ], + "summary": "Notification for when any HDMI port has a connection physically engaged or disengaged.", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } } ], "result": { - "name": "sessionRequest", - "summary": "The request to start a keyboard session", + "name": "info", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/components/schemas/KeyboardProviderRequest" + "$ref": "#/components/schemas/ConnectionChangedInfo" } ] } @@ -9886,20 +10335,29 @@ } ], "result": { - "name": "Default Result", + "name": "info", "value": { - "correlationId": "abc", - "parameters": { - "message": "Enter your user name." - } + "port": "HDMI1", + "connected": true } } } ] }, { - "name": "Keyboard.onRequestEmail", - "summary": "Registers as a provider for when the user should be shown a keyboard optimized for email address entry.", + "name": "HDMIInput.onSignalChanged", + "tags": [ + { + "name": "event" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] + } + ], + "summary": "Notification for when any HDMI port has it's signal status changed.", "params": [ { "name": "listen", @@ -9909,61 +10367,15 @@ } } ], - "tags": [ - { - "name": "rpc-only" - }, - { - "name": "event", - "x-response": { - "$ref": "#/components/schemas/KeyboardResult", - "examples": [ - { - "text": "email@address.com" - } - ] - }, - "x-error": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - }, - { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true - } - ], "result": { - "name": "sessionRequest", - "summary": "The request to start a keyboard session", + "name": "info", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/components/schemas/KeyboardProviderRequest" + "$ref": "#/components/schemas/SignalChangedInfo" } ] } @@ -9978,453 +10390,480 @@ } ], "result": { - "name": "Default Result", + "name": "info", "value": { - "correlationId": "abc", - "parameters": { - "message": "Enter your user name." - } + "port": "HDMI1", + "signal": "stable" } } } ] }, { - "name": "Keyboard.standardFocus", - "summary": "Internal API for Standard Provider to request focus for UX purposes.", - "params": [], + "name": "HDMIInput.lowLatencyMode", + "summary": "Property for the low latency mode setting.", "tags": [ { - "name": "rpc-only" + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] }, { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-allow-focus-for": "onRequestStandard" + "name": "property" } ], + "params": [], "result": { - "name": "result", + "name": "enabled", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { - "name": "Example", + "name": "Default Example", "params": [], "result": { - "name": "result", - "value": null + "name": "enabled", + "value": true + } + }, + { + "name": "Default Example #2", + "params": [], + "result": { + "name": "enabled", + "value": false } } ] }, { - "name": "Keyboard.passwordFocus", - "summary": "Internal API for Password Provider to request focus for UX purposes.", - "params": [], + "name": "HDMIInput.onAutoLowLatencyModeSignalChanged", + "summary": "Notification for changes to ALLM status of any input device.", "tags": [ { - "name": "rpc-only" + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] }, { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-allow-focus-for": "onRequestPassword" + "name": "event" + } + ], + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } } ], "result": { - "name": "result", + "name": "info", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/components/schemas/AutoLowLatencyModeSignalChangedInfo" + } + ] } }, "examples": [ { - "name": "Example", - "params": [], + "name": "Default Example", + "params": [ + { + "name": "listen", + "value": true + } + ], "result": { - "name": "result", - "value": null + "name": "info", + "value": { + "port": "HDMI1", + "autoLowLatencyModeSignalled": true + } } } ] }, { - "name": "Keyboard.emailFocus", - "summary": "Internal API for Email Provider to request focus for UX purposes.", - "params": [], + "name": "HDMIInput.autoLowLatencyModeCapable", + "summary": "Property for each port auto low latency mode setting.", "tags": [ { - "name": "rpc-only" + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] }, { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-allow-focus-for": "onRequestEmail" + "name": "property", + "x-subscriber-type": "global" + } + ], + "params": [ + { + "name": "port", + "required": true, + "schema": { + "$ref": "#/components/schemas/HDMIPortId" + } } ], "result": { - "name": "result", + "name": "enabled", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { - "name": "Example", - "params": [], + "name": "Default Example", + "params": [ + { + "name": "port", + "value": "HDMI1" + } + ], "result": { - "name": "result", - "value": null + "name": "enabled", + "value": true + } + }, + { + "name": "Default Example #2", + "params": [ + { + "name": "port", + "value": "HDMI1" + } + ], + "result": { + "name": "enabled", + "value": false } } ] }, { - "name": "Keyboard.standardResponse", - "summary": "Internal API for Standard Provider to send back response.", + "name": "HDMIInput.edidVersion", + "summary": "Property for each port's active EDID version.", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] + }, + { + "name": "property" + } + ], "params": [ { - "name": "response", + "name": "port", "required": true, "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/KeyboardResult", - "examples": [ - { - "text": "username" - } - ] - } - } - } - ] + "$ref": "#/components/schemas/HDMIPortId" } } ], - "tags": [ - { - "name": "rpc-only" - }, - { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-response-for": "onRequestStandard" - } - ], "result": { - "name": "result", + "name": "edidVersion", "schema": { - "type": "null" + "$ref": "#/components/schemas/EDIDVersion" } }, "examples": [ { - "name": "Example", + "name": "Default Example", "params": [ { - "name": "response", - "value": { - "correlationId": "123", - "result": { - "text": "username" - } - } + "name": "port", + "value": "HDMI1" } ], "result": { - "name": "result", - "value": null + "name": "edidVersion", + "value": "2.0" + } + }, + { + "name": "Default Example #2", + "params": [ + { + "name": "port", + "value": "HDMI1" + } + ], + "result": { + "name": "edidVersion", + "value": "1.4" } } ] }, { - "name": "Keyboard.standardError", - "summary": "Internal API for Standard Provider to send back error.", - "params": [ - { - "name": "error", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - } - } - ] - } - } - ], + "name": "HDMIInput.onLowLatencyModeChanged", + "summary": "Property for the low latency mode setting.", "tags": [ { - "name": "rpc-only" + "name": "subscriber", + "x-subscriber-for": "HDMIInput.lowLatencyMode" + }, + { + "name": "event", + "x-alternative": "lowLatencyMode" }, { "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-error-for": "onRequestStandard" + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] + } + ], + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } } ], "result": { - "name": "result", + "name": "enabled", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ { - "name": "Example 1", + "name": "Default Example", "params": [ { - "name": "error", - "value": { - "correlationId": "123", - "result": { - "code": 1, - "message": "Error" - } - } + "name": "listen", + "value": true } ], "result": { - "name": "result", - "value": null + "name": "enabled", + "value": true + } + }, + { + "name": "Default Example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "enabled", + "value": false } } ] }, { - "name": "Keyboard.passwordResponse", - "summary": "Internal API for Password Provider to send back response.", - "params": [ - { - "name": "response", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/KeyboardResult", - "examples": [ - { - "text": "password" - } - ] - } - } - } - ] - } - } - ], + "name": "HDMIInput.onAutoLowLatencyModeCapableChanged", + "summary": "Property for each port auto low latency mode setting.", "tags": [ { - "name": "rpc-only" + "name": "subscriber", + "x-subscriber-for": "HDMIInput.autoLowLatencyModeCapable" + }, + { + "name": "event", + "x-alternative": "autoLowLatencyModeCapable" }, { "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-response-for": "onRequestPassword" + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] + } + ], + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } } ], "result": { - "name": "result", + "name": "data", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/components/schemas/AutoLowLatencyModeCapableChangedInfo" + } + ] } }, "examples": [ { - "name": "Example", + "name": "Default Example", "params": [ { - "name": "response", - "value": { - "correlationId": "123", - "result": { - "text": "password" - } - } + "name": "listen", + "value": true } ], "result": { - "name": "result", - "value": null + "name": "data", + "value": { + "port": "HDMI1", + "enabled": true + } + } + }, + { + "name": "Default Example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "data", + "value": { + "port": "HDMI1", + "enabled": false + } } } ] }, { - "name": "Keyboard.passwordError", - "summary": "Internal API for Password Provider to send back error.", + "name": "HDMIInput.onEdidVersionChanged", + "summary": "Property for each port's active EDID version.", + "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "HDMIInput.edidVersion" + }, + { + "name": "event", + "x-alternative": "edidVersion" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:inputs:hdmi" + ] + } + ], "params": [ { - "name": "error", + "name": "port", "required": true, "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - } - } - ] + "$ref": "#/components/schemas/HDMIPortId" } - } - ], - "tags": [ - { - "name": "rpc-only" }, { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-error-for": "onRequestPassword" + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } } ], "result": { - "name": "result", + "name": "edidVersion", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/components/schemas/EDIDVersion" + } + ] } }, "examples": [ { - "name": "Example 1", + "name": "Default Example", "params": [ { - "name": "error", - "value": { - "correlationId": "123", - "result": { - "code": 1, - "message": "Error" - } - } + "name": "port", + "value": "HDMI1" + }, + { + "name": "listen", + "value": true } ], "result": { - "name": "result", - "value": null + "name": "edidVersion", + "value": "2.0" + } + }, + { + "name": "Default Example #2", + "params": [ + { + "name": "port", + "value": "HDMI1" + }, + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "edidVersion", + "value": "1.4" } } ] }, { - "name": "Keyboard.emailResponse", - "summary": "Internal API for Email Provider to send back response.", - "params": [ - { - "name": "response", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/KeyboardResult", - "examples": [ - { - "text": "email@address.com" - } - ] - } - } - } - ] - } - } - ], + "name": "HDMIInput.setLowLatencyMode", + "summary": "Property for the low latency mode setting.", "tags": [ { - "name": "rpc-only" + "name": "setter", + "x-setter-for": "lowLatencyMode" }, { "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-response-for": "onRequestEmail" + "x-manages": [ + "xrn:firebolt:capability:inputs:hdmi" + ] + } + ], + "params": [ + { + "name": "value", + "schema": { + "type": "boolean" + }, + "required": true } ], "result": { @@ -10435,409 +10874,351 @@ }, "examples": [ { - "name": "Example", + "name": "Default Example", "params": [ { - "name": "response", - "value": { - "correlationId": "123", - "result": { - "text": "email@address.com" - } - } + "name": "value", + "value": true } ], "result": { - "name": "result", + "name": "enabled", "value": null } - } - ] - }, - { - "name": "Keyboard.emailError", - "summary": "Internal API for Email Provider to send back error.", - "params": [ - { - "name": "error", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - } - } - ] - } - } - ], - "tags": [ - { - "name": "rpc-only" }, { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:input:keyboard", - "x-allow-focus": true, - "x-error-for": "onRequestEmail" - } - ], - "result": { - "name": "result", - "schema": { - "type": "null" - } - }, - "examples": [ - { - "name": "Example 1", + "name": "Default Example #2", "params": [ { - "name": "error", - "value": { - "correlationId": "123", - "result": { - "code": 1, - "message": "Error" - } - } + "name": "value", + "value": false } ], "result": { - "name": "result", + "name": "enabled", "value": null } } ] }, { - "name": "Lifecycle.ready", + "name": "HDMIInput.setAutoLowLatencyModeCapable", + "summary": "Property for each port auto low latency mode setting.", "tags": [ { - "name": "calls-metrics" + "name": "setter", + "x-setter-for": "autoLowLatencyModeCapable" }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:lifecycle:ready" + "x-manages": [ + "xrn:firebolt:capability:inputs:hdmi" ] + } + ], + "params": [ + { + "name": "port", + "required": true, + "schema": { + "$ref": "#/components/schemas/HDMIPortId" + } }, { - "name": "exclude-from-sdk" + "name": "value", + "schema": { + "type": "boolean" + }, + "required": true } ], - "summary": "Notify the platform that the app is ready", - "params": [], "result": { "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Let the platform know that your app is ready", - "params": [], + "name": "Default Example", + "params": [ + { + "name": "port", + "value": "HDMI1" + }, + { + "name": "value", + "value": true + } + ], "result": { - "name": "Default Result", + "name": "enabled", + "value": null + } + }, + { + "name": "Default Example #2", + "params": [ + { + "name": "port", + "value": "HDMI1" + }, + { + "name": "value", + "value": false + } + ], + "result": { + "name": "enabled", "value": null } } ] }, { - "name": "Lifecycle.close", + "name": "HDMIInput.setEdidVersion", + "summary": "Property for each port's active EDID version.", "tags": [ + { + "name": "setter", + "x-setter-for": "edidVersion" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" + "x-manages": [ + "xrn:firebolt:capability:inputs:hdmi" ] } ], - "summary": "Request that the platform move your app out of focus", "params": [ { - "name": "reason", - "summary": "The reason the app is requesting to be closed", + "name": "port", "required": true, "schema": { - "$ref": "#/x-schemas/Lifecycle/CloseReason" + "$ref": "#/components/schemas/HDMIPortId" } + }, + { + "name": "value", + "schema": { + "$ref": "#/components/schemas/EDIDVersion" + }, + "required": true } ], "result": { - "name": "success", + "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Close the app when the user presses back on the app home screen", + "name": "Default Example", "params": [ { - "name": "reason", - "value": "remoteButton" + "name": "port", + "value": "HDMI1" + }, + { + "name": "value", + "value": "2.0" } ], "result": { - "name": "Default Result", + "name": "edidVersion", "value": null } }, { - "name": "Close the app when the user selects an exit menu item", + "name": "Default Example #2", "params": [ { - "name": "reason", - "value": "userExit" + "name": "port", + "value": "HDMI1" + }, + { + "name": "value", + "value": "1.4" } ], "result": { - "name": "Default Result", + "name": "edidVersion", "value": null } } ] }, { - "name": "Lifecycle.finished", + "name": "Keyboard.email", "tags": [ - { - "name": "exclude-from-sdk" - }, { "name": "capabilities", + "x-provided-by": "Keyboard.onRequestEmail", "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" - ] + "xrn:firebolt:capability:input:keyboard" + ], + "x-allow-focus": true } ], - "summary": "Notify the platform that the app is done unloading", - "params": [], - "result": { - "name": "results", - "schema": { - "const": null - } - }, - "examples": [ + "summary": "Prompt the user for their email address with a simplified list of choices.", + "params": [ { - "name": "Default Example", - "params": [], - "result": { - "name": "Default Result", - "value": null + "name": "type", + "summary": "Why the email is being requested, e.g. sign on or sign up", + "required": true, + "schema": { + "$ref": "#/components/schemas/EmailUsage" } - } - ] - }, - { - "name": "Lifecycle.state", - "summary": "Get the current state of the app. This function is **synchronous**.", - "tags": [ - { - "name": "synchronous" - }, - { - "name": "exclude-from-sdk" }, { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" - ] + "name": "message", + "summary": "The message to display while prompting", + "required": false, + "schema": { + "type": "string" + } } ], - "params": [], "result": { - "name": "state", - "summary": "the current state of the app.", + "name": "email", + "summary": "the selected or entered email", "schema": { - "$ref": "#/x-schemas/Lifecycle/LifecycleState" + "type": "string" } }, "examples": [ { - "name": "Default Example", - "params": [], + "name": "Prompt the user to select or type an email address", + "params": [ + { + "name": "type", + "value": "signIn" + }, + { + "name": "message", + "value": "Enter your email to sign into this app" + } + ], "result": { "name": "Default Result", - "value": "foreground" + "value": "user@domain.com" + } + }, + { + "name": "Prompt the user to type an email address to sign up", + "params": [ + { + "name": "type", + "value": "signUp" + }, + { + "name": "message", + "value": "Enter your email to sign up for this app" + } + ], + "result": { + "name": "Default Result", + "value": "user@domain.com" } } ] }, { - "name": "Lifecycle.onInactive", + "name": "Keyboard.password", "tags": [ - { - "name": "event" - }, { "name": "capabilities", + "x-provided-by": "Keyboard.onRequestPassword", "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" - ] + "xrn:firebolt:capability:input:keyboard" + ], + "x-allow-focus": true } ], - "summary": "Listen to the inactive event", + "summary": "Show the password entry keyboard, with typing obfuscated from visibility", "params": [ { - "name": "listen", - "required": true, + "name": "message", + "summary": "The message to display while prompting", + "required": false, "schema": { - "type": "boolean" + "type": "string" } } ], "result": { "name": "value", + "summary": "the selected or entered password", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "$ref": "#/components/schemas/LifecycleEvent" - } - ] + "type": "string" } }, "examples": [ { - "name": "Default Example", + "name": "Prompt the user to enter their password", "params": [ { - "name": "listen", - "value": true + "name": "message", + "value": "Enter your password" } ], "result": { "name": "Default Result", - "value": { - "state": "inactive", - "previous": "initializing" - } + "value": "abc123" } } ] }, { - "name": "Lifecycle.onForeground", + "name": "Keyboard.standard", "tags": [ - { - "name": "event" - }, { "name": "capabilities", + "x-provided-by": "Keyboard.onRequestStandard", "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" - ] + "xrn:firebolt:capability:input:keyboard" + ], + "x-allow-focus": true } ], - "summary": "Listen to the foreground event", + "summary": "Show the standard platform keyboard, and return the submitted value", "params": [ { - "name": "listen", + "name": "message", + "summary": "The message to display while prompting", "required": true, "schema": { - "type": "boolean" + "type": "string" } } ], "result": { "name": "value", + "summary": "the selected or entered text", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "$ref": "#/components/schemas/LifecycleEvent" - } - ] + "type": "string" } }, "examples": [ { - "name": "Default Example", + "name": "Prompt the user for an arbitrary string", "params": [ { - "name": "listen", - "value": true + "name": "message", + "value": "Enter the name you'd like to associate with this device" } ], "result": { "name": "Default Result", - "value": { - "state": "foreground", - "previous": "inactive" - } - } - }, - { - "name": "Move to foreground via remote branded buton", - "params": [ - { - "name": "listen", - "value": true - } - ], - "result": { - "name": "value", - "value": { - "state": "foreground", - "previous": "inactive", - "source": "remote" - } + "value": "Living Room" } } ] }, { - "name": "Lifecycle.onBackground", - "tags": [ - { - "name": "event" - }, - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" - ] - } - ], - "summary": "Listen to the background event", + "name": "Keyboard.onRequestStandard", + "summary": "Registers as a provider for when the user should be shown a standard keyboard.", "params": [ { "name": "listen", @@ -10847,15 +11228,59 @@ } } ], + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "event", + "x-response": { + "type": "string", + "examples": [ + "username" + ] + }, + "x-error": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + } + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true + } + ], "result": { - "name": "value", + "name": "sessionRequest", + "summary": "The request to start a keyboard session", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/components/schemas/LifecycleEvent" + "$ref": "#/components/schemas/KeyboardProviderRequest" } ] } @@ -10872,28 +11297,19 @@ "result": { "name": "Default Result", "value": { - "state": "background", - "previous": "foreground" + "correlationId": "abc", + "parameters": { + "message": "Enter your user name." + } } } } ] }, { - "name": "Lifecycle.onSuspended", - "tags": [ - { - "name": "event" - }, - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" - ] - } - ], - "summary": "Listen to the suspended event", - "params": [ + "name": "Keyboard.onRequestPassword", + "summary": "Registers as a provider for when the user should be shown a password keyboard, with dots for each character entered.", + "params": [ { "name": "listen", "required": true, @@ -10902,15 +11318,59 @@ } } ], + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "event", + "x-response": { + "type": "string", + "examples": [ + "password" + ] + }, + "x-error": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + } + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true + } + ], "result": { - "name": "value", + "name": "sessionRequest", + "summary": "The request to start a keyboard session", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/components/schemas/LifecycleEvent" + "$ref": "#/components/schemas/KeyboardProviderRequest" } ] } @@ -10927,27 +11387,18 @@ "result": { "name": "Default Result", "value": { - "state": "suspended", - "previous": "inactive" + "correlationId": "abc", + "parameters": { + "message": "Enter your user name." + } } } } ] }, { - "name": "Lifecycle.onUnloading", - "tags": [ - { - "name": "event" - }, - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" - ] - } - ], - "summary": "Listen to the unloading event", + "name": "Keyboard.onRequestEmail", + "summary": "Registers as a provider for when the user should be shown a keyboard optimized for email address entry.", "params": [ { "name": "listen", @@ -10957,15 +11408,59 @@ } } ], + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "event", + "x-response": { + "type": "string", + "examples": [ + "email@address.com" + ] + }, + "x-error": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + } + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true + } + ], "result": { - "name": "value", + "name": "sessionRequest", + "summary": "The request to start a keyboard session", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/components/schemas/LifecycleEvent" + "$ref": "#/components/schemas/KeyboardProviderRequest" } ] } @@ -10982,640 +11477,701 @@ "result": { "name": "Default Result", "value": { - "state": "unloading", - "previous": "inactive" + "correlationId": "abc", + "parameters": { + "message": "Enter your user name." + } } } } ] }, { - "name": "Localization.locality", + "name": "Keyboard.standardFocus", + "summary": "Internal API for Standard Provider to request focus for UX purposes.", + "params": [], "tags": [ { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:locality" - ] + "name": "rpc-only" }, { - "name": "property" + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-allow-focus-for": "onRequestStandard" } ], - "summary": "Get the locality/city the device is located in", - "params": [], "result": { - "name": "locality", - "summary": "the device city", + "name": "result", "schema": { - "$ref": "#/x-schemas/Localization/Locality" + "type": "null" } }, "examples": [ { - "name": "Default example #1", - "params": [], - "result": { - "name": "Default Result", - "value": "Philadelphia" - } - }, - { - "name": "Default example #2", + "name": "Example", "params": [], "result": { - "name": "Default Result", - "value": "Rockville" + "name": "result", + "value": null } } ] }, { - "name": "Localization.postalCode", + "name": "Keyboard.passwordFocus", + "summary": "Internal API for Password Provider to request focus for UX purposes.", + "params": [], "tags": [ { - "name": "property" + "name": "rpc-only" }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:postal-code" - ] + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-allow-focus-for": "onRequestPassword" } ], - "summary": "Get the postal code the device is located in", - "params": [], "result": { - "name": "postalCode", - "summary": "the device postal code", + "name": "result", "schema": { - "type": "string" + "type": "null" } }, "examples": [ { - "name": "Default example #1", - "params": [], - "result": { - "name": "Default Result", - "value": "19103" - } - }, - { - "name": "Default example #2", + "name": "Example", "params": [], "result": { - "name": "Default Result", - "value": "20850" + "name": "result", + "value": null } } ] }, { - "name": "Localization.countryCode", + "name": "Keyboard.emailFocus", + "summary": "Internal API for Email Provider to request focus for UX purposes.", + "params": [], "tags": [ { - "name": "property" + "name": "rpc-only" }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:country-code" - ] + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-allow-focus-for": "onRequestEmail" } ], - "summary": "Get the ISO 3166-1 alpha-2 code for the country device is located in", - "params": [], "result": { - "name": "code", - "summary": "the device country code", + "name": "result", "schema": { - "$ref": "#/x-schemas/Localization/CountryCode" + "type": "null" } }, "examples": [ { - "name": "Default example #1", - "params": [], - "result": { - "name": "Default Result", - "value": "US" - } - }, - { - "name": "Default example #2", + "name": "Example", "params": [], "result": { - "name": "Default Result", - "value": "UK" + "name": "result", + "value": null } } ] }, { - "name": "Localization.language", - "summary": "Get the ISO 639 1/2 code for the preferred language", - "params": [], - "tags": [ - { - "name": "deprecated", - "x-since": "0.17.0", - "x-alternative": "Localization.locale" - }, + "name": "Keyboard.standardResponse", + "summary": "Internal API for Standard Provider to send back response.", + "params": [ { - "name": "property" + "name": "correlationId", + "schema": { + "type": "string" + }, + "required": true }, { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:language" - ] - } + "name": "result", + "schema": { + "type": "string", + "examples": [ + "username" + ] + }, + "required": true + } + ], + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-response-for": "onRequestStandard" + } ], "result": { - "name": "lang", - "summary": "the device language", + "name": "result", "schema": { - "$ref": "#/x-schemas/Localization/Language" + "type": "null" } }, "examples": [ { - "name": "Default example #1", - "params": [], - "result": { - "name": "Default Result", - "value": "en" - } - }, - { - "name": "Default example #2", - "params": [], + "name": "Example", + "params": [ + { + "name": "correlationId", + "value": "123" + }, + { + "name": "result", + "value": "username" + } + ], "result": { - "name": "Default Result", - "value": "es" + "name": "result", + "value": null } } ] }, { - "name": "Localization.preferredAudioLanguages", - "summary": "A prioritized list of ISO 639 1/2 codes for the preferred audio languages on this device.", - "params": [], + "name": "Keyboard.standardError", + "summary": "Internal API for Standard Provider to send back error.", + "params": [ + { + "name": "correlationId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "error", + "schema": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + }, + "required": true + } + ], "tags": [ { - "name": "property" + "name": "rpc-only" }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:language" - ] + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-error-for": "onRequestStandard" } ], "result": { - "name": "languages", - "summary": "the preferred audio languages", + "name": "result", "schema": { - "type": "array", - "items": { - "$ref": "#/x-schemas/Localization/ISO639_2Language" - } + "type": "null" } }, "examples": [ { - "name": "Default Example", - "params": [], - "result": { - "name": "Default Result", - "value": [ - "spa", - "eng" - ] - } - }, - { - "name": "Default Example #2", - "params": [], + "name": "Example 1", + "params": [ + { + "name": "correlationId", + "value": "123" + }, + { + "name": "error", + "value": { + "code": 1, + "message": "Error" + } + } + ], "result": { - "name": "Default Result", - "value": [ - "eng", - "spa" - ] + "name": "result", + "value": null } } ] }, { - "name": "Localization.locale", + "name": "Keyboard.passwordResponse", + "summary": "Internal API for Password Provider to send back response.", + "params": [ + { + "name": "correlationId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "result", + "schema": { + "type": "string", + "examples": [ + "password" + ] + }, + "required": true + } + ], "tags": [ { - "name": "property" + "name": "rpc-only" }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:locale" - ] + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-response-for": "onRequestPassword" } ], - "summary": "Get the *full* BCP 47 code, including script, region, variant, etc., for the preferred langauage/locale", - "params": [], "result": { - "name": "locale", - "summary": "the device locale", + "name": "result", "schema": { - "$ref": "#/x-schemas/Localization/Locale" + "type": "null" } }, "examples": [ { - "name": "Default example #1", - "params": [], - "result": { - "name": "Default Result", - "value": "en-US" - } - }, - { - "name": "Default example #2", - "params": [], + "name": "Example", + "params": [ + { + "name": "correlationId", + "value": "123" + }, + { + "name": "result", + "value": "password" + } + ], "result": { - "name": "Default Result", - "value": "es-US" + "name": "result", + "value": null } } ] }, { - "name": "Localization.latlon", + "name": "Keyboard.passwordError", + "summary": "Internal API for Password Provider to send back error.", + "params": [ + { + "name": "correlationId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "error", + "schema": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + }, + "required": true + } + ], "tags": [ + { + "name": "rpc-only" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:location" - ] + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-error-for": "onRequestPassword" } ], - "summary": "Get the approximate latitude and longitude coordinates of the device location", - "params": [], "result": { - "name": "latlong", - "summary": "lat/long tuple", + "name": "result", "schema": { - "$ref": "#/components/schemas/LatLon" + "type": "null" } }, "examples": [ { - "name": "Default Example", - "params": [], - "result": { - "name": "Default Result", - "value": [ - 39.9549, - 75.1699 - ] - } - } - ] - }, - { - "name": "Localization.additionalInfo", - "tags": [ - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:localization:additional-info" - ] - } - ], - "summary": "Get any platform-specific localization information, in an Map", - "params": [], - "result": { - "name": "info", - "summary": "the additional info", - "schema": { - "type": "object", - "additionalProperties": { - "type": "string", - "maxLength": 1024 - }, - "maxProperties": 32 - } - }, - "examples": [ - { - "name": "Default Example", - "params": [], + "name": "Example 1", + "params": [ + { + "name": "correlationId", + "value": "123" + }, + { + "name": "error", + "value": { + "code": 1, + "message": "Error" + } + } + ], "result": { - "name": "info", - "value": {} + "name": "result", + "value": null } } ] }, { - "name": "Localization.addAdditionalInfo", - "tags": [ - { - "name": "capabilities", - "x-manages": [ - "xrn:firebolt:capability:localization:additional-info" - ] - } - ], - "summary": "Add any platform-specific localization information in key/value pair", + "name": "Keyboard.emailResponse", + "summary": "Internal API for Email Provider to send back response.", "params": [ { - "name": "key", - "summary": "Key to add additionalInfo", + "name": "correlationId", "schema": { "type": "string" }, "required": true }, { - "name": "value", - "summary": "Value to be set for additionalInfo", + "name": "result", "schema": { - "type": "string" + "type": "string", + "examples": [ + "email@address.com" + ] }, "required": true } ], + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-response-for": "onRequestEmail" + } + ], "result": { "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Add an additionalInfo for localization", + "name": "Example", "params": [ { - "name": "key", - "value": "defaultKey" + "name": "correlationId", + "value": "123" }, { - "name": "value", - "value": "defaultValue=" + "name": "result", + "value": "email@address.com" } ], "result": { - "name": "defaultResult", + "name": "result", "value": null } } ] }, { - "name": "Localization.removeAdditionalInfo", - "tags": [ - { - "name": "capabilities", - "x-manages": [ - "xrn:firebolt:capability:localization:additional-info" - ] - } - ], - "summary": "Remove any platform-specific localization information from map", + "name": "Keyboard.emailError", + "summary": "Internal API for Email Provider to send back error.", "params": [ { - "name": "key", - "summary": "Key to remove additionalInfo", + "name": "correlationId", "schema": { "type": "string" }, "required": true + }, + { + "name": "error", + "schema": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + }, + "required": true + } + ], + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:input:keyboard", + "x-allow-focus": true, + "x-error-for": "onRequestEmail" } ], "result": { "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Remove an additionalInfo for localization", + "name": "Example 1", "params": [ { - "name": "key", - "value": "defaultKey" + "name": "correlationId", + "value": "123" + }, + { + "name": "error", + "value": { + "code": 1, + "message": "Error" + } } ], "result": { - "name": "defaultResult", + "name": "result", "value": null } } ] }, { - "name": "Localization.timeZone", + "name": "Lifecycle.ready", "tags": [ { - "name": "property" + "name": "calls-metrics" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:time-zone" + "xrn:firebolt:capability:lifecycle:ready" ] + }, + { + "name": "exclude-from-sdk" } ], - "summary": "Set the IANA timezone for the device", + "summary": "Notify the platform that the app is ready", "params": [], "result": { "name": "result", "schema": { - "$ref": "#/x-schemas/Localization/TimeZone" + "const": null } }, "examples": [ { - "name": "Default Example", - "params": [], - "result": { - "name": "Default Result", - "value": "America/New_York" - } - }, - { - "name": "Additional Example", + "name": "Let the platform know that your app is ready", "params": [], "result": { "name": "Default Result", - "value": "America/Los_Angeles" + "value": null } } ] }, { - "name": "Localization.onLocalityChanged", + "name": "Lifecycle.close", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "locality" - }, - { - "name": "event", - "x-alternative": "locality" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:locality" + "xrn:firebolt:capability:lifecycle:state" ] } ], - "summary": "Get the locality/city the device is located in", + "summary": "Request that the platform move your app out of focus", "params": [ { - "name": "listen", + "name": "reason", + "summary": "The reason the app is requesting to be closed", "required": true, "schema": { - "type": "boolean" + "$ref": "#/x-schemas/Lifecycle/CloseReason" } } ], "result": { - "name": "locality", - "summary": "the device city", + "name": "success", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "$ref": "#/x-schemas/Localization/Locality" - } - ] + "const": null } }, "examples": [ { - "name": "Default example #1", + "name": "Close the app when the user presses back on the app home screen", "params": [ { - "name": "listen", - "value": true + "name": "reason", + "value": "remoteButton" } ], "result": { "name": "Default Result", - "value": "Philadelphia" + "value": null } }, { - "name": "Default example #2", + "name": "Close the app when the user selects an exit menu item", "params": [ { - "name": "listen", - "value": true + "name": "reason", + "value": "userExit" } ], "result": { "name": "Default Result", - "value": "Rockville" + "value": null } } ] }, { - "name": "Localization.onPostalCodeChanged", + "name": "Lifecycle.finished", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "postalCode" - }, - { - "name": "event", - "x-alternative": "postalCode" + "name": "exclude-from-sdk" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:postal-code" + "xrn:firebolt:capability:lifecycle:state" ] } ], - "summary": "Get the postal code the device is located in", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "summary": "Notify the platform that the app is done unloading", + "params": [], "result": { - "name": "postalCode", - "summary": "the device postal code", + "name": "results", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "string" - } - ] + "const": null } }, "examples": [ { - "name": "Default example #1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "name": "Default Example", + "params": [], "result": { "name": "Default Result", - "value": "19103" + "value": null } + } + ] + }, + { + "name": "Lifecycle.state", + "summary": "Get the current state of the app. This function is **synchronous**.", + "tags": [ + { + "name": "synchronous" }, { - "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true - } - ], + "name": "exclude-from-sdk" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:lifecycle:state" + ] + } + ], + "params": [], + "result": { + "name": "state", + "summary": "the current state of the app.", + "schema": { + "$ref": "#/x-schemas/Lifecycle/LifecycleState" + } + }, + "examples": [ + { + "name": "Default Example", + "params": [], "result": { "name": "Default Result", - "value": "20850" + "value": "foreground" } } ] }, { - "name": "Localization.onCountryCodeChanged", + "name": "Lifecycle.onInactive", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "countryCode" - }, - { - "name": "event", - "x-alternative": "countryCode" + "name": "event" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:country-code" + "xrn:firebolt:capability:lifecycle:state" ] } ], - "summary": "Get the ISO 3166-1 alpha-2 code for the country device is located in", + "summary": "Listen to the inactive event", "params": [ { "name": "listen", @@ -11626,22 +12182,21 @@ } ], "result": { - "name": "code", - "summary": "the device country code", + "name": "value", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/x-schemas/Localization/CountryCode" + "$ref": "#/components/schemas/LifecycleEvent" } ] } }, "examples": [ { - "name": "Default example #1", + "name": "Default Example", "params": [ { "name": "listen", @@ -11650,74 +12205,53 @@ ], "result": { "name": "Default Result", - "value": "US" - } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true + "value": { + "state": "inactive", + "previous": "initializing" } - ], - "result": { - "name": "Default Result", - "value": "UK" } } ] }, { - "name": "Localization.onLanguageChanged", - "summary": "Get the ISO 639 1/2 code for the preferred language", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "name": "Lifecycle.onForeground", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "language" - }, - { - "name": "event", - "x-alternative": "language" - }, - { - "name": "deprecated", - "x-since": "0.17.0", - "x-alternative": "Localization.locale" + "name": "event" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:language" + "xrn:firebolt:capability:lifecycle:state" ] } ], + "summary": "Listen to the foreground event", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "result": { - "name": "lang", - "summary": "the device language", + "name": "value", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/x-schemas/Localization/Language" + "$ref": "#/components/schemas/LifecycleEvent" } ] } }, "examples": [ { - "name": "Default example #1", + "name": "Default Example", "params": [ { "name": "listen", @@ -11726,11 +12260,14 @@ ], "result": { "name": "Default Result", - "value": "en" + "value": { + "state": "foreground", + "previous": "inactive" + } } }, { - "name": "Default example #2", + "name": "Move to foreground via remote branded buton", "params": [ { "name": "listen", @@ -11738,53 +12275,48 @@ } ], "result": { - "name": "Default Result", - "value": "es" + "name": "value", + "value": { + "state": "foreground", + "previous": "inactive", + "source": "remote" + } } } ] }, { - "name": "Localization.onPreferredAudioLanguagesChanged", - "summary": "A prioritized list of ISO 639 1/2 codes for the preferred audio languages on this device.", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "name": "Lifecycle.onBackground", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "preferredAudioLanguages" - }, - { - "name": "event", - "x-alternative": "preferredAudioLanguages" + "name": "event" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:language" + "xrn:firebolt:capability:lifecycle:state" ] } ], + "summary": "Listen to the background event", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "result": { - "name": "languages", - "summary": "the preferred audio languages", + "name": "value", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "type": "array", - "items": { - "$ref": "#/x-schemas/Localization/ISO639_2Language" - } + "$ref": "#/components/schemas/LifecycleEvent" } ] } @@ -11800,49 +12332,28 @@ ], "result": { "name": "Default Result", - "value": [ - "spa", - "eng" - ] - } - }, - { - "name": "Default Example #2", - "params": [ - { - "name": "listen", - "value": true + "value": { + "state": "background", + "previous": "foreground" } - ], - "result": { - "name": "Default Result", - "value": [ - "eng", - "spa" - ] } } ] }, { - "name": "Localization.onLocaleChanged", + "name": "Lifecycle.onSuspended", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "locale" - }, - { - "name": "event", - "x-alternative": "locale" + "name": "event" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:locale" + "xrn:firebolt:capability:lifecycle:state" ] } ], - "summary": "Get the *full* BCP 47 code, including script, region, variant, etc., for the preferred langauage/locale", + "summary": "Listen to the suspended event", "params": [ { "name": "listen", @@ -11853,22 +12364,21 @@ } ], "result": { - "name": "locale", - "summary": "the device locale", + "name": "value", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/x-schemas/Localization/Locale" + "$ref": "#/components/schemas/LifecycleEvent" } ] } }, "examples": [ { - "name": "Default example #1", + "name": "Default Example", "params": [ { "name": "listen", @@ -11877,43 +12387,28 @@ ], "result": { "name": "Default Result", - "value": "en-US" - } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true + "value": { + "state": "suspended", + "previous": "inactive" } - ], - "result": { - "name": "Default Result", - "value": "es-US" } } ] }, { - "name": "Localization.onTimeZoneChanged", + "name": "Lifecycle.onUnloading", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "timeZone" - }, - { - "name": "event", - "x-alternative": "timeZone" + "name": "event" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:localization:time-zone" + "xrn:firebolt:capability:lifecycle:state" ] } ], - "summary": "Set the IANA timezone for the device", + "summary": "Listen to the unloading event", "params": [ { "name": "listen", @@ -11924,14 +12419,14 @@ } ], "result": { - "name": "result", + "name": "value", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/x-schemas/Localization/TimeZone" + "$ref": "#/components/schemas/LifecycleEvent" } ] } @@ -11947,2822 +12442,2606 @@ ], "result": { "name": "Default Result", - "value": "America/New_York" - } - }, - { - "name": "Additional Example", - "params": [ - { - "name": "listen", - "value": true + "value": { + "state": "unloading", + "previous": "inactive" } - ], - "result": { - "name": "Default Result", - "value": "America/Los_Angeles" } } ] }, { - "name": "Localization.setLocality", + "name": "Localization.locality", "tags": [ - { - "name": "setter", - "x-setter-for": "locality" - }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:localization:locality" ] - } - ], - "summary": "Get the locality/city the device is located in", - "params": [ + }, { - "name": "value", - "summary": "the device city", - "schema": { - "$ref": "#/x-schemas/Localization/Locality" - }, - "required": true + "name": "property" } ], + "summary": "Get the locality/city the device is located in", + "params": [], "result": { - "name": "result", + "name": "locality", + "summary": "the device city", "schema": { - "type": "null" + "$ref": "#/x-schemas/Localization/Locality" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": "Philadelphia" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "Philadelphia" } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": "Rockville" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "Rockville" } } ] }, { - "name": "Localization.setPostalCode", + "name": "Localization.postalCode", "tags": [ { - "name": "setter", - "x-setter-for": "postalCode" + "name": "property" }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:localization:postal-code" ] } ], "summary": "Get the postal code the device is located in", - "params": [ - { - "name": "value", - "summary": "the device postal code", - "schema": { - "type": "string" - }, - "required": true - } - ], + "params": [], "result": { - "name": "result", + "name": "postalCode", + "summary": "the device postal code", "schema": { - "type": "null" + "type": "string" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": "19103" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "19103" } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": "20850" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "20850" } } ] }, { - "name": "Localization.setCountryCode", + "name": "Localization.countryCode", "tags": [ { - "name": "setter", - "x-setter-for": "countryCode" + "name": "property" }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:localization:country-code" ] } ], "summary": "Get the ISO 3166-1 alpha-2 code for the country device is located in", - "params": [ - { - "name": "value", - "summary": "the device country code", - "schema": { - "$ref": "#/x-schemas/Localization/CountryCode" - }, - "required": true - } - ], + "params": [], "result": { - "name": "result", + "name": "code", + "summary": "the device country code", "schema": { - "type": "null" + "$ref": "#/x-schemas/Localization/CountryCode" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": "US" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "US" } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": "UK" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "UK" } } ] }, { - "name": "Localization.setLanguage", + "name": "Localization.language", "summary": "Get the ISO 639 1/2 code for the preferred language", - "params": [ - { - "name": "value", - "summary": "the device language", - "schema": { - "$ref": "#/x-schemas/Localization/Language" - }, - "required": true - } - ], + "params": [], "tags": [ - { - "name": "setter", - "x-setter-for": "language" - }, { "name": "deprecated", "x-since": "0.17.0", "x-alternative": "Localization.locale" }, + { + "name": "property" + }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:localization:language" ] } ], "result": { - "name": "result", + "name": "lang", + "summary": "the device language", "schema": { - "type": "null" + "$ref": "#/x-schemas/Localization/Language" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": "en" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "en" } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": "es" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "es" } } ] }, { - "name": "Localization.setPreferredAudioLanguages", + "name": "Localization.preferredAudioLanguages", "summary": "A prioritized list of ISO 639 1/2 codes for the preferred audio languages on this device.", - "params": [ - { - "name": "value", - "summary": "the preferred audio languages", - "schema": { - "type": "array", - "items": { - "$ref": "#/x-schemas/Localization/ISO639_2Language" - } - }, - "required": true - } - ], + "params": [], "tags": [ { - "name": "setter", - "x-setter-for": "preferredAudioLanguages" + "name": "property" }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:localization:language" ] } ], "result": { - "name": "result", + "name": "languages", + "summary": "the preferred audio languages", "schema": { - "type": "null" + "type": "array", + "items": { + "$ref": "#/x-schemas/Localization/ISO639_2Language" + } } }, "examples": [ { "name": "Default Example", - "params": [ - { - "name": "value", - "value": [ - "spa", - "eng" - ] - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": [ + "spa", + "eng" + ] } }, { "name": "Default Example #2", - "params": [ - { - "name": "value", - "value": [ - "eng", - "spa" - ] - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": [ + "eng", + "spa" + ] } } ] }, { - "name": "Localization.setLocale", + "name": "Localization.locale", "tags": [ { - "name": "setter", - "x-setter-for": "locale" + "name": "property" }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:localization:locale" ] } ], "summary": "Get the *full* BCP 47 code, including script, region, variant, etc., for the preferred langauage/locale", - "params": [ - { - "name": "value", - "summary": "the device locale", - "schema": { - "$ref": "#/x-schemas/Localization/Locale" - }, - "required": true - } - ], + "params": [], "result": { - "name": "result", + "name": "locale", + "summary": "the device locale", "schema": { - "type": "null" + "$ref": "#/x-schemas/Localization/Locale" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": "en-US" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "en-US" } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": "es-US" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": "es-US" } } ] }, { - "name": "Localization.setTimeZone", + "name": "Localization.latlon", "tags": [ - { - "name": "setter", - "x-setter-for": "timeZone" - }, { "name": "capabilities", - "x-manages": [ - "xrn:firebolt:capability:localization:time-zone" + "x-uses": [ + "xrn:firebolt:capability:localization:location" ] } ], - "summary": "Set the IANA timezone for the device", - "params": [ - { - "name": "value", - "schema": { - "$ref": "#/x-schemas/Localization/TimeZone" - }, - "required": true - } - ], + "summary": "Get the approximate latitude and longitude coordinates of the device location", + "params": [], "result": { - "name": "result", + "name": "latlong", + "summary": "lat/long tuple", "schema": { - "type": "null" + "$ref": "#/components/schemas/LatLon" } }, "examples": [ { "name": "Default Example", - "params": [ - { - "name": "value", - "value": "America/New_York" - } - ], - "result": { - "name": "Default Result", - "value": null - } - }, - { - "name": "Additional Example", - "params": [ - { - "name": "value", - "value": "America/Los_Angeles" - } - ], + "params": [], "result": { "name": "Default Result", - "value": null + "value": [ + 39.9549, + 75.1699 + ] } } ] }, { - "name": "Metrics.ready", + "name": "Localization.additionalInfo", "tags": [ - { - "name": "rpc-only" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:general" + "xrn:firebolt:capability:localization:additional-info" ] } ], - "summary": "Inform the platform that your app is minimally usable. This method is called automatically by `Lifecycle.ready()`", + "summary": "Get any platform-specific localization information, in an Map", "params": [], "result": { - "name": "success", + "name": "info", + "summary": "the additional info", "schema": { - "type": "boolean" + "type": "object", + "additionalProperties": { + "type": "string", + "maxLength": 1024 + }, + "maxProperties": 32 } }, "examples": [ { - "name": "Send ready metric", + "name": "Default Example", "params": [], "result": { - "name": "success", - "value": true + "name": "info", + "value": {} } } ] }, { - "name": "Metrics.signIn", + "name": "Localization.addAdditionalInfo", "tags": [ - { - "name": "rpc-only" - }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:general" + "x-manages": [ + "xrn:firebolt:capability:localization:additional-info" ] } ], - "summary": "Log a sign in event, called by Discovery.signIn().", - "params": [], - "result": { - "name": "success", - "schema": { - "type": "boolean" - } - }, - "examples": [ - { - "name": "Send signIn metric", - "params": [], - "result": { - "name": "success", - "value": true - } - }, - { - "name": "Send signIn metric with entitlements", - "params": [ - { - "name": "entitlements", - "value": [ - { - "entitlementId": "123", - "startTime": "2025-01-01T00:00:00.000Z", - "endTime": "2025-01-01T00:00:00.000Z" - } - ] - } - ], - "result": { - "name": "success", - "value": true - } - } - ] - }, - { - "name": "Metrics.signOut", - "tags": [ + "summary": "Add any platform-specific localization information in key/value pair", + "params": [ { - "name": "rpc-only" + "name": "key", + "summary": "Key to add additionalInfo", + "schema": { + "type": "string" + }, + "required": true }, { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:general" - ] + "name": "value", + "summary": "Value to be set for additionalInfo", + "schema": { + "type": "string" + }, + "required": true } ], - "summary": "Log a sign out event, called by Discovery.signOut().", - "params": [], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "const": null } }, "examples": [ { - "name": "Send signOut metric", - "params": [], + "name": "Add an additionalInfo for localization", + "params": [ + { + "name": "key", + "value": "defaultKey" + }, + { + "name": "value", + "value": "defaultValue=" + } + ], "result": { - "name": "success", - "value": true + "name": "defaultResult", + "value": null } } ] }, { - "name": "Metrics.startContent", + "name": "Localization.removeAdditionalInfo", "tags": [ { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:general" + "x-manages": [ + "xrn:firebolt:capability:localization:additional-info" ] } ], - "summary": "Inform the platform that your user has started content.", + "summary": "Remove any platform-specific localization information from map", "params": [ { - "name": "entityId", - "summary": "Optional entity ID of the content.", + "name": "key", + "summary": "Key to remove additionalInfo", "schema": { "type": "string" }, - "required": false + "required": true } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "const": null } }, "examples": [ { - "name": "Send startContent metric", - "params": [], - "result": { - "name": "success", - "value": true - } - }, - { - "name": "Send startContent metric w/ entity", + "name": "Remove an additionalInfo for localization", "params": [ { - "name": "entityId", - "value": "abc" + "name": "key", + "value": "defaultKey" } ], "result": { - "name": "success", - "value": true + "name": "defaultResult", + "value": null } } ] }, { - "name": "Metrics.stopContent", + "name": "Localization.timeZone", "tags": [ + { + "name": "property" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:general" + "xrn:firebolt:capability:localization:time-zone" ] } ], - "summary": "Inform the platform that your user has stopped content.", - "params": [ - { - "name": "entityId", - "summary": "Optional entity ID of the content.", - "schema": { - "type": "string" - }, - "required": false - } - ], + "summary": "Set the IANA timezone for the device", + "params": [], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "$ref": "#/x-schemas/Localization/TimeZone" } }, "examples": [ { - "name": "Send stopContent metric", + "name": "Default Example", "params": [], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "America/New_York" } }, { - "name": "Send stopContent metric w/ entity", - "params": [ - { - "name": "entityId", - "value": "abc" - } - ], + "name": "Additional Example", + "params": [], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "America/Los_Angeles" } } ] }, { - "name": "Metrics.page", + "name": "Localization.onLocalityChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Localization.locality" + }, + { + "name": "event", + "x-alternative": "locality" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:general" + "xrn:firebolt:capability:localization:locality" ] } ], - "summary": "Inform the platform that your user has navigated to a page or view.", + "summary": "Get the locality/city the device is located in", "params": [ { - "name": "pageId", - "summary": "Page ID of the content.", + "name": "listen", + "required": true, "schema": { - "type": "string" - }, - "required": true + "type": "boolean" + } } ], "result": { - "name": "success", + "name": "locality", + "summary": "the device city", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/Localization/Locality" + } + ] } }, "examples": [ { - "name": "Send page metric", + "name": "Default example #1", "params": [ { - "name": "pageId", - "value": "xyz" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "Philadelphia" } }, { - "name": "Send startContent metric w/ entity", + "name": "Default example #2", "params": [ { - "name": "pageId", - "value": "home" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "Rockville" } } ] }, { - "name": "Metrics.action", + "name": "Localization.onPostalCodeChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Localization.postalCode" + }, + { + "name": "event", + "x-alternative": "postalCode" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:general" + "xrn:firebolt:capability:localization:postal-code" ] } ], - "summary": "Inform the platform of something not covered by other Metrics APIs.", + "summary": "Get the postal code the device is located in", "params": [ { - "name": "category", - "summary": "The category of action being logged. Must be 'user' for user-initated actions or 'app' for all other actions", - "schema": { - "type": "string", - "enum": [ - "user", - "app" - ] - }, - "required": true - }, - { - "name": "type", - "summary": "A short, indexible identifier for the action, e.g. 'SignIn Prompt Displayed'", - "schema": { - "type": "string", - "maxLength": 256 - }, - "required": true - }, - { - "name": "parameters", + "name": "listen", + "required": true, "schema": { - "$ref": "#/x-schemas/Types/FlatMap" - }, - "required": false + "type": "boolean" + } } ], "result": { - "name": "success", + "name": "postalCode", + "summary": "the device postal code", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "string" + } + ] } }, "examples": [ { - "name": "Send foo action", + "name": "Default example #1", "params": [ { - "name": "category", - "value": "user" - }, + "name": "listen", + "value": true + } + ], + "result": { + "name": "Default Result", + "value": "19103" + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "type", - "value": "The user did foo" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "20850" } } ] }, { - "name": "Metrics.error", + "name": "Localization.onCountryCodeChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Localization.countryCode" + }, + { + "name": "event", + "x-alternative": "countryCode" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:general" + "xrn:firebolt:capability:localization:country-code" ] } ], - "summary": "Inform the platform of an error that has occured in your app.", + "summary": "Get the ISO 3166-1 alpha-2 code for the country device is located in", "params": [ { - "name": "type", - "summary": "The type of error", - "schema": { - "$ref": "#/components/schemas/ErrorType" - }, - "required": true - }, - { - "name": "code", - "summary": "an app-specific error code", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "description", - "summary": "A short description of the error", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "visible", - "summary": "Whether or not this error was visible to the user.", + "name": "listen", + "required": true, "schema": { "type": "boolean" - }, - "required": true - }, - { - "name": "parameters", - "summary": "Optional additional parameters to be logged with the error", - "schema": { - "$ref": "#/x-schemas/Types/FlatMap" - }, - "required": false + } } ], "result": { - "name": "success", + "name": "code", + "summary": "the device country code", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/Localization/CountryCode" + } + ] } }, "examples": [ { - "name": "Send error metric", + "name": "Default example #1", "params": [ { - "name": "type", - "value": "media" - }, - { - "name": "code", - "value": "MEDIA-STALLED" - }, - { - "name": "description", - "value": "playback stalled" - }, - { - "name": "visible", + "name": "listen", "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "US" } - } - ] - }, - { - "name": "Metrics.mediaLoadStart", - "tags": [ - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:media" - ] - } - ], - "summary": "Called when setting the URL of a media asset to play, in order to infer load time.", - "params": [ - { - "name": "entityId", - "summary": "The entityId of the media.", - "schema": { - "type": "string" - }, - "required": true - } - ], - "result": { - "name": "success", - "schema": { - "type": "boolean" - } - }, - "examples": [ + }, { - "name": "Send loadstart metric.", + "name": "Default example #2", "params": [ { - "name": "entityId", - "value": "345" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "UK" } } ] }, { - "name": "Metrics.mediaPlay", + "name": "Localization.onLanguageChanged", + "summary": "Get the ISO 639 1/2 code for the preferred language", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Localization.language" + }, + { + "name": "event", + "x-alternative": "language" + }, + { + "name": "deprecated", + "x-since": "0.17.0", + "x-alternative": "Localization.locale" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "xrn:firebolt:capability:localization:language" ] } ], - "summary": "Called when media playback should start due to autoplay, user-initiated play, or unpausing.", - "params": [ - { - "name": "entityId", - "summary": "The entityId of the media.", - "schema": { - "type": "string" - }, - "required": true - } - ], "result": { - "name": "success", + "name": "lang", + "summary": "the device language", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/Localization/Language" + } + ] } }, "examples": [ { - "name": "Send play metric.", + "name": "Default example #1", "params": [ { - "name": "entityId", - "value": "345" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "en" + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "Default Result", + "value": "es" } } ] }, { - "name": "Metrics.mediaPlaying", + "name": "Localization.onPreferredAudioLanguagesChanged", + "summary": "A prioritized list of ISO 639 1/2 codes for the preferred audio languages on this device.", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Localization.preferredAudioLanguages" + }, + { + "name": "event", + "x-alternative": "preferredAudioLanguages" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "xrn:firebolt:capability:localization:language" ] } ], - "summary": "Called when media playback actually starts due to autoplay, user-initiated play, unpausing, or recovering from a buffering interuption.", - "params": [ - { - "name": "entityId", - "summary": "The entityId of the media.", - "schema": { - "type": "string" - }, - "required": true - } - ], "result": { - "name": "success", + "name": "languages", + "summary": "the preferred audio languages", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "array", + "items": { + "$ref": "#/x-schemas/Localization/ISO639_2Language" + } + } + ] } }, "examples": [ { - "name": "Send playing metric.", + "name": "Default Example", "params": [ { - "name": "entityId", - "value": "345" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": [ + "spa", + "eng" + ] + } + }, + { + "name": "Default Example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "Default Result", + "value": [ + "eng", + "spa" + ] } } ] }, { - "name": "Metrics.mediaPause", + "name": "Localization.onLocaleChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Localization.locale" + }, + { + "name": "event", + "x-alternative": "locale" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "xrn:firebolt:capability:localization:locale" ] } ], - "summary": "Called when media playback will pause due to an intentional pause operation.", + "summary": "Get the *full* BCP 47 code, including script, region, variant, etc., for the preferred langauage/locale", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", + "name": "listen", + "required": true, "schema": { - "type": "string" - }, - "required": true + "type": "boolean" + } } ], "result": { - "name": "success", + "name": "locale", + "summary": "the device locale", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/Localization/Locale" + } + ] } }, "examples": [ { - "name": "Send pause metric.", + "name": "Default example #1", "params": [ { - "name": "entityId", - "value": "345" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "en-US" + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "Default Result", + "value": "es-US" } } ] }, { - "name": "Metrics.mediaWaiting", + "name": "Localization.onTimeZoneChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Localization.timeZone" + }, + { + "name": "event", + "x-alternative": "timeZone" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "xrn:firebolt:capability:localization:time-zone" ] } ], - "summary": "Called when media playback will halt due to a network, buffer, or other unintentional constraint.", + "summary": "Set the IANA timezone for the device", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", + "name": "listen", + "required": true, "schema": { - "type": "string" - }, - "required": true + "type": "boolean" + } } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/Localization/TimeZone" + } + ] } }, "examples": [ { - "name": "Send waiting metric.", + "name": "Default Example", "params": [ { - "name": "entityId", - "value": "345" + "name": "listen", + "value": true } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": "America/New_York" + } + }, + { + "name": "Additional Example", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "Default Result", + "value": "America/Los_Angeles" } } ] }, { - "name": "Metrics.mediaProgress", + "name": "Localization.setLocality", "tags": [ + { + "name": "setter", + "x-setter-for": "locality" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "x-manages": [ + "xrn:firebolt:capability:localization:locality" ] } ], - "summary": "Called every 60 seconds as media playback progresses.", + "summary": "Get the locality/city the device is located in", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "progress", - "summary": "Progress of playback, as a decimal percentage (0-0.999) for content with a known duration, or an integer number of seconds (0-86400) for content with an unknown duration.", + "name": "value", + "summary": "the device city", "schema": { - "$ref": "#/components/schemas/MediaPosition" + "$ref": "#/x-schemas/Localization/Locality" }, "required": true } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "type": "null" } }, "examples": [ { - "name": "Send progress metric.", + "name": "Default example #1", "params": [ { - "name": "entityId", - "value": "345" - }, + "name": "value", + "value": "Philadelphia" + } + ], + "result": { + "name": "Default Result", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "progress", - "value": 0.75 + "name": "value", + "value": "Rockville" } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": null } } ] }, { - "name": "Metrics.mediaSeeking", + "name": "Localization.setPostalCode", "tags": [ + { + "name": "setter", + "x-setter-for": "postalCode" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "x-manages": [ + "xrn:firebolt:capability:localization:postal-code" ] } ], - "summary": "Called when a seek is initiated during media playback.", + "summary": "Get the postal code the device is located in", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", + "name": "value", + "summary": "the device postal code", "schema": { "type": "string" }, "required": true - }, - { - "name": "target", - "summary": "Target destination of the seek, as a decimal percentage (0-0.999) for content with a known duration, or an integer number of seconds (0-86400) for content with an unknown duration.", - "schema": { - "$ref": "#/components/schemas/MediaPosition" - }, - "required": true } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "type": "null" } }, "examples": [ { - "name": "Send seeking metric.", + "name": "Default example #1", "params": [ { - "name": "entityId", - "value": "345" - }, + "name": "value", + "value": "19103" + } + ], + "result": { + "name": "Default Result", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "target", - "value": 0.5 + "name": "value", + "value": "20850" } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": null } } ] }, { - "name": "Metrics.mediaSeeked", + "name": "Localization.setCountryCode", "tags": [ + { + "name": "setter", + "x-setter-for": "countryCode" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "x-manages": [ + "xrn:firebolt:capability:localization:country-code" ] } ], - "summary": "Called when a seek is completed during media playback.", + "summary": "Get the ISO 3166-1 alpha-2 code for the country device is located in", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "position", - "summary": "Resulting position of the seek operation, as a decimal percentage (0-0.999) for content with a known duration, or an integer number of seconds (0-86400) for content with an unknown duration.", + "name": "value", + "summary": "the device country code", "schema": { - "$ref": "#/components/schemas/MediaPosition" + "$ref": "#/x-schemas/Localization/CountryCode" }, "required": true } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "type": "null" } }, "examples": [ { - "name": "Send seeked metric.", + "name": "Default example #1", "params": [ { - "name": "entityId", - "value": "345" - }, + "name": "value", + "value": "US" + } + ], + "result": { + "name": "Default Result", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "position", - "value": 0.51 + "name": "value", + "value": "UK" } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": null } } ] }, { - "name": "Metrics.mediaRateChange", - "tags": [ - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:media" - ] - } - ], - "summary": "Called when the playback rate of media is changed.", + "name": "Localization.setLanguage", + "summary": "Get the ISO 639 1/2 code for the preferred language", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", + "name": "value", + "summary": "the device language", "schema": { - "type": "string" + "$ref": "#/x-schemas/Localization/Language" }, "required": true + } + ], + "tags": [ + { + "name": "setter", + "x-setter-for": "language" }, { - "name": "rate", - "summary": "The new playback rate.", - "schema": { - "type": "number" - }, - "required": true + "name": "deprecated", + "x-since": "0.17.0", + "x-alternative": "Localization.locale" + }, + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:localization:language" + ] } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "type": "null" } }, "examples": [ { - "name": "Send ratechange metric.", + "name": "Default example #1", "params": [ { - "name": "entityId", - "value": "345" - }, - { - "name": "rate", - "value": 2 - } + "name": "value", + "value": "en" + } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "value", + "value": "es" + } + ], + "result": { + "name": "Default Result", + "value": null } } ] }, { - "name": "Metrics.mediaRenditionChange", - "tags": [ - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:media" - ] - } - ], - "summary": "Called when the playback rendition (e.g. bitrate, dimensions, profile, etc) is changed.", + "name": "Localization.setPreferredAudioLanguages", + "summary": "A prioritized list of ISO 639 1/2 codes for the preferred audio languages on this device.", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "bitrate", - "summary": "The new bitrate in kbps.", - "schema": { - "type": "number" - }, - "required": true - }, - { - "name": "width", - "summary": "The new resolution width.", + "name": "value", + "summary": "the preferred audio languages", "schema": { - "type": "number" + "type": "array", + "items": { + "$ref": "#/x-schemas/Localization/ISO639_2Language" + } }, "required": true - }, + } + ], + "tags": [ { - "name": "height", - "summary": "The new resolution height.", - "schema": { - "type": "number" - }, - "required": true + "name": "setter", + "x-setter-for": "preferredAudioLanguages" }, { - "name": "profile", - "summary": "A description of the new profile, e.g. 'HDR' etc.", - "schema": { - "type": "string" - }, - "required": false + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:localization:language" + ] } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "type": "null" } }, "examples": [ { - "name": "Send renditionchange metric.", + "name": "Default Example", "params": [ { - "name": "entityId", - "value": "345" - }, - { - "name": "bitrate", - "value": 5000 - }, - { - "name": "width", - "value": 1920 - }, - { - "name": "height", - "value": 1080 - }, + "name": "value", + "value": [ + "spa", + "eng" + ] + } + ], + "result": { + "name": "Default Result", + "value": null + } + }, + { + "name": "Default Example #2", + "params": [ { - "name": "profile", - "value": "HDR+" + "name": "value", + "value": [ + "eng", + "spa" + ] } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": null } } ] }, { - "name": "Metrics.mediaEnded", + "name": "Localization.setLocale", "tags": [ + { + "name": "setter", + "x-setter-for": "locale" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:media" + "x-manages": [ + "xrn:firebolt:capability:localization:locale" ] } ], - "summary": "Called when playback has stopped because the end of the media was reached.", + "summary": "Get the *full* BCP 47 code, including script, region, variant, etc., for the preferred langauage/locale", "params": [ { - "name": "entityId", - "summary": "The entityId of the media.", + "name": "value", + "summary": "the device locale", "schema": { - "type": "string" + "$ref": "#/x-schemas/Localization/Locale" }, "required": true } ], "result": { - "name": "success", + "name": "result", "schema": { - "type": "boolean" + "type": "null" } }, "examples": [ { - "name": "Send ended metric.", + "name": "Default example #1", "params": [ { - "name": "entityId", - "value": "345" + "name": "value", + "value": "en-US" } ], "result": { - "name": "success", - "value": true + "name": "Default Result", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "value", + "value": "es-US" + } + ], + "result": { + "name": "Default Result", + "value": null } } ] }, { - "name": "Metrics.event", + "name": "Localization.setTimeZone", "tags": [ + { + "name": "setter", + "x-setter-for": "timeZone" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:metrics:distributor" + "x-manages": [ + "xrn:firebolt:capability:localization:time-zone" ] } ], - "summary": "Inform the platform of 1st party distributor metrics.", + "summary": "Set the IANA timezone for the device", "params": [ { - "name": "schema", - "summary": "The schema URI of the metric type", - "schema": { - "type": "string", - "format": "uri" - }, - "required": true - }, - { - "name": "data", - "summary": "A JSON payload conforming the the provided schema", + "name": "value", "schema": { - "$ref": "#/components/schemas/EventObject" + "$ref": "#/x-schemas/Localization/TimeZone" }, "required": true } ], "result": { - "name": "results", + "name": "result", "schema": { "type": "null" } }, "examples": [ { - "name": "Send foo event", + "name": "Default Example", "params": [ { - "name": "schema", - "value": "http://meta.rdkcentral.com/some/schema" - }, + "name": "value", + "value": "America/New_York" + } + ], + "result": { + "name": "Default Result", + "value": null + } + }, + { + "name": "Additional Example", + "params": [ { - "name": "data", - "value": { - "foo": "foo" - } + "name": "value", + "value": "America/Los_Angeles" } ], "result": { - "name": "result", + "name": "Default Result", "value": null } } ] }, { - "name": "Parameters.initialization", + "name": "Metrics.ready", "tags": [ + { + "name": "rpc-only" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:lifecycle:state" + "xrn:firebolt:capability:metrics:general" ] } ], - "summary": "Returns any initialization parameters for the app, e.g. initialial `NavigationIntent`.", + "summary": "Inform the platform that your app is minimally usable. This method is called automatically by `Lifecycle.ready()`", "params": [], "result": { - "name": "init", - "summary": "The initialization parameters.", + "name": "success", "schema": { - "$ref": "#/components/schemas/AppInitialization" + "type": "boolean" } }, "examples": [ { - "name": "Default Example", + "name": "Send ready metric", "params": [], "result": { - "name": "init", - "value": { - "lmt": 0, - "us_privacy": "1-Y-", - "discovery": { - "navigateTo": { - "action": "entity", - "data": { - "entityId": "abc", - "entityType": "program", - "programType": "movie" - }, - "context": { - "source": "voice" - } - } - } - } + "name": "success", + "value": true } } ] }, { - "name": "PinChallenge.onRequestChallenge", - "summary": "Registers as a provider for when the user should be challenged in order to confirm access to a capability through a pin prompt", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "name": "Metrics.signIn", "tags": [ { "name": "rpc-only" }, - { - "name": "event", - "x-response": { - "$ref": "#/components/schemas/PinChallengeResult", - "examples": [ - { - "granted": true, - "reason": "correctPin" - }, - { - "granted": false, - "reason": "exceededPinFailures" - }, - { - "granted": null, - "reason": "cancelled" - } - ] - }, - "x-error": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - }, { "name": "capabilities", - "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", - "x-allow-focus": true + "x-uses": [ + "xrn:firebolt:capability:metrics:general" + ] } ], + "summary": "Log a sign in event, called by Discovery.signIn().", + "params": [], "result": { - "name": "challenge", - "summary": "The request to challenge the user", + "name": "success", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "$ref": "#/components/schemas/PinChallengeProviderRequest" - } - ] + "type": "boolean" } }, "examples": [ { - "name": "Default Example", + "name": "Send signIn metric", + "params": [], + "result": { + "name": "success", + "value": true + } + }, + { + "name": "Send signIn metric with entitlements", "params": [ { - "name": "listen", - "value": true + "name": "entitlements", + "value": [ + { + "entitlementId": "123", + "startTime": "2025-01-01T00:00:00.000Z", + "endTime": "2025-01-01T00:00:00.000Z" + } + ] } ], "result": { - "name": "Default Result", - "value": { - "correlationId": "abc", - "parameters": { - "capability": "xrn:firebolt:capability:commerce::purchase", - "requestor": { - "id": "ReferenceApp", - "name": "Firebolt Reference App" - }, - "pinSpace": "purchase" - } - } + "name": "success", + "value": true } } ] }, { - "name": "PinChallenge.challengeFocus", - "summary": "Internal API for Challenge Provider to request focus for UX purposes.", - "params": [], + "name": "Metrics.signOut", "tags": [ { "name": "rpc-only" }, { "name": "capabilities", - "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", - "x-allow-focus": true, - "x-allow-focus-for": "onRequestChallenge" + "x-uses": [ + "xrn:firebolt:capability:metrics:general" + ] } ], + "summary": "Log a sign out event, called by Discovery.signOut().", + "params": [], "result": { - "name": "result", + "name": "success", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { - "name": "Example", + "name": "Send signOut metric", "params": [], "result": { - "name": "result", - "value": null + "name": "success", + "value": true } } ] }, { - "name": "PinChallenge.challengeResponse", - "summary": "Internal API for Challenge Provider to send back response.", - "params": [ + "name": "Metrics.startContent", + "tags": [ { - "name": "response", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/PinChallengeResult", - "examples": [ - { - "granted": true, - "reason": "correctPin" - }, - { - "granted": false, - "reason": "exceededPinFailures" - }, - { - "granted": null, - "reason": "cancelled" - } - ] - } - } - } - ] - } + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:metrics:general" + ] } ], - "tags": [ - { - "name": "rpc-only" - }, + "summary": "Inform the platform that your user has started content.", + "params": [ { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", - "x-allow-focus": true, - "x-response-for": "onRequestChallenge" + "name": "entityId", + "summary": "Optional entity ID of the content.", + "schema": { + "type": "string" + }, + "required": false } ], "result": { - "name": "result", + "name": "success", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { - "name": "Example #1", - "params": [ - { - "name": "response", - "value": { - "correlationId": "123", - "result": { - "granted": true, - "reason": "correctPin" - } - } - } - ], - "result": { - "name": "result", - "value": null - } - }, - { - "name": "Example #2", - "params": [ - { - "name": "response", - "value": { - "correlationId": "123", - "result": { - "granted": false, - "reason": "exceededPinFailures" - } - } - } - ], + "name": "Send startContent metric", + "params": [], "result": { - "name": "result", - "value": null + "name": "success", + "value": true } }, { - "name": "Example #3", + "name": "Send startContent metric w/ entity", "params": [ { - "name": "response", - "value": { - "correlationId": "123", - "result": { - "granted": null, - "reason": "cancelled" - } - } + "name": "entityId", + "value": "abc" } ], "result": { - "name": "result", - "value": null + "name": "success", + "value": true } } ] }, { - "name": "PinChallenge.challengeError", - "summary": "Internal API for Challenge Provider to send back error.", - "params": [ + "name": "Metrics.stopContent", + "tags": [ { - "name": "error", - "required": true, - "schema": { - "allOf": [ - { - "$ref": "#/x-schemas/Types/ProviderResponse" - }, - { - "type": "object", - "properties": { - "result": { - "type": "object", - "additionalProperties": false, - "required": [ - "code", - "message" - ], - "properties": { - "code": { - "title": "errorObjectCode", - "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", - "type": "integer" - }, - "message": { - "title": "errorObjectMessage", - "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", - "type": "string" - }, - "data": { - "title": "errorObjectData", - "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." - } - } - } - } - } - ] - } + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:metrics:general" + ] } ], - "tags": [ + "summary": "Inform the platform that your user has stopped content.", + "params": [ { - "name": "rpc-only" - }, - { - "name": "capabilities", - "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", - "x-allow-focus": true, - "x-error-for": "onRequestChallenge" + "name": "entityId", + "summary": "Optional entity ID of the content.", + "schema": { + "type": "string" + }, + "required": false } ], "result": { - "name": "result", + "name": "success", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { - "name": "Example 1", + "name": "Send stopContent metric", + "params": [], + "result": { + "name": "success", + "value": true + } + }, + { + "name": "Send stopContent metric w/ entity", "params": [ { - "name": "error", - "value": { - "correlationId": "123", - "result": { - "code": 1, - "message": "Error" - } - } + "name": "entityId", + "value": "abc" } ], "result": { - "name": "result", - "value": null + "name": "success", + "value": true } } ] }, { - "name": "Privacy.allowResumePoints", + "name": "Metrics.page", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:general" ] } ], - "summary": "Whether the user allows resume points for content to show in the main experience", - "params": [], + "summary": "Inform the platform that your user has navigated to a page or view.", + "params": [ + { + "name": "pageId", + "summary": "Page ID of the content.", + "schema": { + "type": "string" + }, + "required": true + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send page metric", + "params": [ + { + "name": "pageId", + "value": "xyz" + } + ], "result": { - "name": "allow", + "name": "success", "value": true } }, { - "name": "Default example #2", - "params": [], + "name": "Send startContent metric w/ entity", + "params": [ + { + "name": "pageId", + "value": "home" + } + ], "result": { - "name": "allow", - "value": false + "name": "success", + "value": true } } ] }, { - "name": "Privacy.allowUnentitledResumePoints", + "name": "Metrics.action", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:general" ] } ], - "summary": "Whether the user allows resume points for content from unentitled providers to show in the main experience", - "params": [], + "summary": "Inform the platform of something not covered by other Metrics APIs.", + "params": [ + { + "name": "category", + "summary": "The category of action being logged. Must be 'user' for user-initated actions or 'app' for all other actions", + "schema": { + "type": "string", + "enum": [ + "user", + "app" + ] + }, + "required": true + }, + { + "name": "type", + "summary": "A short, indexible identifier for the action, e.g. 'SignIn Prompt Displayed'", + "schema": { + "type": "string", + "maxLength": 256 + }, + "required": true + }, + { + "name": "parameters", + "schema": { + "$ref": "#/x-schemas/Types/FlatMap" + }, + "required": false + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send foo action", + "params": [ + { + "name": "category", + "value": "user" + }, + { + "name": "type", + "value": "The user did foo" + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowWatchHistory", + "name": "Metrics.error", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:general" ] } ], - "summary": "Whether the user allows their watch history from all sources to show in the main experience", - "params": [], + "summary": "Inform the platform of an error that has occured in your app.", + "params": [ + { + "name": "type", + "summary": "The type of error", + "schema": { + "$ref": "#/components/schemas/ErrorType" + }, + "required": true + }, + { + "name": "code", + "summary": "an app-specific error code", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "description", + "summary": "A short description of the error", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "visible", + "summary": "Whether or not this error was visible to the user.", + "schema": { + "type": "boolean" + }, + "required": true + }, + { + "name": "parameters", + "summary": "Optional additional parameters to be logged with the error", + "schema": { + "$ref": "#/x-schemas/Types/FlatMap" + }, + "required": false + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send error metric", + "params": [ + { + "name": "type", + "value": "media" + }, + { + "name": "code", + "value": "MEDIA-STALLED" + }, + { + "name": "description", + "value": "playback stalled" + }, + { + "name": "visible", + "value": true + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowProductAnalytics", + "name": "Metrics.mediaLoadStart", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows their usage data can be used for analytics about the product", - "params": [], - "result": { - "name": "allow", - "schema": { - "type": "boolean" - } - }, - "examples": [ + "summary": "Called when setting the URL of a media asset to play, in order to infer load time.", + "params": [ { - "name": "Default example #1", - "params": [], - "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } - } - ] - }, - { - "name": "Privacy.allowPersonalization", - "tags": [ - { - "name": "property", - "x-allow-value": true - }, - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:privacy:settings" - ] + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true } ], - "summary": "Whether the user allows their usage data to be used for personalization and recommendations", - "params": [], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send loadstart metric.", + "params": [ + { + "name": "entityId", + "value": "345" + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowUnentitledPersonalization", + "name": "Metrics.mediaPlay", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows their usage data to be used for personalization and recommendations for unentitled content", - "params": [], + "summary": "Called when media playback should start due to autoplay, user-initiated play, or unpausing.", + "params": [ + { + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send play metric.", + "params": [ + { + "name": "entityId", + "value": "345" + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowRemoteDiagnostics", + "name": "Metrics.mediaPlaying", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows their personal data to be included in diagnostic telemetry. This also allows whether device logs can be remotely accessed from the client device", - "params": [], + "summary": "Called when media playback actually starts due to autoplay, user-initiated play, unpausing, or recovering from a buffering interuption.", + "params": [ + { + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send playing metric.", + "params": [ + { + "name": "entityId", + "value": "345" + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowPrimaryContentAdTargeting", + "name": "Metrics.mediaPause", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows ads to be targeted to the user while watching content in the primary experience", - "params": [], + "summary": "Called when media playback will pause due to an intentional pause operation.", + "params": [ + { + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send pause metric.", + "params": [ + { + "name": "entityId", + "value": "345" + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowPrimaryBrowseAdTargeting", + "name": "Metrics.mediaWaiting", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows ads to be targeted to the user while browsing in the primary experience", - "params": [], + "summary": "Called when media playback will halt due to a network, buffer, or other unintentional constraint.", + "params": [ + { + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send waiting metric.", + "params": [ + { + "name": "entityId", + "value": "345" + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowAppContentAdTargeting", + "name": "Metrics.mediaProgress", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows ads to be targeted to the user while watching content in apps", - "params": [], + "summary": "Called every 60 seconds as media playback progresses.", + "params": [ + { + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "progress", + "summary": "Progress of playback, as a decimal percentage (0-0.999) for content with a known duration, or an integer number of seconds (0-86400) for content with an unknown duration.", + "schema": { + "$ref": "#/components/schemas/MediaPosition" + }, + "required": true + } + ], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send progress metric.", + "params": [ + { + "name": "entityId", + "value": "345" + }, + { + "name": "progress", + "value": 0.75 + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.allowACRCollection", + "name": "Metrics.mediaSeeking", "tags": [ - { - "name": "property", - "x-allow-value": true - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows their automatic content recognition data to be collected", - "params": [], - "result": { - "name": "allow", - "schema": { - "type": "boolean" - } - }, - "examples": [ - { - "name": "Default example #1", - "params": [], - "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } - } - ] - }, - { - "name": "Privacy.allowCameraAnalytics", - "tags": [ + "summary": "Called when a seek is initiated during media playback.", + "params": [ { - "name": "property", - "x-allow-value": true + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true }, { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:privacy:settings" - ] + "name": "target", + "summary": "Target destination of the seek, as a decimal percentage (0-0.999) for content with a known duration, or an integer number of seconds (0-86400) for content with an unknown duration.", + "schema": { + "$ref": "#/components/schemas/MediaPosition" + }, + "required": true } ], - "summary": "Whether the user allows data from their camera to be used for Product Analytics", - "params": [], "result": { - "name": "allow", + "name": "success", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], + "name": "Send seeking metric.", + "params": [ + { + "name": "entityId", + "value": "345" + }, + { + "name": "target", + "value": 0.5 + } + ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.settings", + "name": "Metrics.mediaSeeked", "tags": [ { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Gets the allowed value for all privacy settings", - "params": [], + "summary": "Called when a seek is completed during media playback.", + "params": [ + { + "name": "entityId", + "summary": "The entityId of the media.", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "position", + "summary": "Resulting position of the seek operation, as a decimal percentage (0-0.999) for content with a known duration, or an integer number of seconds (0-86400) for content with an unknown duration.", + "schema": { + "$ref": "#/components/schemas/MediaPosition" + }, + "required": true + } + ], "result": { - "name": "settings", + "name": "success", "schema": { - "$ref": "#/components/schemas/PrivacySettings" + "type": "boolean" } }, "examples": [ { - "name": "Default Example", - "params": [], - "result": { - "name": "settings", - "value": { - "allowACRCollection": true, - "allowResumePoints": false, - "allowAppContentAdTargeting": false, - "allowCameraAnalytics": true, - "allowPersonalization": true, - "allowPrimaryBrowseAdTargeting": false, - "allowPrimaryContentAdTargeting": false, - "allowProductAnalytics": true, - "allowRemoteDiagnostics": true, - "allowUnentitledPersonalization": true, - "allowUnentitledResumePoints": false, - "allowWatchHistory": true + "name": "Send seeked metric.", + "params": [ + { + "name": "entityId", + "value": "345" + }, + { + "name": "position", + "value": 0.51 } + ], + "result": { + "name": "success", + "value": true } } ] }, { - "name": "Privacy.onAllowResumePointsChanged", + "name": "Metrics.mediaRateChange", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "allowResumePoints" - }, - { - "name": "event", - "x-alternative": "allowResumePoints" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows resume points for content to show in the main experience", + "summary": "Called when the playback rate of media is changed.", "params": [ { - "name": "listen", - "required": true, + "name": "entityId", + "summary": "The entityId of the media.", "schema": { - "type": "boolean" - } + "type": "string" + }, + "required": true + }, + { + "name": "rate", + "summary": "The new playback rate.", + "schema": { + "type": "number" + }, + "required": true } ], "result": { - "name": "allow", + "name": "success", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "boolean" } }, "examples": [ { - "name": "Default example #1", + "name": "Send ratechange metric.", "params": [ { - "name": "listen", - "value": true - } - ], - "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [ + "name": "entityId", + "value": "345" + }, { - "name": "listen", - "value": true + "name": "rate", + "value": 2 } ], "result": { - "name": "allow", - "value": false + "name": "success", + "value": true } } ] }, { - "name": "Privacy.onAllowUnentitledResumePointsChanged", + "name": "Metrics.mediaRenditionChange", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "allowUnentitledResumePoints" - }, - { - "name": "event", - "x-alternative": "allowUnentitledResumePoints" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows resume points for content from unentitled providers to show in the main experience", + "summary": "Called when the playback rendition (e.g. bitrate, dimensions, profile, etc) is changed.", "params": [ { - "name": "listen", - "required": true, + "name": "entityId", + "summary": "The entityId of the media.", "schema": { - "type": "boolean" - } - } - ], - "result": { - "name": "allow", + "type": "string" + }, + "required": true + }, + { + "name": "bitrate", + "summary": "The new bitrate in kbps.", + "schema": { + "type": "number" + }, + "required": true + }, + { + "name": "width", + "summary": "The new resolution width.", + "schema": { + "type": "number" + }, + "required": true + }, + { + "name": "height", + "summary": "The new resolution height.", + "schema": { + "type": "number" + }, + "required": true + }, + { + "name": "profile", + "summary": "A description of the new profile, e.g. 'HDR' etc.", + "schema": { + "type": "string" + }, + "required": false + } + ], + "result": { + "name": "success", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "boolean" } }, "examples": [ { - "name": "Default example #1", + "name": "Send renditionchange metric.", "params": [ { - "name": "listen", - "value": true - } - ], - "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [ + "name": "entityId", + "value": "345" + }, { - "name": "listen", - "value": true + "name": "bitrate", + "value": 5000 + }, + { + "name": "width", + "value": 1920 + }, + { + "name": "height", + "value": 1080 + }, + { + "name": "profile", + "value": "HDR+" } ], "result": { - "name": "allow", - "value": false + "name": "success", + "value": true } } ] }, { - "name": "Privacy.onAllowWatchHistoryChanged", + "name": "Metrics.mediaEnded", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "allowWatchHistory" - }, - { - "name": "event", - "x-alternative": "allowWatchHistory" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:media" ] } ], - "summary": "Whether the user allows their watch history from all sources to show in the main experience", + "summary": "Called when playback has stopped because the end of the media was reached.", "params": [ { - "name": "listen", - "required": true, + "name": "entityId", + "summary": "The entityId of the media.", "schema": { - "type": "boolean" - } + "type": "string" + }, + "required": true } ], "result": { - "name": "allow", + "name": "success", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "boolean" } }, "examples": [ { - "name": "Default example #1", + "name": "Send ended metric.", "params": [ { - "name": "listen", - "value": true + "name": "entityId", + "value": "345" } ], "result": { - "name": "allow", + "name": "success", "value": true } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true - } - ], - "result": { - "name": "allow", - "value": false - } } ] }, { - "name": "Privacy.onAllowProductAnalyticsChanged", + "name": "Metrics.event", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "allowProductAnalytics" - }, - { - "name": "event", - "x-alternative": "allowProductAnalytics" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:metrics:distributor" ] } ], - "summary": "Whether the user allows their usage data can be used for analytics about the product", + "summary": "Inform the platform of 1st party distributor metrics.", "params": [ { - "name": "listen", - "required": true, + "name": "schema", + "summary": "The schema URI of the metric type", "schema": { - "type": "boolean" - } + "type": "string", + "format": "uri" + }, + "required": true + }, + { + "name": "data", + "summary": "A JSON payload conforming the the provided schema", + "schema": { + "$ref": "#/components/schemas/EventObject" + }, + "required": true } ], "result": { - "name": "allow", + "name": "results", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "null" } }, "examples": [ { - "name": "Default example #1", + "name": "Send foo event", "params": [ { - "name": "listen", - "value": true - } - ], - "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [ + "name": "schema", + "value": "http://meta.rdkcentral.com/some/schema" + }, { - "name": "listen", - "value": true + "name": "data", + "value": { + "foo": "foo" + } } ], "result": { - "name": "allow", - "value": false + "name": "result", + "value": null } } ] }, { - "name": "Privacy.onAllowPersonalizationChanged", + "name": "Parameters.initialization", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "allowPersonalization" - }, - { - "name": "event", - "x-alternative": "allowPersonalization" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:privacy:settings" + "xrn:firebolt:capability:lifecycle:state" ] } ], - "summary": "Whether the user allows their usage data to be used for personalization and recommendations", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "summary": "Returns any initialization parameters for the app, e.g. initialial `NavigationIntent`.", + "params": [], "result": { - "name": "allow", + "name": "init", + "summary": "The initialization parameters.", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "$ref": "#/components/schemas/AppInitialization" } }, "examples": [ { - "name": "Default example #1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "name": "Default Example", + "params": [], "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true + "name": "init", + "value": { + "lmt": 0, + "us_privacy": "1-Y-", + "discovery": { + "navigateTo": { + "action": "entity", + "data": { + "entityId": "abc", + "entityType": "program", + "programType": "movie" + }, + "context": { + "source": "voice" + } + } + } } - ], - "result": { - "name": "allow", - "value": false } } ] }, { - "name": "Privacy.onAllowUnentitledPersonalizationChanged", + "name": "PinChallenge.onRequestChallenge", + "summary": "Registers as a provider for when the user should be challenged in order to confirm access to a capability through a pin prompt", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "tags": [ { - "name": "subscriber", - "x-subscriber-for": "allowUnentitledPersonalization" + "name": "rpc-only" }, { "name": "event", - "x-alternative": "allowUnentitledPersonalization" + "x-response": { + "$ref": "#/components/schemas/PinChallengeResult", + "examples": [ + { + "granted": true, + "reason": "correctPin" + }, + { + "granted": false, + "reason": "exceededPinFailures" + }, + { + "granted": null, + "reason": "cancelled" + } + ] + }, + "x-error": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + } }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:privacy:settings" - ] - } - ], - "summary": "Whether the user allows their usage data to be used for personalization and recommendations for unentitled content", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } + "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", + "x-allow-focus": true } ], "result": { - "name": "allow", + "name": "challenge", + "summary": "The request to challenge the user", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "type": "boolean" + "$ref": "#/components/schemas/PinChallengeProviderRequest" } ] } }, "examples": [ { - "name": "Default example #1", + "name": "Default Example", "params": [ { "name": "listen", @@ -14770,175 +15049,253 @@ } ], "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true + "name": "Default Result", + "value": { + "correlationId": "abc", + "parameters": { + "capability": "xrn:firebolt:capability:commerce::purchase", + "requestor": { + "id": "ReferenceApp", + "name": "Firebolt Reference App" + }, + "pinSpace": "purchase" + } } - ], - "result": { - "name": "allow", - "value": false } } ] }, { - "name": "Privacy.onAllowRemoteDiagnosticsChanged", + "name": "PinChallenge.challengeFocus", + "summary": "Internal API for Challenge Provider to request focus for UX purposes.", + "params": [], "tags": [ { - "name": "subscriber", - "x-subscriber-for": "allowRemoteDiagnostics" - }, - { - "name": "event", - "x-alternative": "allowRemoteDiagnostics" + "name": "rpc-only" }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:privacy:settings" - ] + "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", + "x-allow-focus": true, + "x-allow-focus-for": "onRequestChallenge" } ], - "summary": "Whether the user allows their personal data to be included in diagnostic telemetry. This also allows whether device logs can be remotely accessed from the client device", + "result": { + "name": "result", + "schema": { + "type": "null" + } + }, + "examples": [ + { + "name": "Example", + "params": [], + "result": { + "name": "result", + "value": null + } + } + ] + }, + { + "name": "PinChallenge.challengeResponse", + "summary": "Internal API for Challenge Provider to send back response.", "params": [ { - "name": "listen", - "required": true, + "name": "correlationId", "schema": { - "type": "boolean" - } + "type": "string" + }, + "required": true + }, + { + "name": "result", + "schema": { + "$ref": "#/components/schemas/PinChallengeResult", + "examples": [ + { + "granted": true, + "reason": "correctPin" + }, + { + "granted": false, + "reason": "exceededPinFailures" + }, + { + "granted": null, + "reason": "cancelled" + } + ] + }, + "required": true + } + ], + "tags": [ + { + "name": "rpc-only" + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", + "x-allow-focus": true, + "x-response-for": "onRequestChallenge" } ], "result": { - "name": "allow", + "name": "result", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "null" } }, "examples": [ { - "name": "Default example #1", + "name": "Example #1", "params": [ { - "name": "listen", - "value": true + "name": "correlationId", + "value": "123" + }, + { + "name": "result", + "value": { + "granted": true, + "reason": "correctPin" + } } ], "result": { - "name": "allow", - "value": true + "name": "result", + "value": null } }, { - "name": "Default example #2", + "name": "Example #2", "params": [ { - "name": "listen", - "value": true + "name": "correlationId", + "value": "123" + }, + { + "name": "result", + "value": { + "granted": false, + "reason": "exceededPinFailures" + } } ], "result": { - "name": "allow", - "value": false + "name": "result", + "value": null } - } - ] - }, - { - "name": "Privacy.onAllowPrimaryContentAdTargetingChanged", - "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "allowPrimaryContentAdTargeting" }, { - "name": "event", - "x-alternative": "allowPrimaryContentAdTargeting" + "name": "Example #3", + "params": [ + { + "name": "correlationId", + "value": "123" + }, + { + "name": "result", + "value": { + "granted": null, + "reason": "cancelled" + } + } + ], + "result": { + "name": "result", + "value": null + } + } + ] + }, + { + "name": "PinChallenge.challengeError", + "summary": "Internal API for Challenge Provider to send back error.", + "params": [ + { + "name": "correlationId", + "schema": { + "type": "string" + }, + "required": true }, { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:privacy:settings" - ] + "name": "error", + "schema": { + "type": "object", + "additionalProperties": false, + "required": [ + "code", + "message" + ], + "properties": { + "code": { + "title": "errorObjectCode", + "description": "A Number that indicates the error type that occurred. This MUST be an integer. The error codes from and including -32768 to -32000 are reserved for pre-defined errors. These pre-defined errors SHOULD be assumed to be returned from any JSON-RPC api.", + "type": "integer" + }, + "message": { + "title": "errorObjectMessage", + "description": "A String providing a short description of the error. The message SHOULD be limited to a concise single sentence.", + "type": "string" + }, + "data": { + "title": "errorObjectData", + "description": "A Primitive or Structured value that contains additional information about the error. This may be omitted. The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.)." + } + } + }, + "required": true } ], - "summary": "Whether the user allows ads to be targeted to the user while watching content in the primary experience", - "params": [ + "tags": [ { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } + "name": "rpc-only" + }, + { + "name": "capabilities", + "x-provides": "xrn:firebolt:capability:usergrant:pinchallenge", + "x-allow-focus": true, + "x-error-for": "onRequestChallenge" } ], "result": { - "name": "allow", + "name": "result", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "null" } }, "examples": [ { - "name": "Default example #1", + "name": "Example 1", "params": [ { - "name": "listen", - "value": true - } - ], - "result": { - "name": "allow", - "value": true - } - }, - { - "name": "Default example #2", - "params": [ + "name": "correlationId", + "value": "123" + }, { - "name": "listen", - "value": true + "name": "error", + "value": { + "code": 1, + "message": "Error" + } } ], "result": { - "name": "allow", - "value": false + "name": "result", + "value": null } } ] }, { - "name": "Privacy.onAllowPrimaryBrowseAdTargetingChanged", + "name": "Privacy.allowResumePoints", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "allowPrimaryBrowseAdTargeting" - }, - { - "name": "event", - "x-alternative": "allowPrimaryBrowseAdTargeting" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", @@ -14947,38 +15304,18 @@ ] } ], - "summary": "Whether the user allows ads to be targeted to the user while browsing in the primary experience", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "summary": "Whether the user allows resume points for content to show in the main experience", + "params": [], "result": { "name": "allow", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": true @@ -14986,12 +15323,7 @@ }, { "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": false @@ -15000,15 +15332,11 @@ ] }, { - "name": "Privacy.onAllowAppContentAdTargetingChanged", + "name": "Privacy.allowUnentitledResumePoints", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "allowAppContentAdTargeting" - }, - { - "name": "event", - "x-alternative": "allowAppContentAdTargeting" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", @@ -15017,38 +15345,18 @@ ] } ], - "summary": "Whether the user allows ads to be targeted to the user while watching content in apps", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "summary": "Whether the user allows resume points for content from unentitled providers to show in the main experience", + "params": [], "result": { "name": "allow", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": true @@ -15056,12 +15364,7 @@ }, { "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": false @@ -15070,15 +15373,11 @@ ] }, { - "name": "Privacy.onAllowACRCollectionChanged", + "name": "Privacy.allowWatchHistory", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "allowACRCollection" - }, - { - "name": "event", - "x-alternative": "allowACRCollection" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", @@ -15087,38 +15386,18 @@ ] } ], - "summary": "Whether the user allows their automatic content recognition data to be collected", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "summary": "Whether the user allows their watch history from all sources to show in the main experience", + "params": [], "result": { "name": "allow", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": true @@ -15126,12 +15405,7 @@ }, { "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": false @@ -15140,15 +15414,11 @@ ] }, { - "name": "Privacy.onAllowCameraAnalyticsChanged", + "name": "Privacy.allowProductAnalytics", "tags": [ { - "name": "subscriber", - "x-subscriber-for": "allowCameraAnalytics" - }, - { - "name": "event", - "x-alternative": "allowCameraAnalytics" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", @@ -15157,38 +15427,18 @@ ] } ], - "summary": "Whether the user allows data from their camera to be used for Product Analytics", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "summary": "Whether the user allows their usage data can be used for analytics about the product", + "params": [], "result": { "name": "allow", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": true @@ -15196,12 +15446,7 @@ }, { "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true - } - ], + "params": [], "result": { "name": "allow", "value": false @@ -15210,564 +15455,414 @@ ] }, { - "name": "Privacy.setAllowResumePoints", + "name": "Privacy.allowPersonalization", "tags": [ { - "name": "setter", - "x-setter-for": "allowResumePoints" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows resume points for content to show in the main experience", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows their usage data to be used for personalization and recommendations", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowUnentitledResumePoints", + "name": "Privacy.allowUnentitledPersonalization", "tags": [ { - "name": "setter", - "x-setter-for": "allowUnentitledResumePoints" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows resume points for content from unentitled providers to show in the main experience", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows their usage data to be used for personalization and recommendations for unentitled content", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowWatchHistory", + "name": "Privacy.allowRemoteDiagnostics", "tags": [ { - "name": "setter", - "x-setter-for": "allowWatchHistory" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows their watch history from all sources to show in the main experience", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows their personal data to be included in diagnostic telemetry. This also allows whether device logs can be remotely accessed from the client device", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowProductAnalytics", + "name": "Privacy.allowPrimaryContentAdTargeting", "tags": [ { - "name": "setter", - "x-setter-for": "allowProductAnalytics" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows their usage data can be used for analytics about the product", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows ads to be targeted to the user while watching content in the primary experience", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowPersonalization", + "name": "Privacy.allowPrimaryBrowseAdTargeting", "tags": [ { - "name": "setter", - "x-setter-for": "allowPersonalization" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows their usage data to be used for personalization and recommendations", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows ads to be targeted to the user while browsing in the primary experience", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowUnentitledPersonalization", + "name": "Privacy.allowAppContentAdTargeting", "tags": [ { - "name": "setter", - "x-setter-for": "allowUnentitledPersonalization" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows their usage data to be used for personalization and recommendations for unentitled content", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows ads to be targeted to the user while watching content in apps", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowRemoteDiagnostics", + "name": "Privacy.allowACRCollection", "tags": [ { - "name": "setter", - "x-setter-for": "allowRemoteDiagnostics" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows their personal data to be included in diagnostic telemetry. This also allows whether device logs can be remotely accessed from the client device", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows their automatic content recognition data to be collected", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowPrimaryContentAdTargeting", + "name": "Privacy.allowCameraAnalytics", "tags": [ { - "name": "setter", - "x-setter-for": "allowPrimaryContentAdTargeting" + "name": "property", + "x-allow-value": true }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows ads to be targeted to the user while watching content in the primary experience", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Whether the user allows data from their camera to be used for Product Analytics", + "params": [], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "type": "boolean" } }, "examples": [ { "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false - } - ], + "params": [], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowPrimaryBrowseAdTargeting", + "name": "Privacy.settings", "tags": [ - { - "name": "setter", - "x-setter-for": "allowPrimaryBrowseAdTargeting" - }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows ads to be targeted to the user while browsing in the primary experience", - "params": [ - { - "name": "value", - "schema": { - "type": "boolean" - }, - "required": true - } - ], + "summary": "Gets the allowed value for all privacy settings", + "params": [], "result": { - "name": "result", + "name": "settings", "schema": { - "type": "null" + "$ref": "#/components/schemas/PrivacySettings" } }, "examples": [ { - "name": "Default example #1", - "params": [ - { - "name": "value", - "value": true - } - ], + "name": "Default Example", + "params": [], "result": { - "name": "allow", - "value": null - } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false + "name": "settings", + "value": { + "allowACRCollection": true, + "allowResumePoints": false, + "allowAppContentAdTargeting": false, + "allowCameraAnalytics": true, + "allowPersonalization": true, + "allowPrimaryBrowseAdTargeting": false, + "allowPrimaryContentAdTargeting": false, + "allowProductAnalytics": true, + "allowRemoteDiagnostics": true, + "allowUnentitledPersonalization": true, + "allowUnentitledResumePoints": false, + "allowWatchHistory": true } - ], - "result": { - "name": "allow", - "value": null } } ] }, { - "name": "Privacy.setAllowAppContentAdTargeting", + "name": "Privacy.onAllowResumePointsChanged", "tags": [ { - "name": "setter", - "x-setter-for": "allowAppContentAdTargeting" + "name": "subscriber", + "x-subscriber-for": "Privacy.allowResumePoints" + }, + { + "name": "event", + "x-alternative": "allowResumePoints" }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows ads to be targeted to the user while watching content in apps", + "summary": "Whether the user allows resume points for content to show in the main experience", "params": [ { - "name": "value", + "name": "listen", + "required": true, "schema": { "type": "boolean" - }, - "required": true + } } ], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ @@ -15775,58 +15870,69 @@ "name": "Default example #1", "params": [ { - "name": "value", + "name": "listen", "value": true } ], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", "params": [ { - "name": "value", - "value": false + "name": "listen", + "value": true } ], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowACRCollection", + "name": "Privacy.onAllowUnentitledResumePointsChanged", "tags": [ { - "name": "setter", - "x-setter-for": "allowACRCollection" + "name": "subscriber", + "x-subscriber-for": "Privacy.allowUnentitledResumePoints" + }, + { + "name": "event", + "x-alternative": "allowUnentitledResumePoints" }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows their automatic content recognition data to be collected", + "summary": "Whether the user allows resume points for content from unentitled providers to show in the main experience", "params": [ { - "name": "value", + "name": "listen", + "required": true, "schema": { "type": "boolean" - }, - "required": true + } } ], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ @@ -15834,58 +15940,69 @@ "name": "Default example #1", "params": [ { - "name": "value", + "name": "listen", "value": true } ], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", "params": [ { - "name": "value", - "value": false + "name": "listen", + "value": true } ], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Privacy.setAllowCameraAnalytics", + "name": "Privacy.onAllowWatchHistoryChanged", "tags": [ { - "name": "setter", - "x-setter-for": "allowCameraAnalytics" + "name": "subscriber", + "x-subscriber-for": "Privacy.allowWatchHistory" + }, + { + "name": "event", + "x-alternative": "allowWatchHistory" }, { "name": "capabilities", - "x-manages": [ + "x-uses": [ "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Whether the user allows data from their camera to be used for Product Analytics", + "summary": "Whether the user allows their watch history from all sources to show in the main experience", "params": [ { - "name": "value", + "name": "listen", + "required": true, "schema": { "type": "boolean" - }, - "required": true + } } ], "result": { - "name": "result", + "name": "allow", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ @@ -15893,53 +16010,93 @@ "name": "Default example #1", "params": [ { - "name": "value", + "name": "listen", "value": true } ], "result": { "name": "allow", - "value": null + "value": true } }, { "name": "Default example #2", "params": [ { - "name": "value", - "value": false + "name": "listen", + "value": true } ], "result": { "name": "allow", - "value": null + "value": false } } ] }, { - "name": "Profile.approveContentRating", + "name": "Privacy.onAllowProductAnalyticsChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Privacy.allowProductAnalytics" + }, + { + "name": "event", + "x-alternative": "allowProductAnalytics" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:approve:content" + "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Verifies that the current profile should have access to mature/adult content.", - "params": [], + "summary": "Whether the user allows their usage data can be used for analytics about the product", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "result": { "name": "allow", - "summary": "Whether or not to allow access", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ { - "name": "Default Example", - "params": [], + "name": "Default example #1", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "allow", + "value": true + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], "result": { "name": "allow", "value": false @@ -15948,28 +16105,68 @@ ] }, { - "name": "Profile.approvePurchase", + "name": "Privacy.onAllowPersonalizationChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Privacy.allowPersonalization" + }, + { + "name": "event", + "x-alternative": "allowPersonalization" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:approve:purchase" + "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Verifies that the current profile should have access to making purchases.", - "params": [], + "summary": "Whether the user allows their usage data to be used for personalization and recommendations", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "result": { "name": "allow", - "summary": "Whether or not to allow access", "schema": { - "type": "boolean" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ { - "name": "Default Example", - "params": [], + "name": "Default example #1", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "allow", + "value": true + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], "result": { "name": "allow", "value": false @@ -15978,155 +16175,164 @@ ] }, { - "name": "Profile.flags", + "name": "Privacy.onAllowUnentitledPersonalizationChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Privacy.allowUnentitledPersonalization" + }, + { + "name": "event", + "x-alternative": "allowUnentitledPersonalization" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:profile:flags" + "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Get a map of profile flags for the current session.", - "params": [], - "result": { - "name": "flags", - "summary": "The profile flags.", - "schema": { - "$ref": "#/x-schemas/Types/FlatMap" - } - }, - "examples": [ + "summary": "Whether the user allows their usage data to be used for personalization and recommendations for unentitled content", + "params": [ { - "name": "Default Example", - "params": [], - "result": { - "name": "flags", - "value": { - "userExperience": "1000" - } + "name": "listen", + "required": true, + "schema": { + "type": "boolean" } } - ] - }, - { - "name": "SecondScreen.protocols", - "summary": "Get the supported second screen discovery protocols", - "tags": [ - { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:device:info" - ] - } ], - "params": [], "result": { - "name": "protocols", - "summary": "the supported protocols", + "name": "allow", "schema": { - "$ref": "#/x-schemas/Types/BooleanMap" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ { - "name": "Default Example", - "params": [], + "name": "Default example #1", + "params": [ + { + "name": "listen", + "value": true + } + ], "result": { - "name": "Default Result", - "value": { - "dial1.7": true + "name": "allow", + "value": true + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true } + ], + "result": { + "name": "allow", + "value": false } } ] }, { - "name": "SecondScreen.device", + "name": "Privacy.onAllowRemoteDiagnosticsChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Privacy.allowRemoteDiagnostics" + }, + { + "name": "event", + "x-alternative": "allowRemoteDiagnostics" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:dial" + "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Get the broadcasted id for the device", + "summary": "Whether the user allows their personal data to be included in diagnostic telemetry. This also allows whether device logs can be remotely accessed from the client device", "params": [ { - "name": "type", - "summary": "The type of second screen protocol, e.g. \"dial\"", - "required": false, + "name": "listen", + "required": true, "schema": { - "type": "string" + "type": "boolean" } } ], "result": { - "name": "deviceId", - "summary": "the device id", + "name": "allow", "schema": { - "type": "string" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ { - "name": "Default Example", - "params": [], + "name": "Default example #1", + "params": [ + { + "name": "listen", + "value": true + } + ], "result": { - "name": "Default Result", - "value": "device-id" + "name": "allow", + "value": true } - } - ] - }, - { - "name": "SecondScreen.friendlyName", - "summary": "Get the broadcasted friendly name for the device", - "params": [], - "tags": [ - { - "name": "property:readonly" }, { - "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:protocol:dial" - ] - } - ], - "result": { - "name": "friendlyName", - "summary": "the device friendly-name", - "schema": { - "type": "string" - } - }, - "examples": [ - { - "name": "Default Example", - "params": [], + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], "result": { - "name": "friendlyName", - "value": "Living Room" + "name": "allow", + "value": false } } ] }, { - "name": "SecondScreen.onLaunchRequest", + "name": "Privacy.onAllowPrimaryContentAdTargetingChanged", "tags": [ { - "name": "event" + "name": "subscriber", + "x-subscriber-for": "Privacy.allowPrimaryContentAdTargeting" + }, + { + "name": "event", + "x-alternative": "allowPrimaryContentAdTargeting" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:dial" + "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Listen to the launchRequest event", + "summary": "Whether the user allows ads to be targeted to the user while watching content in the primary experience", "params": [ { "name": "listen", @@ -16137,22 +16343,21 @@ } ], "result": { - "name": "launchRequestEvent", - "summary": "Dispatched when a second screen device on the local network has requested this app to be launched", + "name": "allow", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/x-schemas/SecondScreen/SecondScreenEvent" + "type": "boolean" } ] } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { "name": "listen", @@ -16160,30 +16365,44 @@ } ], "result": { - "name": "Default Result", - "value": { - "type": "dial", - "version": "1.7", - "data": "{\"code\":\"AQDPQZiQcb3KQ7gY7yy5tHTMbbkGHR9Zjp-KL53H3eKBZIeAt7O9UKYPu6B21l2UZVmIqkFXDXBmXvK4g2e3EgZtjMNmKPsTltgnRl95DImtOXjSpWtTjSaOkW4w1kZKUTwLKdwVWTzBVH8ERHorvLU6vCGOVHxXt65LNwdl5HKRweShVC1V9QsyvRnQS61ov0UclmrH_xZML2Bt-Q-rZFjey5MjwupIb4x4f53XUJMhjHpDHoIUKrjpdPDQvK2a\",\"friendlyName\":\"Operator_TX061AEI\",\"UDN\":\"608fef11-2800-482a-962b-23a6690c93c1\"}" + "name": "allow", + "value": true + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true } + ], + "result": { + "name": "allow", + "value": false } } ] }, { - "name": "SecondScreen.onCloseRequest", + "name": "Privacy.onAllowPrimaryBrowseAdTargetingChanged", "tags": [ { - "name": "event" + "name": "subscriber", + "x-subscriber-for": "Privacy.allowPrimaryBrowseAdTargeting" + }, + { + "name": "event", + "x-alternative": "allowPrimaryBrowseAdTargeting" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:dial" + "xrn:firebolt:capability:privacy:settings" ] } ], - "summary": "Listen to the closeRequest event", + "summary": "Whether the user allows ads to be targeted to the user while browsing in the primary experience", "params": [ { "name": "listen", @@ -16194,21 +16413,21 @@ } ], "result": { - "name": "closeRequestEvent", + "name": "allow", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "$ref": "#/x-schemas/SecondScreen/SecondScreenEvent" + "type": "boolean" } ] } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { "name": "listen", @@ -16216,60 +16435,69 @@ } ], "result": { - "name": "Default Result", - "value": { - "type": "dial", - "version": "1.7" + "name": "allow", + "value": true + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true } + ], + "result": { + "name": "allow", + "value": false } } ] }, { - "name": "SecondScreen.onFriendlyNameChanged", - "summary": "Get the broadcasted friendly name for the device", - "params": [ - { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" - } - } - ], + "name": "Privacy.onAllowAppContentAdTargetingChanged", "tags": [ { "name": "subscriber", - "x-subscriber-for": "friendlyName" + "x-subscriber-for": "Privacy.allowAppContentAdTargeting" }, { "name": "event", - "x-alternative": "friendlyName" + "x-alternative": "allowAppContentAdTargeting" }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:dial" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows ads to be targeted to the user while watching content in apps", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "result": { - "name": "friendlyName", - "summary": "the device friendly-name", + "name": "allow", "schema": { "anyOf": [ { "$ref": "#/x-schemas/Types/ListenResponse" }, { - "type": "string" + "type": "boolean" } ] } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { "name": "listen", @@ -16277,1551 +16505,1414 @@ } ], "result": { - "name": "friendlyName", - "value": "Living Room" + "name": "allow", + "value": true + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "allow", + "value": false } } ] }, { - "name": "SecureStorage.get", - "summary": "Get stored value by key", + "name": "Privacy.onAllowACRCollectionChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Privacy.allowACRCollection" + }, + { + "name": "event", + "x-alternative": "allowACRCollection" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:storage:secure" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows their automatic content recognition data to be collected", "params": [ { - "name": "scope", - "summary": "The scope of the key/value", - "schema": { - "$ref": "#/components/schemas/StorageScope" - }, - "required": true - }, - { - "name": "key", - "summary": "Key to get", + "name": "listen", + "required": true, "schema": { - "type": "string" - }, - "required": true + "type": "boolean" + } } ], "result": { - "name": "value", - "summary": "The retrieved value, if found.", + "name": "allow", "schema": { - "oneOf": [ + "anyOf": [ { - "type": "string" + "$ref": "#/x-schemas/Types/ListenResponse" }, { - "type": "null" + "type": "boolean" } ] } }, "examples": [ { - "name": "Successfully retrieve a refresh token with key authRefreshToken", + "name": "Default example #1", "params": [ { - "name": "scope", - "value": "device" - }, - { - "name": "key", - "value": "authRefreshToken" + "name": "listen", + "value": true } ], "result": { - "name": "value", - "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" + "name": "allow", + "value": true } }, { - "name": "Attempt to retrieve a key with no value set", + "name": "Default example #2", "params": [ { - "name": "scope", - "value": "account" - }, - { - "name": "key", - "value": "authRefreshToken" + "name": "listen", + "value": true } ], "result": { - "name": "value", - "value": null + "name": "allow", + "value": false } } ] }, { - "name": "SecureStorage.set", - "summary": "Set or update a secure data value", + "name": "Privacy.onAllowCameraAnalyticsChanged", "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "Privacy.allowCameraAnalytics" + }, + { + "name": "event", + "x-alternative": "allowCameraAnalytics" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:storage:secure" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows data from their camera to be used for Product Analytics", "params": [ { - "name": "scope", - "summary": "The scope of the data key", - "schema": { - "$ref": "#/components/schemas/StorageScope" - }, - "required": true - }, - { - "name": "key", - "summary": "Key to set", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "value", - "summary": "Value to set", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "options", - "summary": "Optional parameters to set", + "name": "listen", + "required": true, "schema": { - "$ref": "#/components/schemas/StorageOptions" - }, - "required": false + "type": "boolean" + } } ], "result": { - "name": "success", + "name": "allow", "schema": { - "const": null + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] } }, "examples": [ { - "name": "Set a refresh token with name authRefreshToken with optional paramter", + "name": "Default example #1", "params": [ { - "name": "scope", - "value": "device" - }, - { - "name": "key", - "value": "authRefreshToken" - }, - { - "name": "value", - "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" - }, - { - "name": "options", - "value": { - "ttl": 600 - } + "name": "listen", + "value": true } ], "result": { - "name": "defaultResult", - "value": null + "name": "allow", + "value": true } }, { - "name": "Set a refresh token with name authRefreshToken without optional parameter", + "name": "Default example #2", "params": [ { - "name": "scope", - "value": "account" - }, - { - "name": "key", - "value": "authRefreshToken" - }, - { - "name": "value", - "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" + "name": "listen", + "value": true } ], "result": { - "name": "defaultResult", - "value": null + "name": "allow", + "value": false } } ] }, { - "name": "SecureStorage.remove", - "summary": "Remove a secure data value", + "name": "Privacy.setAllowResumePoints", "tags": [ + { + "name": "setter", + "x-setter-for": "allowResumePoints" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:storage:secure" + "x-manages": [ + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows resume points for content to show in the main experience", "params": [ { - "name": "scope", - "summary": "The scope of the data key", - "schema": { - "$ref": "#/components/schemas/StorageScope" - }, - "required": true - }, - { - "name": "key", - "summary": "Key to remove", + "name": "value", "schema": { - "type": "string" + "type": "boolean" }, "required": true } ], "result": { - "name": "success", - "summary": "", + "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Remove the value with key authRefreshToken for device", + "name": "Default example #1", "params": [ { - "name": "scope", - "value": "device" - }, - { - "name": "key", - "value": "authRefreshToken" + "name": "value", + "value": true } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } }, { - "name": "Remove the value with key authRefreshToken for account", + "name": "Default example #2", "params": [ { - "name": "scope", - "value": "account" - }, - { - "name": "key", - "value": "authRefreshToken" + "name": "value", + "value": false } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } } ] }, { - "name": "SecureStorage.setForApp", - "summary": "Set or update a secure data value for a specific app.", + "name": "Privacy.setAllowUnentitledResumePoints", "tags": [ + { + "name": "setter", + "x-setter-for": "allowUnentitledResumePoints" + }, { "name": "capabilities", "x-manages": [ - "xrn:firebolt:capability:storage:secure" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows resume points for content from unentitled providers to show in the main experience", "params": [ - { - "name": "appId", - "summary": "appId for which value is being set", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "scope", - "summary": "The scope of the data key", - "schema": { - "$ref": "#/components/schemas/StorageScope" - }, - "required": true - }, - { - "name": "key", - "summary": "Key to set", - "schema": { - "type": "string" - }, - "required": true - }, { "name": "value", - "summary": "Value to set", "schema": { - "type": "string" + "type": "boolean" }, "required": true - }, - { - "name": "options", - "summary": "Optional parameters to set", - "schema": { - "$ref": "#/components/schemas/StorageOptions" - }, - "required": false } ], "result": { - "name": "success", + "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Set a refresh token with name authRefreshToken with optional parameter for appId foo", + "name": "Default example #1", "params": [ - { - "name": "appId", - "value": "foo" - }, - { - "name": "scope", - "value": "device" - }, - { - "name": "key", - "value": "authRefreshToken" - }, { "name": "value", - "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" - }, - { - "name": "options", - "value": { - "ttl": 600 - } + "value": true } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } }, { - "name": "Set a refresh token with name authRefreshToken without optional parameter for appId foo", + "name": "Default example #2", "params": [ - { - "name": "appId", - "value": "foo" - }, - { - "name": "scope", - "value": "account" - }, - { - "name": "key", - "value": "authRefreshToken" - }, { "name": "value", - "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" + "value": false } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } } ] }, { - "name": "SecureStorage.removeForApp", - "summary": "Removes single data value for a specific app.", + "name": "Privacy.setAllowWatchHistory", "tags": [ + { + "name": "setter", + "x-setter-for": "allowWatchHistory" + }, { "name": "capabilities", "x-manages": [ - "xrn:firebolt:capability:storage:secure" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows their watch history from all sources to show in the main experience", "params": [ { - "name": "appId", - "summary": "appId for which values are removed", + "name": "value", "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "scope", - "summary": "The scope of the key/value", - "schema": { - "$ref": "#/components/schemas/StorageScope" - }, - "required": true - }, - { - "name": "key", - "summary": "Key to remove", - "schema": { - "type": "string" + "type": "boolean" }, "required": true } ], "result": { - "name": "success", - "summary": "", + "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Removes authRefreshToken for appId foo", + "name": "Default example #1", "params": [ { - "name": "appId", - "value": "foo" - }, - { - "name": "scope", - "value": "account" - }, + "name": "value", + "value": true + } + ], + "result": { + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "key", - "value": "authRefreshToken" + "name": "value", + "value": false } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } } ] }, { - "name": "SecureStorage.clearForApp", - "summary": "Clears all the secure data values for a specific app", + "name": "Privacy.setAllowProductAnalytics", "tags": [ + { + "name": "setter", + "x-setter-for": "allowProductAnalytics" + }, { "name": "capabilities", "x-manages": [ - "xrn:firebolt:capability:storage:secure" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows their usage data can be used for analytics about the product", "params": [ { - "name": "appId", - "summary": "appId for which values are removed", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "scope", - "summary": "The scope of the key/value", + "name": "value", "schema": { - "$ref": "#/components/schemas/StorageScope" + "type": "boolean" }, "required": true } ], "result": { - "name": "success", - "summary": "", + "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Clears all the secure data values for appId foo", + "name": "Default example #1", "params": [ { - "name": "appId", - "value": "foo" - }, + "name": "value", + "value": true + } + ], + "result": { + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "scope", - "value": "account" + "name": "value", + "value": false } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } } ] }, { - "name": "SecureStorage.clear", - "summary": "Clears all the secure data values", + "name": "Privacy.setAllowPersonalization", "tags": [ + { + "name": "setter", + "x-setter-for": "allowPersonalization" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:storage:secure" + "x-manages": [ + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows their usage data to be used for personalization and recommendations", "params": [ { - "name": "scope", - "summary": "The scope of the key/value", + "name": "value", "schema": { - "$ref": "#/components/schemas/StorageScope" + "type": "boolean" }, "required": true } ], "result": { - "name": "success", - "summary": "", + "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Clears all the data values of storage", + "name": "Default example #1", "params": [ { - "name": "scope", - "value": "account" + "name": "value", + "value": true } ], "result": { - "name": "defaultResult", + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "value", + "value": false + } + ], + "result": { + "name": "allow", "value": null } } ] }, { - "name": "UserGrants.app", - "summary": "Get all granted and denied user grants for the given app", + "name": "Privacy.setAllowUnentitledPersonalization", "tags": [ + { + "name": "setter", + "x-setter-for": "allowUnentitledPersonalization" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:grants:state" + "x-manages": [ + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows their usage data to be used for personalization and recommendations for unentitled content", "params": [ { - "name": "appId", + "name": "value", "schema": { - "type": "string" + "type": "boolean" }, "required": true } ], "result": { - "name": "info", - "summary": "The list of grants for this app", + "name": "result", "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GrantInfo" - } + "type": "null" } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { - "name": "appId", - "value": "certapp" + "name": "value", + "value": true } ], "result": { - "name": "defaultResult", - "value": [ - { - "app": { - "id": "certapp", - "title": "Firebolt Certification" - }, - "state": "granted", - "capability": "xrn:firebolt:capability:data:app-usage", - "role": "use", - "lifespan": "seconds", - "expires": "2022-12-14T20:20:39+00:00" - }, - { - "app": { - "id": "certapp", - "title": "Firebolt Certification" - }, - "state": "denied", - "capability": "xrn:firebolt:capability:localization:postal-code", - "role": "use", - "lifespan": "appActive" - } - ] + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "value", + "value": false + } + ], + "result": { + "name": "allow", + "value": null } } ] }, { - "name": "UserGrants.device", - "summary": "Get all granted and denied user grants for the device", + "name": "Privacy.setAllowRemoteDiagnostics", "tags": [ + { + "name": "setter", + "x-setter-for": "allowRemoteDiagnostics" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:grants:state" + "x-manages": [ + "xrn:firebolt:capability:privacy:settings" ] } ], - "params": [], + "summary": "Whether the user allows their personal data to be included in diagnostic telemetry. This also allows whether device logs can be remotely accessed from the client device", + "params": [ + { + "name": "value", + "schema": { + "type": "boolean" + }, + "required": true + } + ], "result": { - "name": "info", - "summary": "The list of grants for the device", + "name": "result", "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GrantInfo" - } + "type": "null" } }, "examples": [ { - "name": "Default Example", - "params": [], + "name": "Default example #1", + "params": [ + { + "name": "value", + "value": true + } + ], "result": { - "name": "defaultResult", - "value": [ - { - "state": "granted", - "capability": "xrn:firebolt:capability:localization:postal-code", - "role": "use", - "lifespan": "powerActive" - } - ] + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "value", + "value": false + } + ], + "result": { + "name": "allow", + "value": null } } ] }, { - "name": "UserGrants.capability", - "summary": "Get all granted and denied user grants for the given capability", + "name": "Privacy.setAllowPrimaryContentAdTargeting", "tags": [ + { + "name": "setter", + "x-setter-for": "allowPrimaryContentAdTargeting" + }, { "name": "capabilities", - "x-uses": [ - "xrn:firebolt:capability:grants:state" + "x-manages": [ + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows ads to be targeted to the user while watching content in the primary experience", "params": [ { - "name": "capability", + "name": "value", "schema": { - "$ref": "#/x-schemas/Capabilities/Capability" + "type": "boolean" }, "required": true } ], "result": { - "name": "info", - "summary": "The list of grants associated with the given capability", + "name": "result", "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GrantInfo" - } + "type": "null" } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { - "name": "capability", - "value": "xrn:firebolt:capability:localization:postal-code" + "name": "value", + "value": true } ], "result": { - "name": "defaultResult", - "value": [ - { - "state": "granted", - "capability": "xrn:firebolt:capability:localization:postal-code", - "role": "use", - "lifespan": "powerActive" - } - ] + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "value", + "value": false + } + ], + "result": { + "name": "allow", + "value": null } } ] }, { - "name": "UserGrants.grant", - "summary": "Grants a given capability to a specific app, if appropriate. Calling this results in a persisted active grant that lasts for the duration of the grant policy lifespan. ", + "name": "Privacy.setAllowPrimaryBrowseAdTargeting", "tags": [ + { + "name": "setter", + "x-setter-for": "allowPrimaryBrowseAdTargeting" + }, { "name": "capabilities", "x-manages": [ - "xrn:firebolt:capability:grants:state" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows ads to be targeted to the user while browsing in the primary experience", "params": [ { - "name": "role", - "schema": { - "$ref": "#/x-schemas/Capabilities/Role" - }, - "required": true - }, - { - "name": "capability", + "name": "value", "schema": { - "$ref": "#/x-schemas/Capabilities/Capability" + "type": "boolean" }, "required": true - }, - { - "name": "options", - "schema": { - "$ref": "#/components/schemas/GrantModificationOptions" - } } ], "result": { "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { - "name": "role", - "value": "use" - }, - { - "name": "capability", - "value": "xrn:firebolt:capability:localization:postal-code" - }, + "name": "value", + "value": true + } + ], + "result": { + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "options", - "value": { - "appId": "certapp" - } + "name": "value", + "value": false } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } } ] }, { - "name": "UserGrants.deny", - "summary": "Denies a given capability, to a specific app if appropriate. Calling this results in a persisted Denied Grant that lasts for the duration of the Grant Policy lifespan. ", + "name": "Privacy.setAllowAppContentAdTargeting", "tags": [ + { + "name": "setter", + "x-setter-for": "allowAppContentAdTargeting" + }, { "name": "capabilities", "x-manages": [ - "xrn:firebolt:capability:grants:state" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows ads to be targeted to the user while watching content in apps", "params": [ { - "name": "role", - "schema": { - "$ref": "#/x-schemas/Capabilities/Role" - }, - "required": true - }, - { - "name": "capability", + "name": "value", "schema": { - "$ref": "#/x-schemas/Capabilities/Capability" + "type": "boolean" }, "required": true - }, - { - "name": "options", - "schema": { - "$ref": "#/components/schemas/GrantModificationOptions" - } } ], "result": { "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { - "name": "role", - "value": "use" - }, - { - "name": "capability", - "value": "xrn:firebolt:capability:localization:postal-code" - }, + "name": "value", + "value": true + } + ], + "result": { + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "options", - "value": { - "appId": "certapp" - } + "name": "value", + "value": false } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } } ] }, { - "name": "UserGrants.clear", - "summary": "Clears the grant for a given capability, to a specific app if appropriate. Calling this results in a persisted Denied Grant that lasts for the duration of the Grant Policy lifespan. ", + "name": "Privacy.setAllowACRCollection", "tags": [ + { + "name": "setter", + "x-setter-for": "allowACRCollection" + }, { "name": "capabilities", "x-manages": [ - "xrn:firebolt:capability:grants:state" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows their automatic content recognition data to be collected", "params": [ { - "name": "role", - "schema": { - "$ref": "#/x-schemas/Capabilities/Role" - }, - "required": true - }, - { - "name": "capability", + "name": "value", "schema": { - "$ref": "#/x-schemas/Capabilities/Capability" + "type": "boolean" }, "required": true - }, - { - "name": "options", - "schema": { - "$ref": "#/components/schemas/GrantModificationOptions" - } } ], "result": { "name": "result", "schema": { - "const": null + "type": "null" } }, "examples": [ { - "name": "Default Example", + "name": "Default example #1", "params": [ { - "name": "role", - "value": "use" - }, - { - "name": "capability", - "value": "xrn:firebolt:capability:localization:postal-code" - }, + "name": "value", + "value": true + } + ], + "result": { + "name": "allow", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ { - "name": "options", - "value": { - "appId": "certapp" - } + "name": "value", + "value": false } ], "result": { - "name": "defaultResult", + "name": "allow", "value": null } } ] }, { - "name": "UserGrants.request", - "summary": "Requests Firebolt to carry out a set of user grants for a given application such that the user grant provider is notified or an existing user grant is reused.", + "name": "Privacy.setAllowCameraAnalytics", "tags": [ + { + "name": "setter", + "x-setter-for": "allowCameraAnalytics" + }, { "name": "capabilities", "x-manages": [ - "xrn:firebolt:capability:grants:state" + "xrn:firebolt:capability:privacy:settings" ] } ], + "summary": "Whether the user allows data from their camera to be used for Product Analytics", "params": [ { - "name": "appId", - "schema": { - "type": "string" - }, - "required": true - }, - { - "name": "permissions", + "name": "value", "schema": { - "type": "array", - "items": { - "$ref": "#/x-schemas/Capabilities/Permission" - }, - "minItems": 1 + "type": "boolean" }, "required": true - }, - { - "name": "options", - "summary": "Request options", - "schema": { - "$ref": "#/components/schemas/RequestOptions" - }, - "required": false } ], "result": { - "name": "info", - "summary": "The result of all grants requested by this", + "name": "result", "schema": { - "type": "array", - "items": { - "$ref": "#/components/schemas/GrantInfo" - } + "type": "null" } }, "examples": [ { - "name": "Default result #1", + "name": "Default example #1", "params": [ { - "name": "appId", - "value": "certapp" - }, - { - "name": "permissions", - "value": [ - { - "role": "use", - "capability": "xrn:firebolt:capability:localization:postal-code" - } - ] + "name": "value", + "value": true } ], "result": { - "name": "defaultResult", - "value": [ - { - "app": { - "id": "certapp", - "title": "Certification App" - }, - "state": "granted", - "capability": "xrn:firebolt:capability:localization:postal-code", - "role": "use", - "lifespan": "powerActive" - } - ] + "name": "allow", + "value": null } }, { - "name": "Default result #2", + "name": "Default example #2", "params": [ { - "name": "appId", - "value": "certapp" - }, - { - "name": "permissions", - "value": [ - { - "role": "use", - "capability": "xrn:firebolt:capability:localization:postal-code" - } - ] - }, - { - "name": "options", - "value": { - "force": true - } + "name": "value", + "value": false } ], "result": { - "name": "defaultResult", - "value": [ - { - "app": { - "id": "certapp", - "title": "Certification App" - }, - "state": "granted", - "capability": "xrn:firebolt:capability:localization:postal-code", - "role": "use", - "lifespan": "powerActive" - } - ] + "name": "allow", + "value": null } } ] }, { - "name": "VoiceGuidance.enabled", + "name": "Profile.approveContentRating", "tags": [ - { - "name": "property" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:accessibility:voiceguidance" + "xrn:firebolt:capability:approve:content" ] } ], - "summary": "Whether or not voice-guidance is enabled.", + "summary": "Verifies that the current profile should have access to mature/adult content.", "params": [], "result": { - "name": "enabled", + "name": "allow", + "summary": "Whether or not to allow access", "schema": { "type": "boolean" } }, "examples": [ { - "name": "Default example #1", - "params": [], - "result": { - "name": "enabled", - "value": true - } - }, - { - "name": "Default example #2", + "name": "Default Example", "params": [], "result": { - "name": "enabled", + "name": "allow", "value": false } } ] }, { - "name": "VoiceGuidance.speed", + "name": "Profile.approvePurchase", "tags": [ - { - "name": "property" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:accessibility:voiceguidance" + "xrn:firebolt:capability:approve:purchase" ] } ], - "summary": "The speed at which voice guidance speech will be read back to the user.", + "summary": "Verifies that the current profile should have access to making purchases.", "params": [], "result": { - "name": "speed", + "name": "allow", + "summary": "Whether or not to allow access", "schema": { - "$ref": "#/x-schemas/Accessibility/VoiceSpeed" + "type": "boolean" } }, "examples": [ { - "name": "Voice guidance speed to 1", - "params": [], - "result": { - "name": "speed", - "value": 1 - } - }, - { - "name": "Voice guidance speed to 2", + "name": "Default Example", "params": [], "result": { - "name": "speed", - "value": 2 + "name": "allow", + "value": false } } ] }, { - "name": "VoiceGuidance.onEnabledChanged", + "name": "Profile.flags", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "enabled" - }, - { - "name": "event", - "x-alternative": "enabled" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:accessibility:voiceguidance" + "xrn:firebolt:capability:profile:flags" ] } ], - "summary": "Whether or not voice-guidance is enabled.", - "params": [ + "summary": "Get a map of profile flags for the current session.", + "params": [], + "result": { + "name": "flags", + "summary": "The profile flags.", + "schema": { + "$ref": "#/x-schemas/Types/FlatMap" + } + }, + "examples": [ { - "name": "listen", - "required": true, - "schema": { - "type": "boolean" + "name": "Default Example", + "params": [], + "result": { + "name": "flags", + "value": { + "userExperience": "1000" + } } } + ] + }, + { + "name": "SecondScreen.protocols", + "summary": "Get the supported second screen discovery protocols", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:device:info" + ] + } ], + "params": [], "result": { - "name": "enabled", + "name": "protocols", + "summary": "the supported protocols", "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "type": "boolean" - } - ] + "$ref": "#/x-schemas/Types/BooleanMap" } }, "examples": [ { - "name": "Default example #1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "name": "Default Example", + "params": [], "result": { - "name": "enabled", - "value": true - } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "listen", - "value": true + "name": "Default Result", + "value": { + "dial1.7": true } - ], - "result": { - "name": "enabled", - "value": false } } ] }, { - "name": "VoiceGuidance.onSpeedChanged", + "name": "SecondScreen.device", "tags": [ - { - "name": "subscriber", - "x-subscriber-for": "speed" - }, - { - "name": "event", - "x-alternative": "speed" - }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:accessibility:voiceguidance" + "xrn:firebolt:capability:protocol:dial" ] } ], - "summary": "The speed at which voice guidance speech will be read back to the user.", + "summary": "Get the broadcasted id for the device", "params": [ { - "name": "listen", - "required": true, + "name": "type", + "summary": "The type of second screen protocol, e.g. \"dial\"", + "required": false, "schema": { - "type": "boolean" + "type": "string" } } ], "result": { - "name": "speed", - "schema": { - "anyOf": [ - { - "$ref": "#/x-schemas/Types/ListenResponse" - }, - { - "$ref": "#/x-schemas/Accessibility/VoiceSpeed" - } - ] + "name": "deviceId", + "summary": "the device id", + "schema": { + "type": "string" } }, "examples": [ { - "name": "Voice guidance speed to 1", - "params": [ - { - "name": "listen", - "value": true - } - ], + "name": "Default Example", + "params": [], "result": { - "name": "speed", - "value": 1 + "name": "Default Result", + "value": "device-id" } + } + ] + }, + { + "name": "SecondScreen.friendlyName", + "summary": "Get the broadcasted friendly name for the device", + "params": [], + "tags": [ + { + "name": "property:readonly" }, { - "name": "Voice guidance speed to 2", - "params": [ - { - "name": "listen", - "value": true - } - ], + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:protocol:dial" + ] + } + ], + "result": { + "name": "friendlyName", + "summary": "the device friendly-name", + "schema": { + "type": "string" + } + }, + "examples": [ + { + "name": "Default Example", + "params": [], "result": { - "name": "speed", - "value": 2 + "name": "friendlyName", + "value": "Living Room" } } ] }, { - "name": "VoiceGuidance.setEnabled", + "name": "SecondScreen.onLaunchRequest", "tags": [ { - "name": "setter", - "x-setter-for": "enabled" + "name": "event" }, { "name": "capabilities", - "x-manages": [ - "xrn:firebolt:capability:accessibility:voiceguidance" + "x-uses": [ + "xrn:firebolt:capability:protocol:dial" ] } ], - "summary": "Whether or not voice-guidance is enabled.", + "summary": "Listen to the launchRequest event", "params": [ { - "name": "value", + "name": "listen", + "required": true, "schema": { "type": "boolean" - }, - "required": true + } } ], "result": { - "name": "result", + "name": "launchRequestEvent", + "summary": "Dispatched when a second screen device on the local network has requested this app to be launched", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/SecondScreen/SecondScreenEvent" + } + ] } }, "examples": [ { - "name": "Default example #1", + "name": "Default Example", "params": [ { - "name": "value", + "name": "listen", "value": true } ], "result": { - "name": "enabled", - "value": null - } - }, - { - "name": "Default example #2", - "params": [ - { - "name": "value", - "value": false + "name": "Default Result", + "value": { + "type": "dial", + "version": "1.7", + "data": "{\"code\":\"AQDPQZiQcb3KQ7gY7yy5tHTMbbkGHR9Zjp-KL53H3eKBZIeAt7O9UKYPu6B21l2UZVmIqkFXDXBmXvK4g2e3EgZtjMNmKPsTltgnRl95DImtOXjSpWtTjSaOkW4w1kZKUTwLKdwVWTzBVH8ERHorvLU6vCGOVHxXt65LNwdl5HKRweShVC1V9QsyvRnQS61ov0UclmrH_xZML2Bt-Q-rZFjey5MjwupIb4x4f53XUJMhjHpDHoIUKrjpdPDQvK2a\",\"friendlyName\":\"Operator_TX061AEI\",\"UDN\":\"608fef11-2800-482a-962b-23a6690c93c1\"}" } - ], - "result": { - "name": "enabled", - "value": null } } ] }, { - "name": "VoiceGuidance.setSpeed", + "name": "SecondScreen.onCloseRequest", "tags": [ { - "name": "setter", - "x-setter-for": "speed" + "name": "event" }, { "name": "capabilities", - "x-manages": [ - "xrn:firebolt:capability:accessibility:voiceguidance" + "x-uses": [ + "xrn:firebolt:capability:protocol:dial" ] } ], - "summary": "The speed at which voice guidance speech will be read back to the user.", + "summary": "Listen to the closeRequest event", "params": [ { - "name": "value", + "name": "listen", + "required": true, "schema": { - "$ref": "#/x-schemas/Accessibility/VoiceSpeed" - }, - "required": true + "type": "boolean" + } } ], "result": { - "name": "result", + "name": "closeRequestEvent", "schema": { - "type": "null" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/SecondScreen/SecondScreenEvent" + } + ] } }, "examples": [ { - "name": "Voice guidance speed to 1", + "name": "Default Example", "params": [ { - "name": "value", - "value": 1 + "name": "listen", + "value": true } ], "result": { - "name": "speed", - "value": null - } - }, - { - "name": "Voice guidance speed to 2", - "params": [ - { - "name": "value", - "value": 2 + "name": "Default Result", + "value": { + "type": "dial", + "version": "1.7" } - ], - "result": { - "name": "speed", - "value": null } } ] }, { - "name": "Wifi.scan", - "summary": "Scan available wifi networks in the location.", + "name": "SecondScreen.onFriendlyNameChanged", + "summary": "Get the broadcasted friendly name for the device", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "SecondScreen.friendlyName" + }, + { + "name": "event", + "x-alternative": "friendlyName" + }, { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:wifi" + "xrn:firebolt:capability:protocol:dial" ] } ], - "params": [ - { - "name": "timeout", - "schema": { - "$ref": "#/x-schemas/Types/Timeout" - } - } - ], "result": { - "name": "list", - "summary": "Contains a list of wifi networks available near the device.", + "name": "friendlyName", + "summary": "the device friendly-name", "schema": { - "$ref": "#/components/schemas/AccessPointList" + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "string" + } + ] } }, "examples": [ { - "name": "Successful Wifi List", + "name": "Default Example", "params": [ { - "name": "timeout", - "value": 30 + "name": "listen", + "value": true } ], "result": { - "name": "successfulWifiResultExample", - "value": { - "list": [ - { - "ssid": "DND", - "security": "wpa2Psk", - "signalStrength": -70, - "frequency": 2.4 - }, - { - "ssid": "Fortnite", - "security": "WPA2_ENTERPRISE_AES", - "signalStrength": -70, - "frequency": 5 - }, - { - "ssid": "Guardian", - "security": "none", - "signalStrength": -70, - "frequency": 2.4 - } - ] - } + "name": "friendlyName", + "value": "Living Room" } } ] }, { - "name": "Wifi.connect", - "summary": "Connect the device to the specified SSID.", + "name": "SecureStorage.get", + "summary": "Get stored value by key", "tags": [ { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:wifi" + "xrn:firebolt:capability:storage:secure" ] } ], "params": [ { - "name": "ssid", + "name": "scope", + "summary": "The scope of the key/value", "schema": { - "type": "string" + "$ref": "#/components/schemas/StorageScope" }, - "description": "Name of Wifi SSID to connect for the device." + "required": true }, { - "name": "passphrase", + "name": "key", + "summary": "Key to get", "schema": { "type": "string" }, - "description": "Password or Passphrase for the wifi." - }, - { - "name": "security", - "schema": { - "$ref": "#/components/schemas/WifiSecurityMode" - } + "required": true } ], "result": { - "name": "connectedWifi", - "summary": "Successful Response after connecting to the Wifi.", + "name": "value", + "summary": "The retrieved value, if found.", "schema": { - "$ref": "#/components/schemas/AccessPoint" + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] } }, "examples": [ { - "name": "Connect to a wpa2Psk Wifi with password", + "name": "Successfully retrieve a refresh token with key authRefreshToken", "params": [ { - "name": "ssid", - "value": "DND" - }, - { - "name": "passphrase", - "value": "gargoyle" + "name": "scope", + "value": "device" }, { - "name": "security", - "value": "wpa2Psk" + "name": "key", + "value": "authRefreshToken" } ], "result": { - "name": "successfulWifiConnection", - "value": { - "ssid": "DND", - "security": "wpa2Psk", - "signalStrength": -70, - "frequency": 2.4 - } + "name": "value", + "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" } }, { - "name": "Connect to a WPA2 PSK Wifi with password", + "name": "Attempt to retrieve a key with no value set", "params": [ { - "name": "ssid", - "value": "Guardian WIFI" - }, - { - "name": "passphrase", - "value": "" + "name": "scope", + "value": "account" }, { - "name": "security", - "value": "none" + "name": "key", + "value": "authRefreshToken" } ], "result": { - "name": "successfulWifiConnection", - "value": { - "ssid": "Guardian WIFI", - "security": "none", - "signalStrength": -70, - "frequency": 2.4 - } + "name": "value", + "value": null } } ] }, { - "name": "Wifi.disconnect", - "summary": "Disconnect the device if connected via WIFI.", + "name": "SecureStorage.set", + "summary": "Set or update a secure data value", "tags": [ { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:wifi" + "xrn:firebolt:capability:storage:secure" ] } ], - "params": [], + "params": [ + { + "name": "scope", + "summary": "The scope of the data key", + "schema": { + "$ref": "#/components/schemas/StorageScope" + }, + "required": true + }, + { + "name": "key", + "summary": "Key to set", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "value", + "summary": "Value to set", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "options", + "summary": "Optional parameters to set", + "schema": { + "$ref": "#/components/schemas/StorageOptions" + }, + "required": false + } + ], "result": { - "name": "result", + "name": "success", "schema": { "const": null } }, "examples": [ { - "name": "Disconnect", - "params": [], + "name": "Set a refresh token with name authRefreshToken with optional paramter", + "params": [ + { + "name": "scope", + "value": "device" + }, + { + "name": "key", + "value": "authRefreshToken" + }, + { + "name": "value", + "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" + }, + { + "name": "options", + "value": { + "ttl": 600 + } + } + ], + "result": { + "name": "defaultResult", + "value": null + } + }, + { + "name": "Set a refresh token with name authRefreshToken without optional parameter", + "params": [ + { + "name": "scope", + "value": "account" + }, + { + "name": "key", + "value": "authRefreshToken" + }, + { + "name": "value", + "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" + } + ], "result": { "name": "defaultResult", "value": null @@ -17830,79 +17921,1443 @@ ] }, { - "name": "Wifi.wps", - "summary": "Connect to WPS", + "name": "SecureStorage.remove", + "summary": "Remove a secure data value", "tags": [ { "name": "capabilities", "x-uses": [ - "xrn:firebolt:capability:protocol:wifi" + "xrn:firebolt:capability:storage:secure" ] } ], "params": [ { - "name": "security", + "name": "scope", + "summary": "The scope of the data key", "schema": { - "$ref": "#/components/schemas/WPSSecurityPin" - } + "$ref": "#/components/schemas/StorageScope" + }, + "required": true + }, + { + "name": "key", + "summary": "Key to remove", + "schema": { + "type": "string" + }, + "required": true } ], "result": { - "name": "connectedWifi", - "summary": "Successful Response after connecting to the Wifi.", + "name": "success", + "summary": "", "schema": { - "$ref": "#/components/schemas/AccessPoint" + "const": null } }, "examples": [ { - "name": "Connect to a WPS Wifi router", + "name": "Remove the value with key authRefreshToken for device", "params": [ { - "name": "security", - "value": "pushButton" + "name": "scope", + "value": "device" + }, + { + "name": "key", + "value": "authRefreshToken" } ], "result": { - "name": "successfulWifiConnection", - "value": { - "ssid": "DND", - "security": "wpa2Psk", - "signalStrength": -70, - "frequency": 2.4 + "name": "defaultResult", + "value": null + } + }, + { + "name": "Remove the value with key authRefreshToken for account", + "params": [ + { + "name": "scope", + "value": "account" + }, + { + "name": "key", + "value": "authRefreshToken" } + ], + "result": { + "name": "defaultResult", + "value": null } } ] - } - ], - "components": { - "schemas": { - "AudioDescriptionSettings": { - "title": "AudioDescriptionSettings", - "type": "object", - "required": [ - "enabled" - ], - "properties": { - "enabled": { - "type": "boolean", - "description": "Whether or not audio descriptions should be enabled by default" - } + }, + { + "name": "SecureStorage.setForApp", + "summary": "Set or update a secure data value for a specific app.", + "tags": [ + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:storage:secure" + ] } - }, - "Token": { - "type": "string", - "description": "Encoded token provided by the Distributor for Device Authentication." - }, - "Expiry": { - "type": "integer", - "description": "Number of secs before the token expires", - "minimum": 1 - }, - "ChallengeRequestor": { - "title": "ChallengeRequestor", + ], + "params": [ + { + "name": "appId", + "summary": "appId for which value is being set", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "scope", + "summary": "The scope of the data key", + "schema": { + "$ref": "#/components/schemas/StorageScope" + }, + "required": true + }, + { + "name": "key", + "summary": "Key to set", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "value", + "summary": "Value to set", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "options", + "summary": "Optional parameters to set", + "schema": { + "$ref": "#/components/schemas/StorageOptions" + }, + "required": false + } + ], + "result": { + "name": "success", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Set a refresh token with name authRefreshToken with optional parameter for appId foo", + "params": [ + { + "name": "appId", + "value": "foo" + }, + { + "name": "scope", + "value": "device" + }, + { + "name": "key", + "value": "authRefreshToken" + }, + { + "name": "value", + "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" + }, + { + "name": "options", + "value": { + "ttl": 600 + } + } + ], + "result": { + "name": "defaultResult", + "value": null + } + }, + { + "name": "Set a refresh token with name authRefreshToken without optional parameter for appId foo", + "params": [ + { + "name": "appId", + "value": "foo" + }, + { + "name": "scope", + "value": "account" + }, + { + "name": "key", + "value": "authRefreshToken" + }, + { + "name": "value", + "value": "VGhpcyBub3QgYSByZWFsIHRva2VuLgo=" + } + ], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "SecureStorage.removeForApp", + "summary": "Removes single data value for a specific app.", + "tags": [ + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:storage:secure" + ] + } + ], + "params": [ + { + "name": "appId", + "summary": "appId for which values are removed", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "scope", + "summary": "The scope of the key/value", + "schema": { + "$ref": "#/components/schemas/StorageScope" + }, + "required": true + }, + { + "name": "key", + "summary": "Key to remove", + "schema": { + "type": "string" + }, + "required": true + } + ], + "result": { + "name": "success", + "summary": "", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Removes authRefreshToken for appId foo", + "params": [ + { + "name": "appId", + "value": "foo" + }, + { + "name": "scope", + "value": "account" + }, + { + "name": "key", + "value": "authRefreshToken" + } + ], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "SecureStorage.clearForApp", + "summary": "Clears all the secure data values for a specific app", + "tags": [ + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:storage:secure" + ] + } + ], + "params": [ + { + "name": "appId", + "summary": "appId for which values are removed", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "scope", + "summary": "The scope of the key/value", + "schema": { + "$ref": "#/components/schemas/StorageScope" + }, + "required": true + } + ], + "result": { + "name": "success", + "summary": "", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Clears all the secure data values for appId foo", + "params": [ + { + "name": "appId", + "value": "foo" + }, + { + "name": "scope", + "value": "account" + } + ], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "SecureStorage.clear", + "summary": "Clears all the secure data values", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:storage:secure" + ] + } + ], + "params": [ + { + "name": "scope", + "summary": "The scope of the key/value", + "schema": { + "$ref": "#/components/schemas/StorageScope" + }, + "required": true + } + ], + "result": { + "name": "success", + "summary": "", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Clears all the data values of storage", + "params": [ + { + "name": "scope", + "value": "account" + } + ], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "UserGrants.app", + "summary": "Get all granted and denied user grants for the given app", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:grants:state" + ] + } + ], + "params": [ + { + "name": "appId", + "schema": { + "type": "string" + }, + "required": true + } + ], + "result": { + "name": "info", + "summary": "The list of grants for this app", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GrantInfo" + } + } + }, + "examples": [ + { + "name": "Default Example", + "params": [ + { + "name": "appId", + "value": "certapp" + } + ], + "result": { + "name": "defaultResult", + "value": [ + { + "app": { + "id": "certapp", + "title": "Firebolt Certification" + }, + "state": "granted", + "capability": "xrn:firebolt:capability:data:app-usage", + "role": "use", + "lifespan": "seconds", + "expires": "2022-12-14T20:20:39+00:00" + }, + { + "app": { + "id": "certapp", + "title": "Firebolt Certification" + }, + "state": "denied", + "capability": "xrn:firebolt:capability:localization:postal-code", + "role": "use", + "lifespan": "appActive" + } + ] + } + } + ] + }, + { + "name": "UserGrants.device", + "summary": "Get all granted and denied user grants for the device", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:grants:state" + ] + } + ], + "params": [], + "result": { + "name": "info", + "summary": "The list of grants for the device", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GrantInfo" + } + } + }, + "examples": [ + { + "name": "Default Example", + "params": [], + "result": { + "name": "defaultResult", + "value": [ + { + "state": "granted", + "capability": "xrn:firebolt:capability:localization:postal-code", + "role": "use", + "lifespan": "powerActive" + } + ] + } + } + ] + }, + { + "name": "UserGrants.capability", + "summary": "Get all granted and denied user grants for the given capability", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:grants:state" + ] + } + ], + "params": [ + { + "name": "capability", + "schema": { + "$ref": "#/x-schemas/Capabilities/Capability" + }, + "required": true + } + ], + "result": { + "name": "info", + "summary": "The list of grants associated with the given capability", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GrantInfo" + } + } + }, + "examples": [ + { + "name": "Default Example", + "params": [ + { + "name": "capability", + "value": "xrn:firebolt:capability:localization:postal-code" + } + ], + "result": { + "name": "defaultResult", + "value": [ + { + "state": "granted", + "capability": "xrn:firebolt:capability:localization:postal-code", + "role": "use", + "lifespan": "powerActive" + } + ] + } + } + ] + }, + { + "name": "UserGrants.grant", + "summary": "Grants a given capability to a specific app, if appropriate. Calling this results in a persisted active grant that lasts for the duration of the grant policy lifespan. ", + "tags": [ + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:grants:state" + ] + } + ], + "params": [ + { + "name": "role", + "schema": { + "$ref": "#/x-schemas/Capabilities/Role" + }, + "required": true + }, + { + "name": "capability", + "schema": { + "$ref": "#/x-schemas/Capabilities/Capability" + }, + "required": true + }, + { + "name": "options", + "schema": { + "$ref": "#/components/schemas/GrantModificationOptions" + } + } + ], + "result": { + "name": "result", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Default Example", + "params": [ + { + "name": "role", + "value": "use" + }, + { + "name": "capability", + "value": "xrn:firebolt:capability:localization:postal-code" + }, + { + "name": "options", + "value": { + "appId": "certapp" + } + } + ], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "UserGrants.deny", + "summary": "Denies a given capability, to a specific app if appropriate. Calling this results in a persisted Denied Grant that lasts for the duration of the Grant Policy lifespan. ", + "tags": [ + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:grants:state" + ] + } + ], + "params": [ + { + "name": "role", + "schema": { + "$ref": "#/x-schemas/Capabilities/Role" + }, + "required": true + }, + { + "name": "capability", + "schema": { + "$ref": "#/x-schemas/Capabilities/Capability" + }, + "required": true + }, + { + "name": "options", + "schema": { + "$ref": "#/components/schemas/GrantModificationOptions" + } + } + ], + "result": { + "name": "result", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Default Example", + "params": [ + { + "name": "role", + "value": "use" + }, + { + "name": "capability", + "value": "xrn:firebolt:capability:localization:postal-code" + }, + { + "name": "options", + "value": { + "appId": "certapp" + } + } + ], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "UserGrants.clear", + "summary": "Clears the grant for a given capability, to a specific app if appropriate. Calling this results in a persisted Denied Grant that lasts for the duration of the Grant Policy lifespan. ", + "tags": [ + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:grants:state" + ] + } + ], + "params": [ + { + "name": "role", + "schema": { + "$ref": "#/x-schemas/Capabilities/Role" + }, + "required": true + }, + { + "name": "capability", + "schema": { + "$ref": "#/x-schemas/Capabilities/Capability" + }, + "required": true + }, + { + "name": "options", + "schema": { + "$ref": "#/components/schemas/GrantModificationOptions" + } + } + ], + "result": { + "name": "result", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Default Example", + "params": [ + { + "name": "role", + "value": "use" + }, + { + "name": "capability", + "value": "xrn:firebolt:capability:localization:postal-code" + }, + { + "name": "options", + "value": { + "appId": "certapp" + } + } + ], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "UserGrants.request", + "summary": "Requests Firebolt to carry out a set of user grants for a given application such that the user grant provider is notified or an existing user grant is reused.", + "tags": [ + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:grants:state" + ] + } + ], + "params": [ + { + "name": "appId", + "schema": { + "type": "string" + }, + "required": true + }, + { + "name": "permissions", + "schema": { + "type": "array", + "items": { + "$ref": "#/x-schemas/Capabilities/Permission" + }, + "minItems": 1 + }, + "required": true + }, + { + "name": "options", + "summary": "Request options", + "schema": { + "$ref": "#/components/schemas/RequestOptions" + }, + "required": false + } + ], + "result": { + "name": "info", + "summary": "The result of all grants requested by this", + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/GrantInfo" + } + } + }, + "examples": [ + { + "name": "Default result #1", + "params": [ + { + "name": "appId", + "value": "certapp" + }, + { + "name": "permissions", + "value": [ + { + "role": "use", + "capability": "xrn:firebolt:capability:localization:postal-code" + } + ] + } + ], + "result": { + "name": "defaultResult", + "value": [ + { + "app": { + "id": "certapp", + "title": "Certification App" + }, + "state": "granted", + "capability": "xrn:firebolt:capability:localization:postal-code", + "role": "use", + "lifespan": "powerActive" + } + ] + } + }, + { + "name": "Default result #2", + "params": [ + { + "name": "appId", + "value": "certapp" + }, + { + "name": "permissions", + "value": [ + { + "role": "use", + "capability": "xrn:firebolt:capability:localization:postal-code" + } + ] + }, + { + "name": "options", + "value": { + "force": true + } + } + ], + "result": { + "name": "defaultResult", + "value": [ + { + "app": { + "id": "certapp", + "title": "Certification App" + }, + "state": "granted", + "capability": "xrn:firebolt:capability:localization:postal-code", + "role": "use", + "lifespan": "powerActive" + } + ] + } + } + ] + }, + { + "name": "VoiceGuidance.enabled", + "tags": [ + { + "name": "property" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:accessibility:voiceguidance" + ] + } + ], + "summary": "Whether or not voice-guidance is enabled.", + "params": [], + "result": { + "name": "enabled", + "schema": { + "type": "boolean" + } + }, + "examples": [ + { + "name": "Default example #1", + "params": [], + "result": { + "name": "enabled", + "value": true + } + }, + { + "name": "Default example #2", + "params": [], + "result": { + "name": "enabled", + "value": false + } + } + ] + }, + { + "name": "VoiceGuidance.speed", + "tags": [ + { + "name": "property" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:accessibility:voiceguidance" + ] + } + ], + "summary": "The speed at which voice guidance speech will be read back to the user.", + "params": [], + "result": { + "name": "speed", + "schema": { + "$ref": "#/x-schemas/Accessibility/VoiceSpeed" + } + }, + "examples": [ + { + "name": "Voice guidance speed to 1", + "params": [], + "result": { + "name": "speed", + "value": 1 + } + }, + { + "name": "Voice guidance speed to 2", + "params": [], + "result": { + "name": "speed", + "value": 2 + } + } + ] + }, + { + "name": "VoiceGuidance.onEnabledChanged", + "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "VoiceGuidance.enabled" + }, + { + "name": "event", + "x-alternative": "enabled" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:accessibility:voiceguidance" + ] + } + ], + "summary": "Whether or not voice-guidance is enabled.", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], + "result": { + "name": "enabled", + "schema": { + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "type": "boolean" + } + ] + } + }, + "examples": [ + { + "name": "Default example #1", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "enabled", + "value": true + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "enabled", + "value": false + } + } + ] + }, + { + "name": "VoiceGuidance.onSpeedChanged", + "tags": [ + { + "name": "subscriber", + "x-subscriber-for": "VoiceGuidance.speed" + }, + { + "name": "event", + "x-alternative": "speed" + }, + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:accessibility:voiceguidance" + ] + } + ], + "summary": "The speed at which voice guidance speech will be read back to the user.", + "params": [ + { + "name": "listen", + "required": true, + "schema": { + "type": "boolean" + } + } + ], + "result": { + "name": "speed", + "schema": { + "anyOf": [ + { + "$ref": "#/x-schemas/Types/ListenResponse" + }, + { + "$ref": "#/x-schemas/Accessibility/VoiceSpeed" + } + ] + } + }, + "examples": [ + { + "name": "Voice guidance speed to 1", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "speed", + "value": 1 + } + }, + { + "name": "Voice guidance speed to 2", + "params": [ + { + "name": "listen", + "value": true + } + ], + "result": { + "name": "speed", + "value": 2 + } + } + ] + }, + { + "name": "VoiceGuidance.setEnabled", + "tags": [ + { + "name": "setter", + "x-setter-for": "enabled" + }, + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:accessibility:voiceguidance" + ] + } + ], + "summary": "Whether or not voice-guidance is enabled.", + "params": [ + { + "name": "value", + "schema": { + "type": "boolean" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + } + }, + "examples": [ + { + "name": "Default example #1", + "params": [ + { + "name": "value", + "value": true + } + ], + "result": { + "name": "enabled", + "value": null + } + }, + { + "name": "Default example #2", + "params": [ + { + "name": "value", + "value": false + } + ], + "result": { + "name": "enabled", + "value": null + } + } + ] + }, + { + "name": "VoiceGuidance.setSpeed", + "tags": [ + { + "name": "setter", + "x-setter-for": "speed" + }, + { + "name": "capabilities", + "x-manages": [ + "xrn:firebolt:capability:accessibility:voiceguidance" + ] + } + ], + "summary": "The speed at which voice guidance speech will be read back to the user.", + "params": [ + { + "name": "value", + "schema": { + "$ref": "#/x-schemas/Accessibility/VoiceSpeed" + }, + "required": true + } + ], + "result": { + "name": "result", + "schema": { + "type": "null" + } + }, + "examples": [ + { + "name": "Voice guidance speed to 1", + "params": [ + { + "name": "value", + "value": 1 + } + ], + "result": { + "name": "speed", + "value": null + } + }, + { + "name": "Voice guidance speed to 2", + "params": [ + { + "name": "value", + "value": 2 + } + ], + "result": { + "name": "speed", + "value": null + } + } + ] + }, + { + "name": "Wifi.scan", + "summary": "Scan available wifi networks in the location.", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:protocol:wifi" + ] + } + ], + "params": [ + { + "name": "timeout", + "schema": { + "$ref": "#/x-schemas/Types/Timeout" + } + } + ], + "result": { + "name": "list", + "summary": "Contains a list of wifi networks available near the device.", + "schema": { + "$ref": "#/components/schemas/AccessPointList" + } + }, + "examples": [ + { + "name": "Successful Wifi List", + "params": [ + { + "name": "timeout", + "value": 30 + } + ], + "result": { + "name": "successfulWifiResultExample", + "value": { + "list": [ + { + "ssid": "DND", + "security": "wpa2Psk", + "signalStrength": -70, + "frequency": 2.4 + }, + { + "ssid": "Fortnite", + "security": "WPA2_ENTERPRISE_AES", + "signalStrength": -70, + "frequency": 5 + }, + { + "ssid": "Guardian", + "security": "none", + "signalStrength": -70, + "frequency": 2.4 + } + ] + } + } + } + ] + }, + { + "name": "Wifi.connect", + "summary": "Connect the device to the specified SSID.", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:protocol:wifi" + ] + } + ], + "params": [ + { + "name": "ssid", + "schema": { + "type": "string" + }, + "description": "Name of Wifi SSID to connect for the device." + }, + { + "name": "passphrase", + "schema": { + "type": "string" + }, + "description": "Password or Passphrase for the wifi." + }, + { + "name": "security", + "schema": { + "$ref": "#/components/schemas/WifiSecurityMode" + } + } + ], + "result": { + "name": "connectedWifi", + "summary": "Successful Response after connecting to the Wifi.", + "schema": { + "$ref": "#/components/schemas/AccessPoint" + } + }, + "examples": [ + { + "name": "Connect to a wpa2Psk Wifi with password", + "params": [ + { + "name": "ssid", + "value": "DND" + }, + { + "name": "passphrase", + "value": "gargoyle" + }, + { + "name": "security", + "value": "wpa2Psk" + } + ], + "result": { + "name": "successfulWifiConnection", + "value": { + "ssid": "DND", + "security": "wpa2Psk", + "signalStrength": -70, + "frequency": 2.4 + } + } + }, + { + "name": "Connect to a WPA2 PSK Wifi with password", + "params": [ + { + "name": "ssid", + "value": "Guardian WIFI" + }, + { + "name": "passphrase", + "value": "" + }, + { + "name": "security", + "value": "none" + } + ], + "result": { + "name": "successfulWifiConnection", + "value": { + "ssid": "Guardian WIFI", + "security": "none", + "signalStrength": -70, + "frequency": 2.4 + } + } + } + ] + }, + { + "name": "Wifi.disconnect", + "summary": "Disconnect the device if connected via WIFI.", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:protocol:wifi" + ] + } + ], + "params": [], + "result": { + "name": "result", + "schema": { + "const": null + } + }, + "examples": [ + { + "name": "Disconnect", + "params": [], + "result": { + "name": "defaultResult", + "value": null + } + } + ] + }, + { + "name": "Wifi.wps", + "summary": "Connect to WPS", + "tags": [ + { + "name": "capabilities", + "x-uses": [ + "xrn:firebolt:capability:protocol:wifi" + ] + } + ], + "params": [ + { + "name": "security", + "schema": { + "$ref": "#/components/schemas/WPSSecurityPin" + } + } + ], + "result": { + "name": "connectedWifi", + "summary": "Successful Response after connecting to the Wifi.", + "schema": { + "$ref": "#/components/schemas/AccessPoint" + } + }, + "examples": [ + { + "name": "Connect to a WPS Wifi router", + "params": [ + { + "name": "security", + "value": "pushButton" + } + ], + "result": { + "name": "successfulWifiConnection", + "value": { + "ssid": "DND", + "security": "wpa2Psk", + "signalStrength": -70, + "frequency": 2.4 + } + } + } + ] + } + ], + "components": { + "schemas": { + "AudioDescriptionSettings": { + "title": "AudioDescriptionSettings", + "type": "object", + "required": [ + "enabled" + ], + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether or not audio descriptions should be enabled by default" + } + } + }, + "Token": { + "type": "string", + "description": "Encoded token provided by the Distributor for Device Authentication." + }, + "Expiry": { + "type": "integer", + "description": "Number of secs before the token expires", + "minimum": 1 + }, + "ChallengeRequestor": { + "title": "ChallengeRequestor", "type": "object", "required": [ "id", @@ -18094,6 +19549,46 @@ } ] }, + "InterestResult": { + "title": "InterestResult", + "type": "object", + "properties": { + "appId": { + "type": "string" + }, + "entity": { + "$ref": "#/x-schemas/Entity/EntityDetails" + } + }, + "required": [ + "appId", + "entity" + ] + }, + "InterestEvent": { + "title": "InterestEvent", + "type": "object", + "properties": { + "appId": { + "type": "string" + }, + "type": { + "$ref": "#/x-schemas/Discovery/InterestType" + }, + "reason": { + "$ref": "#/x-schemas/Discovery/InterestReason" + }, + "entity": { + "$ref": "#/x-schemas/Entity/EntityDetails" + } + }, + "required": [ + "appId", + "entity", + "type", + "reason" + ] + }, "Resolution": { "type": "array", "items": [ @@ -18456,6 +19951,163 @@ "xrn:firebolt:channel:any" ] }, + "UserInterestProviderParameters": { + "title": "UserInterestProviderParameters", + "type": "object", + "required": [ + "type", + "reason" + ], + "properties": { + "type": { + "$ref": "#/x-schemas/Discovery/InterestType" + }, + "reason": { + "$ref": "#/x-schemas/Discovery/InterestReason" + } + } + }, + "HDMIPortId": { + "type": "string", + "pattern": "^HDMI[0-9]+$" + }, + "EDIDVersion": { + "title": "EDIDVersion", + "type": "string", + "enum": [ + "1.4", + "2.0", + "unknown" + ] + }, + "HDMIInputPort": { + "title": "HDMIInputPort", + "type": "object", + "additionalProperties": false, + "properties": { + "port": { + "$ref": "#/components/schemas/HDMIPortId" + }, + "connected": { + "type": "boolean" + }, + "signal": { + "$ref": "#/components/schemas/HDMISignalStatus" + }, + "arcCapable": { + "type": "boolean" + }, + "arcConnected": { + "type": "boolean" + }, + "edidVersion": { + "$ref": "#/components/schemas/EDIDVersion" + }, + "autoLowLatencyModeCapable": { + "type": "boolean" + }, + "autoLowLatencyModeSignalled": { + "type": "boolean" + } + }, + "if": { + "properties": { + "edidVersion": { + "type": "string", + "enum": [ + "1.4", + "unknown" + ] + } + } + }, + "then": { + "properties": { + "autoLowLatencyModeCapable": { + "const": false + }, + "autoLowLatencyModeSignalled": { + "const": false + } + } + }, + "required": [ + "port", + "connected", + "signal", + "arcCapable", + "arcConnected", + "edidVersion", + "autoLowLatencyModeCapable", + "autoLowLatencyModeSignalled" + ] + }, + "HDMISignalStatus": { + "type": "string", + "enum": [ + "none", + "stable", + "unstable", + "unsupported", + "unknown" + ] + }, + "SignalChangedInfo": { + "title": "SignalChangedInfo", + "type": "object", + "properties": { + "port": { + "$ref": "#/components/schemas/HDMIPortId" + }, + "signal": { + "$ref": "#/components/schemas/HDMISignalStatus" + } + }, + "required": [ + "port", + "signal" + ] + }, + "ConnectionChangedInfo": { + "title": "ConnectionChangedInfo", + "type": "object", + "properties": { + "port": { + "$ref": "#/components/schemas/HDMIPortId" + }, + "connected": { + "type": "boolean" + } + } + }, + "AutoLowLatencyModeSignalChangedInfo": { + "title": "AutoLowLatencyModeSignalChangedInfo", + "type": "object", + "properties": { + "port": { + "$ref": "#/components/schemas/HDMIPortId" + }, + "autoLowLatencyModeSignalled": { + "type": "boolean" + } + } + }, + "AutoLowLatencyModeCapableChangedInfo": { + "title": "AutoLowLatencyModeCapableChangedInfo", + "type": "object", + "properties": { + "port": { + "$ref": "#/components/schemas/HDMIPortId" + }, + "enabled": { + "type": "boolean" + } + }, + "required": [ + "port", + "enabled" + ] + }, "EmailUsage": { "title": "EmailUsage", "type": "string", @@ -18511,23 +20163,6 @@ } } }, - "KeyboardResult": { - "title": "KeyboardResult", - "type": "object", - "required": [ - "text" - ], - "properties": { - "text": { - "type": "string", - "description": "The text the user entered into the keyboard" - }, - "canceled": { - "type": "boolean", - "description": "Whether the user canceled entering text before they were finished typing on the keyboard" - } - } - }, "LifecycleEvent": { "title": "LifecycleEvent", "description": "A an object describing the previous and current states", @@ -19123,23 +20758,6 @@ }, "additionalProperties": false }, - "ProviderResponse": { - "title": "ProviderResponse", - "type": "object", - "required": [ - "correlationId" - ], - "additionalProperties": false, - "properties": { - "correlationId": { - "type": "string", - "description": "The id that was passed in to the event that triggered a provider method to be called" - }, - "result": { - "description": "The result of the provider response." - } - } - }, "ProviderRequest": { "title": "ProviderRequest", "type": "object", @@ -19161,13 +20779,6 @@ } } }, - "BooleanMap": { - "title": "BooleanMap", - "type": "object", - "additionalProperties": { - "type": "boolean" - } - }, "AudioProfile": { "title": "AudioProfile", "type": "string", @@ -19180,6 +20791,13 @@ "dolbyAtmos" ] }, + "BooleanMap": { + "title": "BooleanMap", + "type": "object", + "additionalProperties": { + "type": "boolean" + } + }, "LocalizedString": { "title": "LocalizedString", "description": "Localized string supports either a simple `string` or a Map of language codes to strings. When using a simple `string`, the current preferred langauge from `Localization.langauge()` is assumed.", @@ -19587,6 +21205,23 @@ }, "Discovery": { "uri": "https://meta.comcast.com/firebolt/discovery", + "InterestType": { + "title": "InterestType", + "type": "string", + "enum": [ + "interest", + "disinterest" + ] + }, + "InterestReason": { + "title": "InterestReason", + "type": "string", + "enum": [ + "playlist", + "reaction", + "recording" + ] + }, "EntityInfoResult": { "title": "EntityInfoResult", "description": "The result for an `entityInfo()` push or pull.", @@ -19639,229 +21274,450 @@ "additionalProperties": false } }, - "Entertainment": { - "uri": "https://meta.comcast.com/firebolt/entertainment", - "ContentIdentifiers": { - "title": "ContentIdentifiers", + "Entity": { + "uri": "https://meta.comcast.com/firebolt/entity", + "EntityDetails": { + "title": "EntityDetails", + "type": "object", + "required": [ + "identifiers" + ], + "properties": { + "identifiers": { + "$ref": "#/x-schemas/Entity/Entity" + }, + "info": { + "$ref": "#/x-schemas/Entity/Metadata" + }, + "waysToWatch": { + "type": "array", + "items": { + "$ref": "#/x-schemas/Entertainment/WayToWatch" + }, + "description": "An array of ways a user is might watch this entity, regardless of entitlements." + } + } + }, + "Entity": { + "oneOf": [ + { + "$ref": "#/x-schemas/Entity/ProgramEntity" + }, + { + "$ref": "#/x-schemas/Entity/MusicEntity" + }, + { + "$ref": "#/x-schemas/Entity/ChannelEntity" + }, + { + "$ref": "#/x-schemas/Entity/UntypedEntity" + }, + { + "$ref": "#/x-schemas/Entity/PlaylistEntity" + } + ] + }, + "Metadata": { + "title": "Metadata", + "type": "object", + "properties": { + "title": { + "type": "string", + "description": "Title of the entity." + }, + "synopsis": { + "type": "string", + "description": "Short description of the entity." + }, + "seasonNumber": { + "type": "number", + "description": "For TV seasons, the season number. For TV episodes, the season that the episode belongs to." + }, + "seasonCount": { + "type": "number", + "description": "For TV series, seasons, and episodes, the total number of seasons." + }, + "episodeNumber": { + "type": "number", + "description": "For TV episodes, the episode number." + }, + "episodeCount": { + "type": "number", + "description": "For TV seasons and episodes, the total number of episodes in the current season." + }, + "releaseDate": { + "type": "string", + "format": "date-time", + "description": "The date that the program or entity was released or first aired." + }, + "contentRatings": { + "type": "array", + "items": { + "$ref": "#/x-schemas/Entertainment/ContentRating" + }, + "description": "A list of ContentRating objects, describing the entity's ratings in various rating schemes." + } + } + }, + "ProgramEntity": { + "title": "ProgramEntity", + "oneOf": [ + { + "$ref": "#/x-schemas/Entity/MovieEntity" + }, + { + "$ref": "#/x-schemas/Entity/TVEpisodeEntity" + }, + { + "$ref": "#/x-schemas/Entity/TVSeasonEntity" + }, + { + "$ref": "#/x-schemas/Entity/TVSeriesEntity" + }, + { + "$ref": "#/x-schemas/Entity/AdditionalEntity" + } + ] + }, + "MusicEntity": { + "title": "MusicEntity", + "type": "object", + "properties": { + "entityType": { + "const": "music" + }, + "musicType": { + "$ref": "#/x-schemas/Entertainment/MusicType" + }, + "entityId": { + "type": "string" + } + }, + "required": [ + "entityType", + "musicType", + "entityId" + ] + }, + "ChannelEntity": { + "title": "ChannelEntity", + "type": "object", + "properties": { + "entityType": { + "const": "channel" + }, + "channelType": { + "type": "string", + "enum": [ + "streaming", + "overTheAir" + ] + }, + "entityId": { + "type": "string", + "description": "ID of the channel, in the target App's scope." + }, + "appContentData": { + "type": "string", + "maxLength": 256 + } + }, + "required": [ + "entityType", + "channelType", + "entityId" + ], + "additionalProperties": false + }, + "UntypedEntity": { + "title": "UntypedEntity", + "allOf": [ + { + "description": "A Firebolt compliant representation of the remaining entity types.", + "type": "object", + "required": [ + "entityId" + ], + "properties": { + "entityId": { + "type": "string" + }, + "assetId": { + "type": "string" + }, + "appContentData": { + "type": "string", + "maxLength": 256 + } + }, + "additionalProperties": false + } + ], + "examples": [ + { + "entityId": "an-entity" + } + ] + }, + "PlaylistEntity": { + "title": "PlaylistEntity", + "description": "A Firebolt compliant representation of a Playlist entity.", + "type": "object", + "required": [ + "entityType", + "entityId" + ], + "properties": { + "entityType": { + "const": "playlist" + }, + "entityId": { + "type": "string" + }, + "assetId": { + "type": "string" + }, + "appContentData": { + "type": "string", + "maxLength": 256 + } + }, + "additionalProperties": false, + "examples": [ + { + "entityType": "playlist", + "entityId": "playlist/xyz" + } + ] + }, + "MovieEntity": { + "title": "MovieEntity", + "description": "A Firebolt compliant representation of a Movie entity.", "type": "object", + "required": [ + "entityType", + "programType", + "entityId" + ], "properties": { + "entityType": { + "const": "program" + }, + "programType": { + "const": "movie" + }, + "entityId": { + "type": "string" + }, "assetId": { + "type": "string" + }, + "appContentData": { "type": "string", - "description": "Identifies a particular playable asset. For example, the HD version of a particular movie separate from the UHD version." + "maxLength": 256 + } + }, + "additionalProperties": false, + "examples": [ + { + "entityType": "program", + "programType": "movie", + "entityId": "el-camino" + } + ] + }, + "TVEpisodeEntity": { + "title": "TVEpisodeEntity", + "description": "A Firebolt compliant representation of a TV Episode entity.", + "type": "object", + "required": [ + "entityType", + "programType", + "entityId", + "seriesId", + "seasonId" + ], + "properties": { + "entityType": { + "const": "program" + }, + "programType": { + "const": "episode" + }, + "entityId": { + "type": "string" }, - "entityId": { - "type": "string", - "description": "Identifies an entity, such as a Movie, TV Series or TV Episode." + "seriesId": { + "type": "string" }, "seasonId": { - "type": "string", - "description": "The TV Season for a TV Episode." + "type": "string" }, - "seriesId": { - "type": "string", - "description": "The TV Series for a TV Episode or TV Season." + "assetId": { + "type": "string" }, "appContentData": { "type": "string", - "description": "App-specific content identifiers.", - "maxLength": 1024 + "maxLength": 256 } }, - "description": "The ContentIdentifiers object is how the app identifies an entity or asset to\nthe Firebolt platform. These ids are used to look up metadata and deep link into\nthe app.\n\nApps do not need to provide all ids. They only need to provide the minimum\nrequired to target a playable stream or an entity detail screen via a deep link.\nIf an id isn't needed to get to those pages, it doesn't need to be included." + "additionalProperties": false, + "examples": [ + { + "entityType": "program", + "programType": "episode", + "entityId": "breaking-bad-pilot", + "seriesId": "breaking-bad", + "seasonId": "breaking-bad-season-1" + } + ] }, - "Entitlement": { - "title": "Entitlement", + "TVSeasonEntity": { + "title": "TVSeasonEntity", + "description": "A Firebolt compliant representation of a TV Season entity.", "type": "object", + "required": [ + "entityType", + "programType", + "entityId", + "seriesId" + ], "properties": { - "entitlementId": { + "entityType": { + "const": "program" + }, + "programType": { + "const": "season" + }, + "entityId": { "type": "string" }, - "startTime": { - "type": "string", - "format": "date-time" + "seriesId": { + "type": "string" }, - "endTime": { + "assetId": { + "type": "string" + }, + "appContentData": { "type": "string", - "format": "date-time" + "maxLength": 256 } }, - "required": [ - "entitlementId" + "additionalProperties": false, + "examples": [ + { + "entityType": "program", + "programType": "season", + "entityId": "breaking-bad-season-1", + "seriesId": "breaking-bad" + } ] }, - "EntityInfo": { - "title": "EntityInfo", - "description": "An EntityInfo object represents an \"entity\" on the platform. Currently, only entities of type `program` are supported. `programType` must be supplied to identify the program type.\n\nAdditionally, EntityInfo objects must specify a properly formed\nContentIdentifiers object, `entityType`, and `title`. The app should provide\nthe `synopsis` property for a good user experience if the content\nmetadata is not available another way.\n\nThe ContentIdentifiers must be sufficient for navigating the user to the\nappropriate entity or detail screen via a `detail` intent or deep link.\n\nEntityInfo objects must provide at least one WayToWatch object when returned as\npart of an `entityInfo` method and a streamable asset is available to the user.\nIt is optional for the `purchasedContent` method, but recommended because the UI\nmay use those data.", + "TVSeriesEntity": { + "title": "TVSeriesEntity", + "description": "A Firebolt compliant representation of a TV Series entity.", "type": "object", "required": [ - "identifiers", "entityType", "programType", - "title" + "entityId" ], "properties": { - "identifiers": { - "$ref": "#/x-schemas/Entertainment/ContentIdentifiers" - }, - "title": { - "type": "string", - "description": "Title of the entity." - }, "entityType": { - "type": "string", - "enum": [ - "program", - "music" - ], - "description": "The type of the entity, e.g. `program` or `music`." + "const": "program" }, "programType": { - "$ref": "#/x-schemas/Entertainment/ProgramType" - }, - "musicType": { - "$ref": "#/x-schemas/Entertainment/MusicType" - }, - "synopsis": { - "type": "string", - "description": "Short description of the entity." - }, - "seasonNumber": { - "type": "number", - "description": "For TV seasons, the season number. For TV episodes, the season that the episode belongs to." - }, - "seasonCount": { - "type": "number", - "description": "For TV series, seasons, and episodes, the total number of seasons." + "const": "series" }, - "episodeNumber": { - "type": "number", - "description": "For TV episodes, the episode number." + "entityId": { + "type": "string" }, - "episodeCount": { - "type": "number", - "description": "For TV seasons and episodes, the total number of episodes in the current season." + "assetId": { + "type": "string" }, - "releaseDate": { + "appContentData": { "type": "string", - "format": "date-time", - "description": "The date that the program or entity was released or first aired." - }, - "contentRatings": { - "type": "array", - "items": { - "$ref": "#/x-schemas/Entertainment/ContentRating" - }, - "description": "A list of ContentRating objects, describing the entity's ratings in various rating schemes." - }, - "waysToWatch": { - "type": "array", - "items": { - "$ref": "#/x-schemas/Entertainment/WayToWatch" - }, - "description": "An array of ways a user is might watch this entity, regardless of entitlements." - } - }, - "if": { - "properties": { - "entityType": { - "const": "program" - } - } - }, - "then": { - "required": [ - "programType" - ], - "not": { - "required": [ - "musicType" - ] + "maxLength": 256 } }, - "else": { - "required": [ - "musicType" - ], - "not": { - "required": [ - "programType" - ] + "additionalProperties": false, + "examples": [ + { + "entityType": "program", + "programType": "series", + "entityId": "breaking-bad" } - } - }, - "OfferingType": { - "title": "OfferingType", - "type": "string", - "enum": [ - "free", - "subscribe", - "buy", - "rent" - ], - "description": "The offering type of the WayToWatch." - }, - "ProgramType": { - "title": "ProgramType", - "type": "string", - "description": "In the case of a program `entityType`, specifies the program type.", - "enum": [ - "movie", - "episode", - "season", - "series", - "other", - "preview", - "extra", - "concert", - "sportingEvent", - "advertisement", - "musicVideo", - "minisode" - ] - }, - "MusicType": { - "title": "MusicType", - "type": "string", - "description": "In the case of a music `entityType`, specifies the type of music entity.", - "enum": [ - "song", - "album" ] }, - "ContentRating": { - "title": "ContentRating", + "AdditionalEntity": { + "title": "AdditionalEntity", + "description": "A Firebolt compliant representation of the remaining program entity types.", "type": "object", "required": [ - "scheme", - "rating" + "entityType", + "programType", + "entityId" ], "properties": { - "scheme": { + "entityType": { + "const": "program" + }, + "programType": { "type": "string", "enum": [ - "CA-Movie", - "CA-TV", - "CA-Movie-Fr", - "CA-TV-Fr", - "US-Movie", - "US-TV" - ], - "description": "The rating scheme." + "concert", + "sportingEvent", + "preview", + "other", + "advertisement", + "musicVideo", + "minisode", + "extra" + ] }, - "rating": { + "entityId": { + "type": "string" + }, + "assetId": { + "type": "string" + }, + "appContentData": { "type": "string", - "description": "The content rating." + "maxLength": 256 + } + }, + "additionalProperties": false, + "examples": [ + { + "entityType": "program", + "programType": "concert", + "entityId": "live-aid" + } + ] + }, + "PlayableEntity": { + "title": "PlayableEntity", + "anyOf": [ + { + "$ref": "#/x-schemas/Entity/MovieEntity" + }, + { + "$ref": "#/x-schemas/Entity/TVEpisodeEntity" + }, + { + "$ref": "#/x-schemas/Entity/PlaylistEntity" + }, + { + "$ref": "#/x-schemas/Entity/MusicEntity" }, - "advisories": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Optional list of subratings or content advisories." + { + "$ref": "#/x-schemas/Entity/AdditionalEntity" } - }, - "description": "A ContentRating represents an age or content based of an entity. Supported rating schemes and associated types are below.\n\n## United States\n\n`US-Movie` (MPAA):\n\nRatings: `NR`, `G`, `PG`, `PG13`, `R`, `NC17`\n\nAdvisories: `AT`, `BN`, `SL`, `SS`, `N`, `V`\n\n`US-TV` (Vchip):\n\nRatings: `TVY`, `TVY7`, `TVG`, `TVPG`, `TV14`, `TVMA`\n\nAdvisories: `FV`, `D`, `L`, `S`, `V`\n\n## Canada\n\n`CA-Movie` (OFRB):\n\nRatings: `G`, `PG`, `14A`, `18A`, `R`, `E`\n\n`CA-TV` (AGVOT)\n\nRatings: `E`, `C`, `C8`, `G`, `PG`, `14+`, `18+`\n\nAdvisories: `C`, `C8`, `G`, `PG`, `14+`, `18+`\n\n`CA-Movie-Fr` (Canadian French language movies):\n\nRatings: `G`, `8+`, `13+`, `16+`, `18+`\n\n`CA-TV-Fr` (Canadian French language TV):\n\nRatings: `G`, `8+`, `13+`, `16+`, `18+`\n" - }, + ] + } + }, + "Entertainment": { + "uri": "https://meta.comcast.com/firebolt/entertainment", "WayToWatch": { "title": "WayToWatch", "type": "object", @@ -19947,366 +21803,302 @@ } }, "description": "A WayToWatch describes a way to watch a video program. It may describe a single\nstreamable asset or a set of streamable assets. For example, an app provider may\ndescribe HD, SD, and UHD assets as individual WayToWatch objects or rolled into\na single WayToWatch.\n\nIf the WayToWatch represents a single streamable asset, the provided\nContentIdentifiers must be sufficient to play back the specific asset when sent\nvia a playback intent or deep link. If the WayToWatch represents multiple\nstreamable assets, the provided ContentIdentifiers must be sufficient to\nplayback one of the assets represented with no user action. In this scenario,\nthe app SHOULD choose the best asset for the user based on their device and\nsettings. The ContentIdentifiers MUST also be sufficient for navigating the user\nto the appropriate entity or detail screen via an entity intent.\n\nThe app should set the `entitled` property to indicate if the user can watch, or\nnot watch, the asset without making a purchase. If the entitlement is known to\nexpire at a certain time (e.g., a rental), the app should also provide the\n`entitledExpires` property. If the entitlement is not expired, the UI will use\nthe `entitled` property to display watchable assets to the user, adjust how\nassets are presented to the user, and how intents into the app are generated.\nFor example, the the Aggregated Experience could render a \"Watch\" button for an\nentitled asset versus a \"Subscribe\" button for an non-entitled asset.\n\nThe app should set the `offeringType` to define how the content may be\nauthorized. The UI will use this to adjust how content is presented to the user.\n\nA single WayToWatch cannot represent streamable assets available via multiple\npurchase paths. If, for example, an asset has both Buy, Rent and Subscription\navailability, the three different entitlement paths MUST be represented as\nmultiple WayToWatch objects.\n\n`price` should be populated for WayToWatch objects with `buy` or `rent`\n`offeringType`. If the WayToWatch represents a set of assets with various price\npoints, the `price` provided must be the lowest available price." - } - }, - "Intents": { - "uri": "https://meta.comcast.com/firebolt/intents", - "NavigationIntent": { - "title": "NavigationIntent", - "description": "A Firebolt compliant representation of a user intention to navigate to a specific place in an app.", - "anyOf": [ - { - "$ref": "#/x-schemas/Intents/HomeIntent" - }, - { - "$ref": "#/x-schemas/Intents/LaunchIntent" - }, - { - "$ref": "#/x-schemas/Intents/EntityIntent" - }, - { - "$ref": "#/x-schemas/Intents/PlaybackIntent" - }, - { - "$ref": "#/x-schemas/Intents/SearchIntent" + }, + "OfferingType": { + "title": "OfferingType", + "type": "string", + "enum": [ + "free", + "subscribe", + "buy", + "rent" + ], + "description": "The offering type of the WayToWatch." + }, + "ContentIdentifiers": { + "title": "ContentIdentifiers", + "type": "object", + "properties": { + "assetId": { + "type": "string", + "description": "Identifies a particular playable asset. For example, the HD version of a particular movie separate from the UHD version." }, - { - "$ref": "#/x-schemas/Intents/SectionIntent" + "entityId": { + "type": "string", + "description": "Identifies an entity, such as a Movie, TV Series or TV Episode." }, - { - "$ref": "#/x-schemas/Intents/TuneIntent" + "seasonId": { + "type": "string", + "description": "The TV Season for a TV Episode." }, - { - "$ref": "#/x-schemas/Intents/PlayEntityIntent" + "seriesId": { + "type": "string", + "description": "The TV Series for a TV Episode or TV Season." }, - { - "$ref": "#/x-schemas/Intents/PlayQueryIntent" + "appContentData": { + "type": "string", + "description": "App-specific content identifiers.", + "maxLength": 1024 } - ] + }, + "description": "The ContentIdentifiers object is how the app identifies an entity or asset to\nthe Firebolt platform. These ids are used to look up metadata and deep link into\nthe app.\n\nApps do not need to provide all ids. They only need to provide the minimum\nrequired to target a playable stream or an entity detail screen via a deep link.\nIf an id isn't needed to get to those pages, it doesn't need to be included." }, - "HomeIntent": { - "description": "A Firebolt compliant representation of a user intention to navigate an app to it's home screen, and bring that app to the foreground if needed.", - "title": "HomeIntent", - "allOf": [ - { - "title": "HomeIntent", - "$ref": "#/x-schemas/Intents/Intent" + "ContentRating": { + "title": "ContentRating", + "type": "object", + "required": [ + "scheme", + "rating" + ], + "properties": { + "scheme": { + "type": "string", + "enum": [ + "CA-Movie", + "CA-TV", + "CA-Movie-Fr", + "CA-TV-Fr", + "US-Movie", + "US-TV" + ], + "description": "The rating scheme." }, - { - "title": "HomeIntent", - "$ref": "#/x-schemas/Intents/IntentProperties" + "rating": { + "type": "string", + "description": "The content rating." }, - { - "title": "HomeIntent", - "type": "object", - "properties": { - "action": { - "const": "home" - } - } - } - ], - "examples": [ - { - "action": "home", - "context": { - "source": "voice" - } + "advisories": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Optional list of subratings or content advisories." } + }, + "description": "A ContentRating represents an age or content based of an entity. Supported rating schemes and associated types are below.\n\n## United States\n\n`US-Movie` (MPAA):\n\nRatings: `NR`, `G`, `PG`, `PG13`, `R`, `NC17`\n\nAdvisories: `AT`, `BN`, `SL`, `SS`, `N`, `V`\n\n`US-TV` (Vchip):\n\nRatings: `TVY`, `TVY7`, `TVG`, `TVPG`, `TV14`, `TVMA`\n\nAdvisories: `FV`, `D`, `L`, `S`, `V`\n\n## Canada\n\n`CA-Movie` (OFRB):\n\nRatings: `G`, `PG`, `14A`, `18A`, `R`, `E`\n\n`CA-TV` (AGVOT)\n\nRatings: `E`, `C`, `C8`, `G`, `PG`, `14+`, `18+`\n\nAdvisories: `C`, `C8`, `G`, `PG`, `14+`, `18+`\n\n`CA-Movie-Fr` (Canadian French language movies):\n\nRatings: `G`, `8+`, `13+`, `16+`, `18+`\n\n`CA-TV-Fr` (Canadian French language TV):\n\nRatings: `G`, `8+`, `13+`, `16+`, `18+`\n" + }, + "MusicType": { + "title": "MusicType", + "type": "string", + "description": "In the case of a music `entityType`, specifies the type of music entity.", + "enum": [ + "song", + "album" ] }, - "LaunchIntent": { - "description": "A Firebolt compliant representation of a user intention to launch an app.", - "title": "LaunchIntent", - "allOf": [ - { - "$ref": "#/x-schemas/Intents/Intent" + "Entitlement": { + "title": "Entitlement", + "type": "object", + "properties": { + "entitlementId": { + "type": "string" }, - { - "$ref": "#/x-schemas/Intents/IntentProperties" + "startTime": { + "type": "string", + "format": "date-time" }, - { - "type": "object", - "properties": { - "action": { - "const": "launch" - } - } - } - ], - "examples": [ - { - "action": "launch", - "context": { - "source": "voice" - } + "endTime": { + "type": "string", + "format": "date-time" } + }, + "required": [ + "entitlementId" ] }, - "EntityIntent": { - "description": "A Firebolt compliant representation of a user intention to navigate an app to a specific entity page, and bring that app to the foreground if needed.", - "title": "EntityIntent", - "allOf": [ - { - "$ref": "#/x-schemas/Intents/Intent" + "EntityInfo": { + "title": "EntityInfo", + "description": "An EntityInfo object represents an \"entity\" on the platform. Currently, only entities of type `program` are supported. `programType` must be supplied to identify the program type.\n\nAdditionally, EntityInfo objects must specify a properly formed\nContentIdentifiers object, `entityType`, and `title`. The app should provide\nthe `synopsis` property for a good user experience if the content\nmetadata is not available another way.\n\nThe ContentIdentifiers must be sufficient for navigating the user to the\nappropriate entity or detail screen via a `detail` intent or deep link.\n\nEntityInfo objects must provide at least one WayToWatch object when returned as\npart of an `entityInfo` method and a streamable asset is available to the user.\nIt is optional for the `purchasedContent` method, but recommended because the UI\nmay use those data.", + "type": "object", + "required": [ + "identifiers", + "entityType", + "title" + ], + "properties": { + "identifiers": { + "$ref": "#/x-schemas/Entertainment/ContentIdentifiers" }, - { - "$ref": "#/x-schemas/Intents/IntentProperties" + "title": { + "type": "string", + "description": "Title of the entity." }, - { - "type": "object", - "required": [ - "data" + "entityType": { + "type": "string", + "enum": [ + "program", + "music" ], - "properties": { - "action": { - "const": "entity" - }, - "data": { - "anyOf": [ - { - "$ref": "#/x-schemas/Intents/MovieEntity" - }, - { - "$ref": "#/x-schemas/Intents/TVEpisodeEntity" - }, - { - "$ref": "#/x-schemas/Intents/TVSeriesEntity" - }, - { - "$ref": "#/x-schemas/Intents/TVSeasonEntity" - }, - { - "$ref": "#/x-schemas/Intents/AdditionalEntity" - }, - { - "$ref": "#/x-schemas/Intents/UntypedEntity" - } - ] - } - } - } - ], - "examples": [ - { - "action": "entity", - "context": { - "source": "voice" + "description": "The type of the entity, e.g. `program` or `music`." + }, + "programType": { + "$ref": "#/x-schemas/Entertainment/ProgramType" + }, + "musicType": { + "$ref": "#/x-schemas/Entertainment/MusicType" + }, + "synopsis": { + "type": "string", + "description": "Short description of the entity." + }, + "seasonNumber": { + "type": "number", + "description": "For TV seasons, the season number. For TV episodes, the season that the episode belongs to." + }, + "seasonCount": { + "type": "number", + "description": "For TV series, seasons, and episodes, the total number of seasons." + }, + "episodeNumber": { + "type": "number", + "description": "For TV episodes, the episode number." + }, + "episodeCount": { + "type": "number", + "description": "For TV seasons and episodes, the total number of episodes in the current season." + }, + "releaseDate": { + "type": "string", + "format": "date-time", + "description": "The date that the program or entity was released or first aired." + }, + "contentRatings": { + "type": "array", + "items": { + "$ref": "#/x-schemas/Entertainment/ContentRating" }, - "data": { - "entityType": "program", - "programType": "movie", - "entityId": "el-camino" + "description": "A list of ContentRating objects, describing the entity's ratings in various rating schemes." + }, + "waysToWatch": { + "type": "array", + "items": { + "$ref": "#/x-schemas/Entertainment/WayToWatch" + }, + "description": "An array of ways a user is might watch this entity, regardless of entitlements." + } + }, + "if": { + "properties": { + "entityType": { + "const": "program" } } - ] - }, - "PlaybackIntent": { - "description": "A Firebolt compliant representation of a user intention to navigate an app to a the video player for a specific, playable entity, and bring that app to the foreground if needed.", - "title": "PlaybackIntent", - "allOf": [ - { - "$ref": "#/x-schemas/Intents/Intent" - }, - { - "$ref": "#/x-schemas/Intents/IntentProperties" - }, - { - "type": "object", + }, + "then": { + "required": [ + "programType" + ], + "not": { "required": [ - "data" - ], - "properties": { - "action": { - "const": "playback" - }, - "data": { - "$ref": "#/x-schemas/Intents/PlayableEntity" - } - } + "musicType" + ] } - ], - "examples": [ - { - "action": "playback", - "data": { - "entityType": "program", - "programType": "episode", - "entityId": "breaking-bad-pilot", - "seriesId": "breaking-bad", - "seasonId": "breaking-bad-season-1" - }, - "context": { - "source": "voice" - } + }, + "else": { + "required": [ + "musicType" + ], + "not": { + "required": [ + "programType" + ] } - ] + } }, - "SearchIntent": { - "description": "A Firebolt compliant representation of a user intention to navigate an app to it's search UI with a search term populated, and bring that app to the foreground if needed.", - "title": "SearchIntent", - "allOf": [ + "ProgramType": { + "title": "ProgramType", + "type": "string", + "description": "In the case of a program `entityType`, specifies the program type.", + "enum": [ + "movie", + "episode", + "season", + "series", + "other", + "preview", + "extra", + "concert", + "sportingEvent", + "advertisement", + "musicVideo", + "minisode" + ] + } + }, + "Intents": { + "uri": "https://meta.comcast.com/firebolt/intents", + "NavigationIntent": { + "title": "NavigationIntent", + "description": "A Firebolt compliant representation of a user intention to navigate to a specific place in an app.", + "anyOf": [ { - "$ref": "#/x-schemas/Intents/Intent" + "$ref": "#/x-schemas/Intents/HomeIntent" }, { - "$ref": "#/x-schemas/Intents/IntentProperties" + "$ref": "#/x-schemas/Intents/LaunchIntent" }, { - "type": "object", - "properties": { - "action": { - "const": "search" - }, - "data": { - "type": "object", - "required": [ - "query" - ], - "properties": { - "query": { - "type": "string" - } - }, - "additionalProperties": false - } - } - } - ], - "examples": [ + "$ref": "#/x-schemas/Intents/EntityIntent" + }, { - "action": "search", - "data": { - "query": "walter white" - }, - "context": { - "source": "voice" - } - } - ] - }, - "SectionIntent": { - "description": "A Firebolt compliant representation of a user intention to navigate an app to a section not covered by `home`, `entity`, `player`, or `search`, and bring that app to the foreground if needed.", - "title": "SectionIntent", - "allOf": [ + "$ref": "#/x-schemas/Intents/PlaybackIntent" + }, { - "$ref": "#/x-schemas/Intents/Intent" + "$ref": "#/x-schemas/Intents/SearchIntent" }, { - "$ref": "#/x-schemas/Intents/IntentProperties" + "$ref": "#/x-schemas/Intents/SectionIntent" }, { - "type": "object", - "properties": { - "action": { - "const": "section" - }, - "data": { - "type": "object", - "required": [ - "sectionName" - ], - "properties": { - "sectionName": { - "type": "string" - } - }, - "additionalProperties": false - } - } - } - ], - "examples": [ + "$ref": "#/x-schemas/Intents/TuneIntent" + }, { - "action": "section", - "data": { - "sectionName": "settings" - }, - "context": { - "source": "voice" - } + "$ref": "#/x-schemas/Intents/PlayEntityIntent" + }, + { + "$ref": "#/x-schemas/Intents/PlayQueryIntent" } ] }, - "TuneIntent": { - "description": "A Firebolt compliant representation of a user intention to 'tune' to a traditional over-the-air broadcast, or an OTT Stream from an OTT or vMVPD App.", - "title": "TuneIntent", + "HomeIntent": { + "description": "A Firebolt compliant representation of a user intention to navigate an app to it's home screen, and bring that app to the foreground if needed.", + "title": "HomeIntent", "allOf": [ { + "title": "HomeIntent", "$ref": "#/x-schemas/Intents/Intent" }, { + "title": "HomeIntent", "$ref": "#/x-schemas/Intents/IntentProperties" }, { + "title": "HomeIntent", "type": "object", - "required": [ - "data" - ], "properties": { "action": { - "const": "tune" - }, - "data": { - "type": "object", - "required": [ - "entity" - ], - "additionalProperties": false, - "properties": { - "entity": { - "$ref": "#/x-schemas/Intents/ChannelEntity" - }, - "options": { - "description": "The options property of the data property MUST have only one of the following fields.", - "type": "object", - "required": [], - "additionalProperties": false, - "minProperties": 1, - "maxProperties": 1, - "properties": { - "assetId": { - "type": "string", - "description": "The ID of a specific 'listing', as scoped by the target App's ID-space, which the App should begin playback from." - }, - "restartCurrentProgram": { - "type": "boolean", - "description": "Denotes that the App should start playback at the most recent program boundary, rather than 'live.'" - }, - "time": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 Date/Time where the App should begin playback from." - } - } - } - } + "const": "home" } + }, + "not": { + "required": [ + "data" + ] } } ], "examples": [ { - "action": "tune", - "data": { - "entity": { - "entityType": "channel", - "channelType": "streaming", - "entityId": "an-ott-channel" - }, - "options": { - "restartCurrentProgram": true - } - }, + "action": "home", "context": { "source": "voice" } } ] }, - "PlayEntityIntent": { - "description": "A Firebolt compliant representation of a user intention to navigate an app to a the video player for a specific, playable entity, and bring that app to the foreground if needed.", - "title": "PlayEntityIntent", + "LaunchIntent": { + "description": "A Firebolt compliant representation of a user intention to launch an app.", + "title": "LaunchIntent", "allOf": [ { "$ref": "#/x-schemas/Intents/Intent" @@ -20316,113 +22108,30 @@ }, { "type": "object", - "required": [ - "data" - ], "properties": { "action": { - "const": "play-entity" - }, - "data": { - "type": "object", - "properties": { - "entity": { - "$ref": "#/x-schemas/Intents/PlayableEntity" - }, - "options": { - "type": "object", - "properties": { - "playFirstId": { - "type": "string" - }, - "playFirstTrack": { - "type": "integer", - "minimum": 1 - } - }, - "additionalProperties": false - } - }, - "required": [ - "entity" - ], - "propertyNames": { - "enum": [ - "entity", - "options" - ] - }, - "if": { - "properties": { - "entity": { - "type": "object", - "required": [ - "entityType" - ], - "properties": { - "entityType": { - "const": "playlist" - } - } - } - } - }, - "then": { - "type": "object", - "properties": { - "options": { - "maxProperties": 1 - } - } - }, - "else": { - "type": "object", - "properties": { - "options": { - "maxProperties": 0 - } - } - } + "const": "launch" } + }, + "not": { + "required": [ + "data" + ] } } ], "examples": [ { - "action": "play-entity", - "data": { - "entity": { - "entityType": "playlist", - "entityId": "playlist/xyz" - }, - "options": { - "playFirstId": "song/xyz" - } - }, - "context": { - "source": "voice" - } - }, - { - "action": "play-entity", - "data": { - "entity": { - "entityType": "playlist", - "entityId": "playlist/xyz" - }, - "options": { - "playFirstTrack": 3 - } - }, + "action": "launch", "context": { "source": "voice" } } ] }, - "PlayQueryIntent": { - "description": "A Firebolt compliant representation of a user intention to navigate an app to a the video player for an abstract query to be searched for and played by the app.", - "title": "PlayQueryIntent", + "EntityIntent": { + "description": "A Firebolt compliant representation of a user intention to navigate an app to a specific entity page, and bring that app to the foreground if needed.", + "title": "EntityIntent", "allOf": [ { "$ref": "#/x-schemas/Intents/Intent" @@ -20437,494 +22146,485 @@ ], "properties": { "action": { - "const": "play-query" + "const": "entity" }, "data": { - "type": "object", - "properties": { - "query": { - "type": "string" - }, - "options": { - "type": "object", - "properties": { - "programTypes": { - "type": "array", - "items": { - "$ref": "#/x-schemas/Entertainment/ProgramType" - } - }, - "musicTypes": { - "type": "array", - "items": { - "$ref": "#/x-schemas/Entertainment/MusicType" - } - } - }, - "additionalProperties": false - } - }, - "required": [ - "query" - ], - "propertyNames": { - "enum": [ - "query", - "options" - ] - } + "$ref": "#/x-schemas/Entity/Entity" } } } ], "examples": [ { - "action": "play-query", - "data": { - "query": "Ed Sheeran" - }, + "action": "entity", "context": { "source": "voice" - } - }, - { - "action": "play-query", - "data": { - "query": "Ed Sheeran", - "options": { - "programTypes": [ - "movie" - ] - } }, - "context": { - "source": "voice" - } - }, - { - "action": "play-query", "data": { - "query": "Ed Sheeran", - "options": { - "programTypes": [ - "movie" - ], - "musicTypes": [ - "song" - ] - } - }, - "context": { - "source": "voice" + "entityType": "program", + "programType": "movie", + "entityId": "el-camino" } } ] }, - "Intent": { - "description": "A Firebolt compliant representation of a user intention.", - "type": "object", - "required": [ - "action", - "context" - ], - "properties": { - "action": { - "type": "string" + "PlaybackIntent": { + "description": "A Firebolt compliant representation of a user intention to navigate an app to a the video player for a specific, playable entity, and bring that app to the foreground if needed.", + "title": "PlaybackIntent", + "allOf": [ + { + "$ref": "#/x-schemas/Intents/Intent" }, - "context": { + { + "$ref": "#/x-schemas/Intents/IntentProperties" + }, + { "type": "object", "required": [ - "source" + "data" ], "properties": { - "source": { - "type": "string" + "action": { + "const": "playback" + }, + "data": { + "$ref": "#/x-schemas/Entity/PlayableEntity" } } } - } - }, - "IntentProperties": { - "type": "object", - "propertyNames": { - "enum": [ - "action", - "data", - "context" - ] - } + ], + "examples": [ + { + "action": "playback", + "data": { + "entityType": "program", + "programType": "episode", + "entityId": "breaking-bad-pilot", + "seriesId": "breaking-bad", + "seasonId": "breaking-bad-season-1" + }, + "context": { + "source": "voice" + } + } + ] }, - "MovieEntity": { - "title": "MovieEntity", + "SearchIntent": { + "description": "A Firebolt compliant representation of a user intention to navigate an app to it's search UI with a search term populated, and bring that app to the foreground if needed.", + "title": "SearchIntent", "allOf": [ { - "$ref": "#/x-schemas/Intents/ProgramEntity" + "$ref": "#/x-schemas/Intents/Intent" + }, + { + "$ref": "#/x-schemas/Intents/IntentProperties" }, { - "description": "A Firebolt compliant representation of a Movie entity.", - "title": "MovieEntity", "type": "object", - "required": [ - "entityType", - "programType", - "entityId" - ], "properties": { - "entityType": { - "const": "program" - }, - "programType": { - "const": "movie" - }, - "entityId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "assetId": { - "$ref": "#/x-schemas/Intents/Identifier" + "action": { + "const": "search" }, - "appContentData": { - "type": "string", - "maxLength": 256 + "data": { + "type": "object", + "required": [ + "query" + ], + "properties": { + "query": { + "type": "string" + } + }, + "additionalProperties": false } - }, - "additionalProperties": false + } } ], "examples": [ { - "entityType": "program", - "programType": "movie", - "entityId": "el-camino" + "action": "search", + "data": { + "query": "walter white" + }, + "context": { + "source": "voice" + } } ] }, - "TVEpisodeEntity": { - "title": "TVEpisodeEntity", + "SectionIntent": { + "description": "A Firebolt compliant representation of a user intention to navigate an app to a section not covered by `home`, `entity`, `player`, or `search`, and bring that app to the foreground if needed.", + "title": "SectionIntent", "allOf": [ { - "$ref": "#/x-schemas/Intents/ProgramEntity" + "$ref": "#/x-schemas/Intents/Intent" + }, + { + "$ref": "#/x-schemas/Intents/IntentProperties" }, { - "description": "A Firebolt compliant representation of a TV Episode entity.", - "title": "TVEpisodeEntity", "type": "object", - "required": [ - "entityType", - "programType", - "entityId", - "seriesId", - "seasonId" - ], "properties": { - "entityType": { - "const": "program" - }, - "programType": { - "const": "episode" - }, - "entityId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "seriesId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "seasonId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "assetId": { - "$ref": "#/x-schemas/Intents/Identifier" + "action": { + "const": "section" }, - "appContentData": { - "type": "string", - "maxLength": 256 + "data": { + "type": "object", + "required": [ + "sectionName" + ], + "properties": { + "sectionName": { + "type": "string" + } + }, + "additionalProperties": false } }, - "additionalProperties": false + "required": [ + "data" + ] } ], "examples": [ { - "entityType": "program", - "programType": "episode", - "entityId": "breaking-bad-pilot", - "seriesId": "breaking-bad", - "seasonId": "breaking-bad-season-1" + "action": "section", + "data": { + "sectionName": "settings" + }, + "context": { + "source": "voice" + } } ] }, - "TVSeriesEntity": { - "title": "TVSeriesEntity", + "TuneIntent": { + "description": "A Firebolt compliant representation of a user intention to 'tune' to a traditional over-the-air broadcast, or an OTT Stream from an OTT or vMVPD App.", + "title": "TuneIntent", "allOf": [ { - "$ref": "#/x-schemas/Intents/ProgramEntity" + "$ref": "#/x-schemas/Intents/Intent" + }, + { + "$ref": "#/x-schemas/Intents/IntentProperties" }, { - "description": "A Firebolt compliant representation of a TV Series entity.", "type": "object", "required": [ - "entityType", - "programType", - "entityId" + "data" ], "properties": { - "entityType": { - "const": "program" - }, - "programType": { - "const": "series" - }, - "entityId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "assetId": { - "$ref": "#/x-schemas/Intents/Identifier" + "action": { + "const": "tune" }, - "appContentData": { - "type": "string", - "maxLength": 256 + "data": { + "type": "object", + "required": [ + "entity" + ], + "additionalProperties": false, + "properties": { + "entity": { + "$ref": "#/x-schemas/Entity/ChannelEntity" + }, + "options": { + "description": "The options property of the data property MUST have only one of the following fields.", + "type": "object", + "required": [], + "additionalProperties": false, + "minProperties": 1, + "maxProperties": 1, + "properties": { + "assetId": { + "type": "string", + "description": "The ID of a specific 'listing', as scoped by the target App's ID-space, which the App should begin playback from." + }, + "restartCurrentProgram": { + "type": "boolean", + "description": "Denotes that the App should start playback at the most recent program boundary, rather than 'live.'" + }, + "time": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 Date/Time where the App should begin playback from." + } + } + } + } } - }, - "additionalProperties": false + } } ], "examples": [ { - "entityType": "program", - "programType": "series", - "entityId": "breaking-bad" + "action": "tune", + "data": { + "entity": { + "entityType": "channel", + "channelType": "streaming", + "entityId": "an-ott-channel" + }, + "options": { + "restartCurrentProgram": true + } + }, + "context": { + "source": "voice" + } } ] }, - "TVSeasonEntity": { - "title": "TVSeasonEntity", - "description": "A Firebolt compliant representation of a TV Season entity.", + "PlayEntityIntent": { + "description": "A Firebolt compliant representation of a user intention to navigate an app to a the video player for a specific, playable entity, and bring that app to the foreground if needed.", + "title": "PlayEntityIntent", "allOf": [ { - "$ref": "#/x-schemas/Intents/ProgramEntity" + "$ref": "#/x-schemas/Intents/Intent" + }, + { + "$ref": "#/x-schemas/Intents/IntentProperties" }, { "type": "object", "required": [ - "entityType", - "programType", - "entityId", - "seriesId" + "data" ], "properties": { - "entityType": { - "const": "program" - }, - "programType": { - "const": "season" - }, - "entityId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "seriesId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "assetId": { - "$ref": "#/x-schemas/Intents/Identifier" + "action": { + "const": "play-entity" }, - "appContentData": { - "type": "string", - "maxLength": 256 + "data": { + "type": "object", + "properties": { + "entity": { + "$ref": "#/x-schemas/Entity/PlayableEntity" + }, + "options": { + "type": "object", + "properties": { + "playFirstId": { + "type": "string" + }, + "playFirstTrack": { + "type": "integer", + "minimum": 1 + } + }, + "additionalProperties": false + } + }, + "required": [ + "entity" + ], + "propertyNames": { + "enum": [ + "entity", + "options" + ] + }, + "if": { + "properties": { + "entity": { + "type": "object", + "required": [ + "entityType" + ], + "properties": { + "entityType": { + "const": "playlist" + } + } + } + } + }, + "then": { + "type": "object", + "properties": { + "options": { + "type": "object", + "maxProperties": 1 + } + } + }, + "else": { + "type": "object", + "properties": { + "options": { + "type": "object", + "maxProperties": 0 + } + } + } } - }, - "additionalProperties": false - } - ], - "examples": [ - { - "entityType": "program", - "programType": "season", - "entityId": "breaking-bad-season-1", - "seriesId": "breaking-bad" + } } - ] - }, - "AdditionalEntity": { - "title": "AdditionalEntity", - "allOf": [ + ], + "examples": [ { - "$ref": "#/x-schemas/Intents/ProgramEntity" + "action": "play-entity", + "data": { + "entity": { + "entityType": "playlist", + "entityId": "playlist/xyz" + }, + "options": { + "playFirstId": "song/xyz" + } + }, + "context": { + "source": "voice" + } }, { - "description": "A Firebolt compliant representation of the remaining entity types.", - "type": "object", - "required": [ - "entityType", - "entityId" - ], - "properties": { - "entityType": { - "const": "program" - }, - "programType": { - "type": "string", - "enum": [ - "concert", - "sportingEvent", - "preview", - "other", - "advertisement", - "musicVideo", - "minisode", - "extra" - ] - }, - "entityId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "assetId": { - "$ref": "#/x-schemas/Intents/Identifier" + "action": "play-entity", + "data": { + "entity": { + "entityType": "playlist", + "entityId": "playlist/xyz" }, - "appContentData": { - "type": "string", - "maxLength": 256 + "options": { + "playFirstTrack": 3 } }, - "additionalProperties": false - } - ], - "examples": [ - { - "entityType": "program", - "programType": "concert", - "entityId": "live-aid" + "context": { + "source": "voice" + } } ] }, - "UntypedEntity": { - "title": "UntypedEntity", + "PlayQueryIntent": { + "description": "A Firebolt compliant representation of a user intention to navigate an app to a the video player for an abstract query to be searched for and played by the app.", + "title": "PlayQueryIntent", "allOf": [ { - "description": "A Firebolt compliant representation of the remaining entity types.", + "$ref": "#/x-schemas/Intents/Intent" + }, + { + "$ref": "#/x-schemas/Intents/IntentProperties" + }, + { "type": "object", "required": [ - "entityId" + "data" ], "properties": { - "entityId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "assetId": { - "$ref": "#/x-schemas/Intents/Identifier" + "action": { + "const": "play-query" }, - "appContentData": { - "type": "string", - "maxLength": 256 + "data": { + "type": "object", + "properties": { + "query": { + "type": "string" + }, + "options": { + "type": "object", + "properties": { + "programTypes": { + "type": "array", + "items": { + "$ref": "#/x-schemas/Entertainment/ProgramType" + } + }, + "musicTypes": { + "type": "array", + "items": { + "$ref": "#/x-schemas/Entertainment/MusicType" + } + } + }, + "additionalProperties": false + } + }, + "required": [ + "query" + ], + "propertyNames": { + "enum": [ + "query", + "options" + ] + } } - }, - "additionalProperties": false + } } ], "examples": [ { - "entityId": "an-entity" - } - ] - }, - "PlayableEntity": { - "title": "PlayableEntity", - "anyOf": [ - { - "$ref": "#/x-schemas/Intents/MovieEntity" - }, - { - "$ref": "#/x-schemas/Intents/TVEpisodeEntity" + "action": "play-query", + "data": { + "query": "Ed Sheeran" + }, + "context": { + "source": "voice" + } }, { - "$ref": "#/x-schemas/Intents/PlaylistEntity" + "action": "play-query", + "data": { + "query": "Ed Sheeran", + "options": { + "programTypes": [ + "movie" + ] + } + }, + "context": { + "source": "voice" + } }, { - "$ref": "#/x-schemas/Intents/AdditionalEntity" + "action": "play-query", + "data": { + "query": "Ed Sheeran", + "options": { + "programTypes": [ + "movie" + ], + "musicTypes": [ + "song" + ] + } + }, + "context": { + "source": "voice" + } } ] }, - "ChannelEntity": { - "title": "ChannelEntity", + "Intent": { + "description": "A Firebolt compliant representation of a user intention.", "type": "object", - "properties": { - "entityType": { - "const": "channel" - }, - "channelType": { - "type": "string", - "enum": [ - "streaming", - "overTheAir" - ] - }, - "entityId": { - "type": "string", - "description": "ID of the channel, in the target App's scope." - }, - "appContentData": { - "type": "string", - "maxLength": 256 - } - }, "required": [ - "entityType", - "channelType", - "entityId" + "action", + "context" ], - "additionalProperties": false - }, - "ProgramEntity": { - "title": "ProgramEntity", - "type": "object", "properties": { - "entityType": { - "const": "program" - }, - "programType": { - "$ref": "#/x-schemas/Entertainment/ProgramType" - }, - "entityId": { + "action": { "type": "string" + }, + "context": { + "type": "object", + "required": [ + "source" + ], + "properties": { + "source": { + "type": "string" + } + } } - }, - "required": [ - "entityType", - "programType", - "entityId" - ] - }, - "Identifier": { - "type": "string" + } }, - "PlaylistEntity": { - "title": "PlaylistEntity", - "description": "A Firebolt compliant representation of a Playlist entity.", + "IntentProperties": { "type": "object", - "required": [ - "entityType", - "entityId" - ], - "properties": { - "entityType": { - "const": "playlist" - }, - "entityId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "assetId": { - "$ref": "#/x-schemas/Intents/Identifier" - }, - "appContentData": { - "type": "string", - "maxLength": 256 - } - }, - "additionalProperties": false, - "examples": [ - { - "entityType": "playlist", - "entityId": "playlist/xyz" - } - ] + "propertyNames": { + "enum": [ + "action", + "data", + "context" + ] + } } }, "Lifecycle": { diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 0dcb59570..508802c97 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -15,19 +15,22 @@ // SPDX-License-Identifier: Apache-2.0 // -use ripple_sdk::api::{ - firebolt::{ - fb_capabilities::FireboltPermission, - fb_openrpc::{ - CapabilitySet, FireboltOpenRpc, FireboltOpenRpcMethod, FireboltSemanticVersion, - FireboltVersionManifest, OpenRPCParser, +use ripple_sdk::log::error; +use ripple_sdk::{api::firebolt::fb_openrpc::CapabilityPolicy, serde_json}; +use ripple_sdk::{ + api::{ + firebolt::{ + fb_capabilities::FireboltPermission, + fb_openrpc::{ + CapabilitySet, FireboltOpenRpc, FireboltOpenRpcMethod, FireboltSemanticVersion, + FireboltVersionManifest, OpenRPCParser, + }, + provider::ProviderAttributes, }, - provider::ProviderAttributes, + manifest::exclusory::{Exclusory, ExclusoryImpl}, }, - manifest::exclusory::{Exclusory, ExclusoryImpl}, + semver::Op, }; -use ripple_sdk::log::error; -use ripple_sdk::{api::firebolt::fb_openrpc::CapabilityPolicy, serde_json}; use std::{ collections::HashMap, sync::{Arc, RwLock}, @@ -48,6 +51,10 @@ pub struct ProviderSet { pub response: Option, pub error: Option, pub attributes: Option<&'static ProviderAttributes>, + // + pub provides: Option, + pub provided_by: Option, + // } impl ProviderSet { @@ -56,6 +63,69 @@ impl ProviderSet { } } +// +// pub fn build_provider_sets( +// openrpc_methods: &Vec, +// ) -> HashMap { +// let mut provider_sets = HashMap::default(); + +// for method in openrpc_methods { +// let mut has_x_provides = None; + +// // Only build provider sets for AcknowledgeChallenge and PinChallenge methods for now +// if !method.name.starts_with("AcknowledgeChallenge.") +// && !method.name.starts_with("PinChallenge.") +// { +// continue; +// } + +// if let Some(tags) = &method.tags { +// let mut has_event = false; +// let mut has_caps = false; +// let mut has_x_allow_focus_for = false; +// let mut has_x_response_for = false; +// let mut has_x_error_for = false; + +// for tag in tags { +// if tag.name.eq("event") { +// has_event = true; +// } else if tag.name.eq("capabilities") { +// has_caps = true; +// has_x_provides = tag.get_provides(); +// has_x_allow_focus_for |= tag.allow_focus_for.is_some(); +// has_x_response_for |= tag.response_for.is_some(); +// has_x_error_for |= tag.error_for.is_some(); +// } +// } + +// if let Some(p) = has_x_provides { +// let mut provider_set = provider_sets +// .get(&p.as_str()) +// .unwrap_or(&ProviderSet::new()) +// .clone(); + +// if has_event && has_caps { +// provider_set.request = Some(method.clone()); +// } +// if has_x_allow_focus_for { +// provider_set.focus = Some(method.clone()); +// } +// if has_x_response_for { +// provider_set.response = Some(method.clone()); +// } +// if has_x_error_for { +// provider_set.error = Some(method.clone()); +// } + +// let module: Vec<&str> = method.name.split('.').collect(); +// provider_set.attributes = ProviderAttributes::get(module[0]); + +// provider_sets.insert(p.as_str(), provider_set.to_owned()); +// } +// } +// } +// provider_sets +// } pub fn build_provider_sets( openrpc_methods: &Vec, ) -> HashMap { @@ -67,6 +137,8 @@ pub fn build_provider_sets( // Only build provider sets for AcknowledgeChallenge and PinChallenge methods for now if !method.name.starts_with("AcknowledgeChallenge.") && !method.name.starts_with("PinChallenge.") + && !method.name.starts_with("Discovery.") + && !method.name.starts_with("Content.") { continue; } @@ -77,6 +149,8 @@ pub fn build_provider_sets( let mut has_x_allow_focus_for = false; let mut has_x_response_for = false; let mut has_x_error_for = false; + let mut x_provided_by = None; + let mut x_provides = None; for tag in tags { if tag.name.eq("event") { @@ -87,15 +161,14 @@ pub fn build_provider_sets( has_x_allow_focus_for |= tag.allow_focus_for.is_some(); has_x_response_for |= tag.response_for.is_some(); has_x_error_for |= tag.error_for.is_some(); + x_provided_by = tag.provided_by.clone(); + x_provides = tag.provides.clone(); } } - if let Some(p) = has_x_provides { - let mut provider_set = provider_sets - .get(&p.as_str()) - .unwrap_or(&ProviderSet::new()) - .clone(); + let mut provider_set = ProviderSet::new(); + if let Some(_capability) = has_x_provides { if has_event && has_caps { provider_set.request = Some(method.clone()); } @@ -108,16 +181,27 @@ pub fn build_provider_sets( if has_x_error_for { provider_set.error = Some(method.clone()); } + provider_set.provides = x_provides; + } else { + // x-provided-by can only be set if x-provides isn't. + provider_set.provided_by = x_provided_by; + } - let module: Vec<&str> = method.name.split('.').collect(); - provider_set.attributes = ProviderAttributes::get(module[0]); + let module: Vec<&str> = method.name.split('.').collect(); + provider_set.attributes = ProviderAttributes::get(module[0]); - provider_sets.insert(p.as_str(), provider_set.to_owned()); - } + println!("build_provider_sets: provider_set={:?}", provider_set); + + provider_sets.insert( + FireboltOpenRpcMethod::name_with_lowercase_module(&method.name), + provider_set.to_owned(), + ); } } + provider_sets } +// #[derive(Debug, Clone)] pub struct OpenRpcState { diff --git a/core/sdk/src/api/firebolt/fb_openrpc.rs b/core/sdk/src/api/firebolt/fb_openrpc.rs index 2169bc8e4..f12e186c7 100644 --- a/core/sdk/src/api/firebolt/fb_openrpc.rs +++ b/core/sdk/src/api/firebolt/fb_openrpc.rs @@ -307,6 +307,10 @@ pub struct FireboltOpenRpcTag { pub manages: Option>, #[serde(rename = "x-provides")] pub provides: Option, + // + #[serde(rename = "x-provided-by")] + pub provided_by: Option, + // #[serde(rename = "x-alternative")] pub alternative: Option, #[serde(rename = "x-since")] From a56383a78abb2d787681d5a34a8afc03757e5a4f Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Thu, 11 Jul 2024 10:05:03 -0400 Subject: [PATCH 02/24] x-provided-by Pull pattern: WIP --- .../firebolt/handlers/provider_registrar.rs | 155 ++++++++++++------ core/main/src/state/openrpc_state.rs | 5 + core/sdk/src/api/firebolt/provider.rs | 12 +- 3 files changed, 121 insertions(+), 51 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 56a1e1690..f192ee88f 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -16,8 +16,9 @@ // use crate::{ - firebolt::rpc::register_aliases, service::apps::provider_broker::ProviderBroker, - state::platform_state::PlatformState, + firebolt::rpc::register_aliases, + service::apps::provider_broker::ProviderBroker, + state::{cap, platform_state::PlatformState}, }; use jsonrpsee::{ core::{server::rpc_module::Methods, RpcResult}, @@ -31,7 +32,7 @@ use ripple_sdk::{ fb_openrpc::FireboltOpenRpcMethod, fb_pin::PinChallengeResponse, provider::{ - ChallengeResponse, ExternalProviderResponse, FocusRequest, ProviderResponse, + self, ChallengeResponse, ExternalProviderResponse, FocusRequest, ProviderResponse, ProviderResponsePayload, ProviderResponsePayloadType, }, }, @@ -104,32 +105,66 @@ impl ProviderRegistrar { let request_method = FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); - rpc_module - .register_async_method( - request_method, - move |params, context| async move { - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let request: ListenRequest = params_sequence.next().unwrap(); - let listening = request.listen; - - ProviderBroker::register_or_unregister_provider( - &context.platform_state, - attributes.capability.into(), - attributes.method.into(), - attributes.event, - call_context, - request, - ) - .await; - - Ok(ListenerResponse { - listening, - event: attributes.event.into(), - }) - }, - ) - .unwrap(); + // + // rpc_module + // .register_async_method( + // request_method, + // move |params, context| async move { + // let mut params_sequence = params.sequence(); + // let call_context: CallContext = params_sequence.next().unwrap(); + // let request: ListenRequest = params_sequence.next().unwrap(); + // let listening = request.listen; + + // ProviderBroker::register_or_unregister_provider( + // &context.platform_state, + // // + // //attributes.capability.into(), + // provider_set.uses.clone().into(), + // // + // attributes.method.into(), + // attributes.event, + // call_context, + // request, + // ) + // .await; + + // Ok(ListenerResponse { + // listening, + // event: attributes.event.into(), + // }) + // }, + // ) + // .unwrap(); + if let Some(capability) = provider_set.uses { + rpc_module + .register_async_method( + request_method, + move |params, context| async move { + let mut params_sequence = params.sequence(); + let call_context: CallContext = + params_sequence.next().unwrap(); + let request: ListenRequest = + params_sequence.next().unwrap(); + let listening = request.listen; + + ProviderBroker::register_or_unregister_provider( + &context.platform_state, + capability.into(), + attributes.method.into(), + attributes.event, + call_context, + request, + ) + .await; + + Ok(ListenerResponse { + listening, + event: attributes.event.into(), + }) + }, + ) + .unwrap(); + } } // Register focus function @@ -137,25 +172,49 @@ impl ProviderRegistrar { let focus_method = FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); - rpc_module - .register_async_method( - focus_method, - move |params, context| async move { - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let request: FocusRequest = params_sequence.next().unwrap(); - - ProviderBroker::focus( - &context.platform_state, - call_context, - attributes.capability.into(), - request, - ) - .await; - Ok(None) as RpcResult> - }, - ) - .unwrap(); + // + // rpc_module + // .register_async_method( + // focus_method, + // move |params, context| async move { + // let mut params_sequence = params.sequence(); + // let call_context: CallContext = params_sequence.next().unwrap(); + // let request: FocusRequest = params_sequence.next().unwrap(); + + // ProviderBroker::focus( + // &context.platform_state, + // call_context, + // attributes.capability.into(), + // request, + // ) + // .await; + // Ok(None) as RpcResult> + // }, + // ) + // .unwrap(); + if let Some(capability) = provider_set.uses { + rpc_module + .register_async_method( + focus_method, + move |params, context| async move { + let mut params_sequence = params.sequence(); + let call_context: CallContext = + params_sequence.next().unwrap(); + let request: FocusRequest = params_sequence.next().unwrap(); + + ProviderBroker::focus( + &context.platform_state, + call_context, + capability.into(), + request, + ) + .await; + Ok(None) as RpcResult> + }, + ) + .unwrap(); + } + // } // Register response function diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 508802c97..535f77d4f 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -54,6 +54,7 @@ pub struct ProviderSet { // pub provides: Option, pub provided_by: Option, + pub uses: Option, // } @@ -151,6 +152,7 @@ pub fn build_provider_sets( let mut has_x_error_for = false; let mut x_provided_by = None; let mut x_provides = None; + let mut x_uses = None; for tag in tags { if tag.name.eq("event") { @@ -163,6 +165,7 @@ pub fn build_provider_sets( has_x_error_for |= tag.error_for.is_some(); x_provided_by = tag.provided_by.clone(); x_provides = tag.provides.clone(); + x_uses = tag.uses.clone(); } } @@ -187,6 +190,8 @@ pub fn build_provider_sets( provider_set.provided_by = x_provided_by; } + provider_set.uses = x_uses; + let module: Vec<&str> = method.name.split('.').collect(); provider_set.attributes = ProviderAttributes::get(module[0]); diff --git a/core/sdk/src/api/firebolt/provider.rs b/core/sdk/src/api/firebolt/provider.rs index ea9ece16b..5fdbb4f09 100644 --- a/core/sdk/src/api/firebolt/provider.rs +++ b/core/sdk/src/api/firebolt/provider.rs @@ -159,7 +159,9 @@ pub struct ProviderAttributes { pub event: &'static str, pub response_payload_type: ProviderResponsePayloadType, pub error_payload_type: ProviderResponsePayloadType, - pub capability: &'static str, + // + //pub capability: &'static str, + // pub method: &'static str, } @@ -177,7 +179,9 @@ pub const ACKNOWLEDGE_CHALLENGE_ATTRIBS: ProviderAttributes = ProviderAttributes event: ACK_CHALLENGE_EVENT, response_payload_type: ProviderResponsePayloadType::ChallengeResponse, error_payload_type: ProviderResponsePayloadType::ChallengeError, - capability: ACK_CHALLENGE_CAPABILITY, + // + //capability: ACK_CHALLENGE_CAPABILITY, + // method: "challenge", }; @@ -185,7 +189,9 @@ pub const PIN_CHALLENGE_ATTRIBS: ProviderAttributes = ProviderAttributes { event: PIN_CHALLENGE_EVENT, response_payload_type: ProviderResponsePayloadType::PinChallengeResponse, error_payload_type: ProviderResponsePayloadType::ChallengeError, - capability: PIN_CHALLENGE_CAPABILITY, + // + //capability: PIN_CHALLENGE_CAPABILITY, + // method: "challenge", }; From ec45fd83d92424766d0f82c24294ee5264648121 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sat, 13 Jul 2024 16:38:44 -0400 Subject: [PATCH 03/24] x-provided-by Pull pattern: WIP --- .../src/bootstrap/start_fbgateway_step.rs | 1 + core/main/src/firebolt/firebolt_gateway.rs | 4 - core/main/src/firebolt/firebolt_ws.rs | 4 - .../src/firebolt/handlers/discovery_rpc.rs | 10 +- .../src/firebolt/handlers/keyboard_rpc.rs | 5 +- core/main/src/firebolt/handlers/lcm_rpc.rs | 10 +- .../src/firebolt/handlers/localization_rpc.rs | 5 +- .../firebolt/handlers/provider_registrar.rs | 632 ++++++++++++++---- core/main/src/firebolt/rpc_router.rs | 34 +- core/main/src/service/apps/provider_broker.rs | 61 +- core/main/src/state/openrpc_state.rs | 53 +- core/sdk/src/api/firebolt/provider.rs | 39 +- 12 files changed, 664 insertions(+), 194 deletions(-) diff --git a/core/main/src/bootstrap/start_fbgateway_step.rs b/core/main/src/bootstrap/start_fbgateway_step.rs index 52c55972f..1ffe06531 100644 --- a/core/main/src/bootstrap/start_fbgateway_step.rs +++ b/core/main/src/bootstrap/start_fbgateway_step.rs @@ -54,6 +54,7 @@ impl FireboltGatewayStep { // TODO: Ultimately this should be able to register all provider below, for now just does // AcknowledgeChallenge and PinChallenge. ProviderRegistrar::register(&state, &mut methods); + // let _ = methods.merge(DeviceRPCProvider::provide_with_alias(state.clone())); let _ = methods.merge(WifiRPCProvider::provide_with_alias(state.clone())); diff --git a/core/main/src/firebolt/firebolt_gateway.rs b/core/main/src/firebolt/firebolt_gateway.rs index 39cba57d4..0e423e440 100644 --- a/core/main/src/firebolt/firebolt_gateway.rs +++ b/core/main/src/firebolt/firebolt_gateway.rs @@ -214,10 +214,6 @@ impl FireboltGateway { .handle_brokerage(request_c.clone(), extn_msg.clone()) { // Route - println!( - "*** _DEBUG: route: request.clone().ctx.protocol={:?}", - request.clone().ctx.protocol - ); match request.clone().ctx.protocol { ApiProtocol::Extn => { if let Some(extn_msg) = extn_msg { diff --git a/core/main/src/firebolt/firebolt_ws.rs b/core/main/src/firebolt/firebolt_ws.rs index 2bdee57b9..49ba3fdc9 100644 --- a/core/main/src/firebolt/firebolt_ws.rs +++ b/core/main/src/firebolt/firebolt_ws.rs @@ -265,10 +265,6 @@ impl FireboltWs { "firebolt_ws Received Firebolt request {} {} {}", connection_id, request.ctx.request_id, request.method ); - debug!( - "*** _DEBUG: firebolt_ws Received Firebolt request {} {} {}", - connection_id, request.ctx.request_id, request.method - ); let msg = FireboltGatewayCommand::HandleRpc { request }; if let Err(e) = client.clone().send_gateway_command(msg) { error!("failed to send request {:?}", e); diff --git a/core/main/src/firebolt/handlers/discovery_rpc.rs b/core/main/src/firebolt/handlers/discovery_rpc.rs index ee062eef5..0aa327f86 100644 --- a/core/main/src/firebolt/handlers/discovery_rpc.rs +++ b/core/main/src/firebolt/handlers/discovery_rpc.rs @@ -625,7 +625,10 @@ impl DiscoveryServer for DiscoveryImpl { &self.state, FireboltCap::Short(ENTITY_INFO_CAPABILITY.into()).as_str(), String::from("entityInfo"), - ENTITY_INFO_EVENT, + // 3 + //ENTITY_INFO_EVENT, + String::from(ENTITY_INFO_EVENT), + // ctx, request, ) @@ -697,7 +700,10 @@ impl DiscoveryServer for DiscoveryImpl { &self.state, FireboltCap::Short(PURCHASED_CONTENT_CAPABILITY.into()).as_str(), String::from("purchasedContent"), - PURCHASED_CONTENT_EVENT, + // 3 + //PURCHASED_CONTENT_EVENT, + String::from(PURCHASED_CONTENT_EVENT), + // ctx, request, ) diff --git a/core/main/src/firebolt/handlers/keyboard_rpc.rs b/core/main/src/firebolt/handlers/keyboard_rpc.rs index 58526a96a..3ed1a220b 100644 --- a/core/main/src/firebolt/handlers/keyboard_rpc.rs +++ b/core/main/src/firebolt/handlers/keyboard_rpc.rs @@ -302,7 +302,10 @@ impl KeyboardImpl { &self.platform_state, String::from(KEYBOARD_PROVIDER_CAPABILITY), method, - event_name, + // 3 + //event_name, + String::from(event_name), + // ctx, request, ) diff --git a/core/main/src/firebolt/handlers/lcm_rpc.rs b/core/main/src/firebolt/handlers/lcm_rpc.rs index b77f5a0a0..a89b001e3 100644 --- a/core/main/src/firebolt/handlers/lcm_rpc.rs +++ b/core/main/src/firebolt/handlers/lcm_rpc.rs @@ -113,7 +113,10 @@ impl LifecycleManagementImpl { &self.state, FireboltCap::short("app:lifecycle").as_str(), method.into(), - event_name, + // 3 + //event_name, + String::from(event_name), + // ctx, request, ) @@ -121,7 +124,10 @@ impl LifecycleManagementImpl { Ok(ListenerResponse { listening: listen, - event: event_name.into(), + // 3 + //event: event_name.into(), + event: String::from(event_name), + // }) } } diff --git a/core/main/src/firebolt/handlers/localization_rpc.rs b/core/main/src/firebolt/handlers/localization_rpc.rs index 6f0b36dea..93acb63d8 100644 --- a/core/main/src/firebolt/handlers/localization_rpc.rs +++ b/core/main/src/firebolt/handlers/localization_rpc.rs @@ -221,7 +221,10 @@ impl LocalizationImpl { // TODO update with Firebolt Cap in later effort "xrn:firebolt:capability:localization:locale".into(), method.into(), - event_name, + // 3 + //event_name, + String::from(event_name), + // ctx, request, ) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index f192ee88f..cc52adc8c 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -15,13 +15,18 @@ // SPDX-License-Identifier: Apache-2.0 // +use std::time::Duration; + use crate::{ firebolt::rpc::register_aliases, - service::apps::provider_broker::ProviderBroker, - state::{cap, platform_state::PlatformState}, + service::apps::{ + app_events::AppEvents, + provider_broker::{ProviderBroker, ProviderBrokerRequest}, + }, + state::{cap, openrpc_state::ProviderSet, platform_state::PlatformState}, }; use jsonrpsee::{ - core::{server::rpc_module::Methods, RpcResult}, + core::{server::rpc_module::Methods, Error, RpcResult}, types::{error::CallError, ParamsSequence}, RpcModule, }; @@ -32,24 +37,45 @@ use ripple_sdk::{ fb_openrpc::FireboltOpenRpcMethod, fb_pin::PinChallengeResponse, provider::{ - self, ChallengeResponse, ExternalProviderResponse, FocusRequest, ProviderResponse, - ProviderResponsePayload, ProviderResponsePayloadType, + self, ChallengeResponse, ExternalProviderResponse, FocusRequest, + ProviderRequestPayload, ProviderResponse, ProviderResponsePayload, + ProviderResponsePayloadType, }, }, - gateway::rpc_gateway_api::CallContext, + gateway::rpc_gateway_api::{CallContext, CallerSession}, }, - log::error, + log::{error, info}, + tokio::{sync::oneshot, time::timeout}, }; +use serde_json::Value; + +// +// TODO: Add to config +const DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS: u64 = 5000; +// #[derive(Clone)] struct RpcModuleContext { platform_state: PlatformState, + // + method: String, + provider_set: ProviderSet, + // } impl RpcModuleContext { - fn new(platform_state: PlatformState) -> Self { - RpcModuleContext { platform_state } + // + // fn new(platform_state: PlatformState) -> Self { + // RpcModuleContext { platform_state } + // } + fn new(platform_state: PlatformState, method: String, provider_set: ProviderSet) -> Self { + RpcModuleContext { + method, + platform_state, + provider_set, + } } + // } pub struct ProviderRegistrar; @@ -59,6 +85,7 @@ impl ProviderRegistrar { payload_type: ProviderResponsePayloadType, mut params_sequence: ParamsSequence, ) -> Option { + let _call_context: CallContext = params_sequence.next().unwrap(); match payload_type { ProviderResponsePayloadType::ChallengeResponse => { let external_provider_response: Result< @@ -92,54 +119,214 @@ impl ProviderRegistrar { None } + // + // pub fn register(platform_state: &PlatformState, methods: &mut Methods) { + // let provider_map = platform_state.open_rpc_state.get_provider_map(); + // for method_name in provider_map.clone().keys() { + // if let Some(provider_set) = provider_map.get(method_name) { + // if let Some(attributes) = provider_set.attributes { + // let rpc_module_context = RpcModuleContext::new(platform_state.clone()); + // let mut rpc_module = RpcModule::new(rpc_module_context.clone()); + + // // Register request function + // if let Some(method) = provider_set.request.clone() { + // let request_method = + // FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); + + // rpc_module + // .register_async_method( + // request_method, + // move |params, context| async move { + // let mut params_sequence = params.sequence(); + // let call_context: CallContext = params_sequence.next().unwrap(); + // let request: ListenRequest = params_sequence.next().unwrap(); + // let listening = request.listen; + + // ProviderBroker::register_or_unregister_provider( + // &context.platform_state, + // attributes.capability.into(), + // attributes.method.into(), + // attributes.event, + // call_context, + // request, + // ) + // .await; + + // Ok(ListenerResponse { + // listening, + // event: attributes.event.into(), + // }) + // }, + // ) + // .unwrap(); + // } + + // // Register focus function + // if let Some(method) = provider_set.focus.clone() { + // let focus_method = + // FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); + + // rpc_module + // .register_async_method( + // focus_method, + // move |params, context| async move { + // let mut params_sequence = params.sequence(); + // let call_context: CallContext = params_sequence.next().unwrap(); + // let request: FocusRequest = params_sequence.next().unwrap(); + + // ProviderBroker::focus( + // &context.platform_state, + // call_context, + // attributes.capability.into(), + // request, + // ) + // .await; + // Ok(None) as RpcResult> + // }, + // ) + // .unwrap(); + // } + + // // Register response function + // if let Some(method) = provider_set.response.clone() { + // let response_method = + // FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); + + // rpc_module + // .register_async_method( + // response_method, + // move |params, context| async move { + // let params_sequence = params.sequence(); + + // if let Some(provider_response) = + // ProviderRegistrar::get_provider_response( + // attributes.response_payload_type.clone(), + // params_sequence, + // ) + // { + // ProviderBroker::provider_response( + // &context.platform_state, + // provider_response, + // ) + // .await; + // } + + // Ok(None) as RpcResult> + // }, + // ) + // .unwrap(); + // } + + // // Register error function + // if let Some(method) = provider_set.error.clone() { + // let error_method = method.name.clone().leak(); + + // rpc_module + // .register_async_method( + // error_method, + // move |params, context| async move { + // let params_sequence = params.sequence(); + + // if let Some(provider_response) = + // ProviderRegistrar::get_provider_response( + // attributes.error_payload_type.clone(), + // params_sequence, + // ) + // { + // ProviderBroker::provider_response( + // &context.platform_state, + // provider_response, + // ) + // .await; + // } + + // Ok(None) as RpcResult> + // }, + // ) + // .unwrap(); + // } + + // //methods.merge(rpc_module.clone()).ok(); + // methods + // .merge(register_aliases(platform_state, rpc_module)) + // .ok(); + // } + // } + // } + // } pub fn register(platform_state: &PlatformState, methods: &mut Methods) { let provider_map = platform_state.open_rpc_state.get_provider_map(); + for method_name in provider_map.clone().keys() { if let Some(provider_set) = provider_map.get(method_name) { - if let Some(attributes) = provider_set.attributes { - let rpc_module_context = RpcModuleContext::new(platform_state.clone()); - let mut rpc_module = RpcModule::new(rpc_module_context.clone()); + let method_name_lc = + FireboltOpenRpcMethod::name_with_lowercase_module(method_name).leak(); - // Register request function - if let Some(method) = provider_set.request.clone() { - let request_method = - FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); + let rpc_module_context = RpcModuleContext::new( + platform_state.clone(), + method_name_lc.into(), + provider_set.clone(), + ); + let mut rpc_module = RpcModule::new(rpc_module_context.clone()); - // - // rpc_module - // .register_async_method( - // request_method, - // move |params, context| async move { - // let mut params_sequence = params.sequence(); - // let call_context: CallContext = params_sequence.next().unwrap(); - // let request: ListenRequest = params_sequence.next().unwrap(); - // let listening = request.listen; - - // ProviderBroker::register_or_unregister_provider( - // &context.platform_state, - // // - // //attributes.capability.into(), - // provider_set.uses.clone().into(), - // // - // attributes.method.into(), - // attributes.event, - // call_context, - // request, - // ) - // .await; - - // Ok(ListenerResponse { - // listening, - // event: attributes.event.into(), - // }) - // }, - // ) - // .unwrap(); - if let Some(capability) = provider_set.uses { - rpc_module - .register_async_method( - request_method, - move |params, context| async move { + if provider_set.event { + if let Some(provided_by) = &provider_set.provided_by { + // Register app event listener + println!("*** _DEBUG: Registering method: App event listener: method_name={}, provided_by={}", + method_name, provided_by + ); + info!("Registering method: App event listener: method_name={}, provided_by={}", + method_name, provided_by + ); + rpc_module + .register_async_method( + method_name_lc, + move |params, context| async move { + println!( + "*** _DEBUG: App event listener registration: method={}", + context.method + ); + info!( + "App event listener registration: method={}", + context.method + ); + + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: ListenRequest = params_sequence.next().unwrap(); + let listen = request.listen; + + AppEvents::add_listener( + &context.platform_state, + context.method.clone(), + call_context, + request, + ); + Ok(ListenerResponse { + listening: listen, + event: context.method.clone(), + }) + }, + ) + .unwrap(); + } else if provider_set.provides.is_some() || provider_set.provides_to.is_some() + { + println!( + "*** _DEBUG: Registering method: Provider: method_name={}", + method_name + ); + info!("Registering method: Provider: method_name={}", method_name); + rpc_module + .register_async_method( + method_name_lc, + move |params, context| async move { + println!( + "*** _DEBUG: Provider registration: method={}", + context.method + ); + info!("Provider registration: method={}", context.method); + + if let Some(provides) = &context.provider_set.provides { let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); @@ -149,9 +336,9 @@ impl ProviderRegistrar { ProviderBroker::register_or_unregister_provider( &context.platform_state, - capability.into(), - attributes.method.into(), - attributes.event, + provides.clone(), + context.method.clone(), + context.method.clone(), call_context, request, ) @@ -159,107 +346,303 @@ impl ProviderRegistrar { Ok(ListenerResponse { listening, - event: attributes.event.into(), + event: context.method.clone(), }) - }, - ) - .unwrap(); - } + } else { + Err(Error::Custom("Missing provides attribute".to_string())) + } + }, + ) + .unwrap(); } + } else { + if provider_set.provides_to.is_some() { + println!( + "*** _DEBUG: Registering method: App event emitter: method_name={}", + method_name + ); + info!( + "Registering method: App event emitter: method_name={}", + method_name + ); + rpc_module + .register_async_method( + method_name_lc, + move |params, context| async move { + println!( + "*** _DEBUG: App event emitter: method={}", + context.method + ); + info!("App event emitter: method={}", context.method); + if let Some(event) = &context.provider_set.provides_to { + let mut params_sequence = params.sequence(); + let _call_context: CallContext = + params_sequence.next().unwrap(); + let result: Value = params_sequence.next().unwrap(); - // Register focus function - if let Some(method) = provider_set.focus.clone() { - let focus_method = - FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); + AppEvents::emit(&context.platform_state, event, &result) + .await; + } + + Ok(None) as RpcResult> + }, + ) + .unwrap(); + } + + if let Some(error) = &provider_set.error_for { + if let Some(attributes) = provider_set.attributes { + println!( + "*** _DEBUG: Registering method: Error function: method_name={}", + method_name + ); + info!( + "Registering method: Error function: method_name={}", + method_name + ); + + let error_method = method_name.clone().leak(); - // - // rpc_module - // .register_async_method( - // focus_method, - // move |params, context| async move { - // let mut params_sequence = params.sequence(); - // let call_context: CallContext = params_sequence.next().unwrap(); - // let request: FocusRequest = params_sequence.next().unwrap(); - - // ProviderBroker::focus( - // &context.platform_state, - // call_context, - // attributes.capability.into(), - // request, - // ) - // .await; - // Ok(None) as RpcResult> - // }, - // ) - // .unwrap(); - if let Some(capability) = provider_set.uses { rpc_module .register_async_method( - focus_method, + error_method, move |params, context| async move { - let mut params_sequence = params.sequence(); - let call_context: CallContext = - params_sequence.next().unwrap(); - let request: FocusRequest = params_sequence.next().unwrap(); + println!( + "*** _DEBUG: Provider error: method={}", + context.method + ); + info!("Provider error: method={}", context.method); + let params_sequence = params.sequence(); + + if let Some(provider_response) = + ProviderRegistrar::get_provider_response( + attributes.error_payload_type.clone(), + params_sequence, + ) + { + ProviderBroker::provider_response( + &context.platform_state, + provider_response, + ) + .await; + } - ProviderBroker::focus( - &context.platform_state, - call_context, - capability.into(), - request, - ) - .await; Ok(None) as RpcResult> }, ) .unwrap(); + } else { + println!("*** _DEBUG: Registering method: Error function: NO ATTRIBUTES: method_name={}", method_name); + error!( + "Registering method: Error function: NO ATTRIBUTES: method_name={}", + method_name + ); } - // } - // Register response function - if let Some(method) = provider_set.response.clone() { - let response_method = - FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); + if let Some(provided_by) = &provider_set.provided_by { + println!( + "*** _DEBUG: Registering method: Provider invoker: method_name={}", + method_name + ); + info!( + "Registering method: Provider invoker: method_name={}", + method_name + ); rpc_module .register_async_method( - response_method, + method_name_lc, move |params, context| async move { - let params_sequence = params.sequence(); + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let params: Value = params_sequence.next().unwrap(); - if let Some(provider_response) = - ProviderRegistrar::get_provider_response( - attributes.response_payload_type.clone(), - params_sequence, - ) - { - ProviderBroker::provider_response( - &context.platform_state, - provider_response, - ) - .await; + println!( + "*** _DEBUG: Provider invoker: method={}", + context.method + ); + info!("Provider invoker: method={}", context.method); + + if let Some(provided_by) = &context.provider_set.provided_by { + let provider_map = context + .platform_state + .open_rpc_state + .get_provider_map(); + + if let Some(provided_by_set) = provider_map.get( + &FireboltOpenRpcMethod::name_with_lowercase_module( + provided_by, + ), + ) { + if let Some(capability) = &provided_by_set.provides { + let ( + provider_response_payload_tx, + provider_response_payload_rx, + ) = oneshot::channel::(); + + let caller = CallerSession { + session_id: Some( + call_context.session_id.clone(), + ), + app_id: Some(call_context.app_id.clone()), + }; + + let provider_broker_request = + ProviderBrokerRequest { + capability: capability.clone(), + method: provided_by.clone(), + caller, + request: ProviderRequestPayload::Generic( + params.to_string(), + ), + tx: provider_response_payload_tx, + app_id: None, + }; + + ProviderBroker::invoke_method( + &context.platform_state, + provider_broker_request, + ) + .await; + + // match provider_response_payload_rx.await { + // Ok(provider_response_payload) => { + // return Ok( + // provider_response_payload.as_value(), // TODO: Is this right? + // ); + // } + // Err(e) => { + // return Err(Error::Custom(String::from( + // "Error returning from provider", + // ))); + // } + // } + + // let provider_response = timeout( + // Duration::from_millis( + // DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS, + // ), + // provider_response_payload_rx, + // ) + // .await + // .map_err(|_| { + // Err(Error::Custom(String::from( + // "Privder response timeout", + // ))) + // })?; + // return provider_response; + + match timeout( + Duration::from_millis( + DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS, + ), + provider_response_payload_rx, + ) + .await + { + Ok(result) => match result { + Ok(provider_response_payload) => { + return Ok(provider_response_payload + .as_value()); + } + Err(e) => { + return Err(Error::Custom( + String::from( + "Error returning from provider", + ), + )); + } + }, + Err(_) => { + return Err(Error::Custom(String::from( + "Provider response timeout", + ))); + } + } + } + } } - Ok(None) as RpcResult> + Err(Error::Custom(String::from( + "Unexpected schema configuration", + ))) }, ) .unwrap(); } + } + + // Register focus function + if let Some(method) = provider_set.focus.clone() { + println!( + "*** _DEBUG: Registering method: Focus function: method_name={}", + method_name + ); + info!( + "Registering method: Focus function: method_name={}", + method_name + ); + + let focus_method = + FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); + + rpc_module + .register_async_method(focus_method, move |params, context| async move { + println!("*** _DEBUG: Provider focus: method={}", context.method); + info!("Provider focus: method={}", context.method); + + if let Some(provides) = &context.provider_set.provides { + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: FocusRequest = params_sequence.next().unwrap(); - // Register error function - if let Some(method) = provider_set.error.clone() { - let error_method = method.name.clone().leak(); + ProviderBroker::focus( + &context.platform_state, + call_context, + provides.clone(), + request, + ) + .await; + + Ok(None) as RpcResult> + } else { + Err(Error::Custom("Missing provides attribute".to_string())) + } + }) + .unwrap(); + } + + if let Some(attributes) = provider_set.attributes { + // Register response function + if let Some(method) = provider_set.response.clone() { + println!( + "*** _DEBUG: Registering method: Response function: method_name={}", + method_name + ); + info!( + "Registering method: Response function: method_name={}", + method_name + ); + + let response_method = + FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); rpc_module .register_async_method( - error_method, + response_method, move |params, context| async move { + println!( + "*** _DEBUG: Provider response: method={}", + context.method + ); + info!("Provider response: method={}", context.method); + let params_sequence = params.sequence(); if let Some(provider_response) = ProviderRegistrar::get_provider_response( - attributes.error_payload_type.clone(), + attributes.response_payload_type.clone(), params_sequence, ) { @@ -275,13 +658,12 @@ impl ProviderRegistrar { ) .unwrap(); } - - //methods.merge(rpc_module.clone()).ok(); - methods - .merge(register_aliases(platform_state, rpc_module)) - .ok(); } + methods + .merge(register_aliases(platform_state, rpc_module)) + .ok(); } } } + // } diff --git a/core/main/src/firebolt/rpc_router.rs b/core/main/src/firebolt/rpc_router.rs index d7b5c5b1b..bdd633dbd 100644 --- a/core/main/src/firebolt/rpc_router.rs +++ b/core/main/src/firebolt/rpc_router.rs @@ -41,8 +41,12 @@ use ripple_sdk::{ use std::sync::{Arc, RwLock}; use crate::{ - service::telemetry_builder::TelemetryBuilder, - state::{openrpc_state::ProviderSet, platform_state::PlatformState, session_state::Session}, + service::{apps::provider_broker::ProviderBroker, telemetry_builder::TelemetryBuilder}, + state::{ + openrpc_state::ProviderSet, + platform_state::{self, PlatformState}, + session_state::Session, + }, utils::router_utils::{return_api_message_for_transport, return_extn_response}, }; @@ -82,17 +86,8 @@ async fn resolve_route( methods: Methods, resources: Resources, req: RpcRequest, - // - provider_set: Option, - // ) -> Result { info!("Routing {}", req.method); - // resolve_route - println!( - "*** _DEBUG: req.method={}, provider_set={:?}", - req.method, provider_set - ); - // YAH: Check if it's a provider and pass request to ProviderBroker? let id = Id::Number(req.ctx.call_id); let (sink_tx, mut sink_rx) = futures_channel::mpsc::unbounded::(); let sink = MethodSink::new_with_limit(sink_tx, TEN_MB_SIZE_BYTES); @@ -148,23 +143,12 @@ impl RpcRouter { ) { let methods = state.router_state.get_methods(); let resources = state.router_state.resources.clone(); - // - println!("*** _DEBUG: route: req.method={}", req.method); - let provider_map = state.open_rpc_state.get_provider_map(); - let provider_set = match provider_map.get(&req.method) { - Some(ps) => Some(ps.clone()), - None => None, - }; - // tokio::spawn(async move { let method = req.method.clone(); let app_id = req.ctx.app_id.clone(); let start = Utc::now().timestamp_millis(); - // - //let resp = resolve_route(methods, resources, req.clone()).await; - let resp = resolve_route(methods, resources, req.clone(), provider_set).await; - // + let resp = resolve_route(methods, resources, req.clone()).await; let status = match resp.clone() { Ok(msg) => { @@ -215,9 +199,7 @@ impl RpcRouter { let methods = state.router_state.get_methods(); let resources = state.router_state.resources.clone(); tokio::spawn(async move { - // - //if let Ok(msg) = resolve_route(methods, resources, req).await { - if let Ok(msg) = resolve_route(methods, resources, req, None).await { + if let Ok(msg) = resolve_route(methods, resources, req).await { return_extn_response(msg, extn_msg); } }); diff --git a/core/main/src/service/apps/provider_broker.rs b/core/main/src/service/apps/provider_broker.rs index 0ce5f0a57..4f8b408e4 100644 --- a/core/main/src/service/apps/provider_broker.rs +++ b/core/main/src/service/apps/provider_broker.rs @@ -29,12 +29,12 @@ use ripple_sdk::{ ProviderResponsePayload, }, }, - gateway::rpc_gateway_api::{CallContext, CallerSession}, + gateway::rpc_gateway_api::{ApiMessage, CallContext, CallerSession, RpcRequest}, }, log::{debug, error, info, warn}, serde_json, tokio::sync::oneshot, - utils::channel_utils::oneshot_send_and_log, + utils::{channel_utils::oneshot_send_and_log, error::RippleError}, uuid::Uuid, }; use serde::{Deserialize, Serialize}; @@ -76,7 +76,10 @@ pub struct ProviderBroker {} #[derive(Clone, Debug)] struct ProviderMethod { - event_name: &'static str, + // 3 + //event_name: &'static str, + event_name: String, + // provider: CallContext, } @@ -121,7 +124,10 @@ impl ProviderBroker { pst: &PlatformState, capability: String, method: String, - event_name: &'static str, + // 3 + //event_name: &'static str, + event_name: String, + // provider: CallContext, listen_request: ListenRequest, ) { @@ -164,7 +170,10 @@ impl ProviderBroker { pst: &PlatformState, capability: String, method: String, - event_name: &'static str, + // 3 + //event_name: &'static str, + event_name: String, + // provider: CallContext, listen_request: ListenRequest, ) { @@ -175,7 +184,10 @@ impl ProviderBroker { let cap_method = format!("{}:{}", capability, method); AppEvents::add_listener( pst, - event_name.to_string(), + // 3 + //event_name.to_string(), + event_name.clone(), + // provider.clone(), listen_request, ); @@ -212,11 +224,17 @@ impl ProviderBroker { for cap in all_caps { if let Some(provider) = provider_methods.get(&cap) { if let Some(list) = result.get_mut(&provider.provider.app_id) { - list.push(String::from(provider.event_name)); + // 3 + //list.push(String::from(provider.event_name)); + list.push(provider.event_name.clone()); + // } else { result.insert( provider.provider.app_id.clone(), - vec![String::from(provider.event_name)], + // 3 + //vec![String::from(provider.event_name)], + vec![provider.event_name.clone()], + // ); } } @@ -233,7 +251,10 @@ impl ProviderBroker { provider_methods.get(&cap_method).cloned() }; if let Some(provider) = provider_opt { - let event_name = provider.event_name; + // 3 + //let event_name = provider.event_name; + let event_name = provider.event_name.clone(); + // let req_params = request.request.clone(); let app_id_opt = request.app_id.clone(); let c_id = ProviderBroker::start_provider_session(pst, request, provider); @@ -242,7 +263,10 @@ impl ProviderBroker { AppEvents::emit_to_app( pst, app_id, - event_name, + // 3 + //event_name, + &event_name, + // &serde_json::to_value(ProviderRequest { correlation_id: c_id, parameters: req_params, @@ -254,7 +278,10 @@ impl ProviderBroker { debug!("Broadcasting request to all the apps!!"); AppEvents::emit( pst, - event_name, + // 3 + //event_name, + &event_name, + // &serde_json::to_value(ProviderRequest { correlation_id: c_id, parameters: req_params, @@ -417,4 +444,16 @@ impl ProviderBroker { warn!("Focus: No active session for request"); } } + + // + pub async fn handle_passthrough_request( + platform_state: &PlatformState, + rpc_request: RpcRequest, + ) { + println!( + "*** _DEBUG: handle_passthrough_request: rpc_request={:?}", + rpc_request + ); + } + // } diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 535f77d4f..147ecb8d1 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -49,12 +49,17 @@ pub struct ProviderSet { pub request: Option, pub focus: Option, pub response: Option, - pub error: Option, + // 2 + //pub error: Option, + pub error_for: Option, + // pub attributes: Option<&'static ProviderAttributes>, // pub provides: Option, pub provided_by: Option, - pub uses: Option, + pub uses: Option>, + pub event: bool, + pub provides_to: Option, // } @@ -149,7 +154,10 @@ pub fn build_provider_sets( let mut has_caps = false; let mut has_x_allow_focus_for = false; let mut has_x_response_for = false; - let mut has_x_error_for = false; + // 2 + //let mut has_x_error_for = false; + let mut x_error_for = None; + // let mut x_provided_by = None; let mut x_provides = None; let mut x_uses = None; @@ -162,14 +170,22 @@ pub fn build_provider_sets( has_x_provides = tag.get_provides(); has_x_allow_focus_for |= tag.allow_focus_for.is_some(); has_x_response_for |= tag.response_for.is_some(); - has_x_error_for |= tag.error_for.is_some(); + // 2 + //has_x_error_for |= tag.error_for.is_some(); + x_error_for = tag.error_for.clone(); + // x_provided_by = tag.provided_by.clone(); x_provides = tag.provides.clone(); x_uses = tag.uses.clone(); } } - let mut provider_set = ProviderSet::new(); + let mut provider_set = provider_sets + .get(&FireboltOpenRpcMethod::name_with_lowercase_module( + &method.name, + )) + .unwrap_or(&ProviderSet::new()) + .clone(); if let Some(_capability) = has_x_provides { if has_event && has_caps { @@ -181,22 +197,37 @@ pub fn build_provider_sets( if has_x_response_for { provider_set.response = Some(method.clone()); } - if has_x_error_for { - provider_set.error = Some(method.clone()); - } + // 2 + // if has_x_error_for { + // provider_set.error = Some(method.clone()); + // } + provider_set.error_for = x_error_for; + // provider_set.provides = x_provides; } else { // x-provided-by can only be set if x-provides isn't. - provider_set.provided_by = x_provided_by; + provider_set.provided_by = x_provided_by.clone(); + if let Some(provided_by) = x_provided_by { + let mut provided_by_set = provider_sets + .get(&provided_by) + .unwrap_or(&ProviderSet::new()) + .clone(); + + provided_by_set.provides_to = Some(method.name.clone()); + + provider_sets.insert( + FireboltOpenRpcMethod::name_with_lowercase_module(&provided_by), + provided_by_set.to_owned(), + ); + } } provider_set.uses = x_uses; + provider_set.event = has_event; let module: Vec<&str> = method.name.split('.').collect(); provider_set.attributes = ProviderAttributes::get(module[0]); - println!("build_provider_sets: provider_set={:?}", provider_set); - provider_sets.insert( FireboltOpenRpcMethod::name_with_lowercase_module(&method.name), provider_set.to_owned(), diff --git a/core/sdk/src/api/firebolt/provider.rs b/core/sdk/src/api/firebolt/provider.rs index 5fdbb4f09..4bea2aeed 100644 --- a/core/sdk/src/api/firebolt/provider.rs +++ b/core/sdk/src/api/firebolt/provider.rs @@ -123,6 +123,23 @@ impl ProviderResponsePayload { _ => None, } } + + // + pub fn as_value(&self) -> serde_json::Value { + match self { + ProviderResponsePayload::ChallengeResponse(res) => serde_json::to_value(res).unwrap(), + ProviderResponsePayload::ChallengeError(res) => serde_json::to_value(res).unwrap(), + ProviderResponsePayload::PinChallengeResponse(res) => { + serde_json::to_value(res).unwrap() + } + ProviderResponsePayload::KeyboardResult(res) => serde_json::to_value(res).unwrap(), + ProviderResponsePayload::EntityInfoResponse(res) => serde_json::to_value(res).unwrap(), + ProviderResponsePayload::PurchasedContentResponse(res) => { + serde_json::to_value(res).unwrap() + } + } + } + // } #[derive(Serialize, Deserialize)] @@ -156,43 +173,51 @@ pub struct ExternalProviderResponse { #[derive(Debug, Clone, Serialize)] pub struct ProviderAttributes { - pub event: &'static str, + // + //pub event: &'static str, + // pub response_payload_type: ProviderResponsePayloadType, pub error_payload_type: ProviderResponsePayloadType, // //pub capability: &'static str, // - pub method: &'static str, + // 3 + //pub method: &'static str, + // } impl ProviderAttributes { pub fn get(module: &str) -> Option<&'static ProviderAttributes> { match module { "AcknowledgeChallenge" => Some(&ACKNOWLEDGE_CHALLENGE_ATTRIBS), - "PinChallenge" => Some(&ACKNOWLEDGE_CHALLENGE_ATTRIBS), + "PinChallenge" => Some(&PIN_CHALLENGE_ATTRIBS), _ => None, } } } pub const ACKNOWLEDGE_CHALLENGE_ATTRIBS: ProviderAttributes = ProviderAttributes { - event: ACK_CHALLENGE_EVENT, + // + //event: ACK_CHALLENGE_EVENT, + // response_payload_type: ProviderResponsePayloadType::ChallengeResponse, error_payload_type: ProviderResponsePayloadType::ChallengeError, // //capability: ACK_CHALLENGE_CAPABILITY, + //method: "challenge", // - method: "challenge", }; pub const PIN_CHALLENGE_ATTRIBS: ProviderAttributes = ProviderAttributes { - event: PIN_CHALLENGE_EVENT, + // + //event: PIN_CHALLENGE_EVENT, + // response_payload_type: ProviderResponsePayloadType::PinChallengeResponse, error_payload_type: ProviderResponsePayloadType::ChallengeError, // //capability: PIN_CHALLENGE_CAPABILITY, + //method: "challenge", // - method: "challenge", }; #[derive(Debug, Serialize, Deserialize, Clone)] From 0fea010fe9141568ef29dcb03d0fd98713545552 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sat, 13 Jul 2024 17:22:10 -0400 Subject: [PATCH 04/24] x-provided-by Pull pattern: Cleaned-up warnings --- .../firebolt/handlers/provider_registrar.rs | 15 +++++------- core/main/src/firebolt/rpc_router.rs | 8 ++----- core/main/src/service/apps/provider_broker.rs | 16 ++----------- core/main/src/state/openrpc_state.rs | 23 ++++++++----------- core/sdk/src/api/firebolt/provider.rs | 4 +--- 5 files changed, 21 insertions(+), 45 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 54a91553d..2a100a711 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -23,7 +23,7 @@ use crate::{ app_events::AppEvents, provider_broker::{ProviderBroker, ProviderBrokerRequest}, }, - state::{cap, openrpc_state::ProviderSet, platform_state::PlatformState}, + state::{openrpc_state::ProviderSet, platform_state::PlatformState}, }; use jsonrpsee::{ core::{server::rpc_module::Methods, Error, RpcResult}, @@ -37,7 +37,7 @@ use ripple_sdk::{ fb_openrpc::FireboltOpenRpcMethod, fb_pin::PinChallengeResponse, provider::{ - self, ChallengeResponse, ExternalProviderResponse, FocusRequest, + ChallengeResponse, ExternalProviderError, ExternalProviderResponse, FocusRequest, ProviderRequestPayload, ProviderResponse, ProviderResponsePayload, ProviderResponsePayloadType, }, @@ -401,7 +401,7 @@ impl ProviderRegistrar { .unwrap(); } - if let Some(error) = &provider_set.error_for { + if provider_set.error_for.is_some() { if let Some(attributes) = provider_set.attributes { println!( "*** _DEBUG: Registering method: Error function: method_name={}", @@ -412,9 +412,6 @@ impl ProviderRegistrar { method_name ); - // let error_method = - // FireboltOpenRpcMethod::name_with_lowercase_module(method_name).leak(); - rpc_module .register_async_method( //error_method, @@ -453,7 +450,7 @@ impl ProviderRegistrar { } } - if let Some(provided_by) = &provider_set.provided_by { + if provider_set.provided_by.is_some() { println!( "*** _DEBUG: Registering method: Provider invoker: method_name={}", method_name @@ -559,7 +556,7 @@ impl ProviderRegistrar { return Ok(provider_response_payload .as_value()); } - Err(e) => { + Err(_) => { return Err(Error::Custom( String::from( "Error returning from provider", @@ -587,7 +584,7 @@ impl ProviderRegistrar { } // Register focus function - if let Some(method) = provider_set.focus.clone() { + if provider_set.focus.is_some() { println!( "*** _DEBUG: Registering method: Focus function: method_name={}", method_name diff --git a/core/main/src/firebolt/rpc_router.rs b/core/main/src/firebolt/rpc_router.rs index bdd633dbd..23aad5f50 100644 --- a/core/main/src/firebolt/rpc_router.rs +++ b/core/main/src/firebolt/rpc_router.rs @@ -41,12 +41,8 @@ use ripple_sdk::{ use std::sync::{Arc, RwLock}; use crate::{ - service::{apps::provider_broker::ProviderBroker, telemetry_builder::TelemetryBuilder}, - state::{ - openrpc_state::ProviderSet, - platform_state::{self, PlatformState}, - session_state::Session, - }, + service::telemetry_builder::TelemetryBuilder, + state::{platform_state::PlatformState, session_state::Session}, utils::router_utils::{return_api_message_for_transport, return_extn_response}, }; diff --git a/core/main/src/service/apps/provider_broker.rs b/core/main/src/service/apps/provider_broker.rs index 4f8b408e4..a2a8ab958 100644 --- a/core/main/src/service/apps/provider_broker.rs +++ b/core/main/src/service/apps/provider_broker.rs @@ -29,12 +29,12 @@ use ripple_sdk::{ ProviderResponsePayload, }, }, - gateway::rpc_gateway_api::{ApiMessage, CallContext, CallerSession, RpcRequest}, + gateway::rpc_gateway_api::{CallContext, CallerSession}, }, log::{debug, error, info, warn}, serde_json, tokio::sync::oneshot, - utils::{channel_utils::oneshot_send_and_log, error::RippleError}, + utils::channel_utils::oneshot_send_and_log, uuid::Uuid, }; use serde::{Deserialize, Serialize}; @@ -444,16 +444,4 @@ impl ProviderBroker { warn!("Focus: No active session for request"); } } - - // - pub async fn handle_passthrough_request( - platform_state: &PlatformState, - rpc_request: RpcRequest, - ) { - println!( - "*** _DEBUG: handle_passthrough_request: rpc_request={:?}", - rpc_request - ); - } - // } diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index c896a5f48..bbca60bcc 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -15,22 +15,19 @@ // SPDX-License-Identifier: Apache-2.0 // -use ripple_sdk::log::{debug, error}; -use ripple_sdk::{api::firebolt::fb_openrpc::CapabilityPolicy, serde_json}; -use ripple_sdk::{ - api::{ - firebolt::{ - fb_capabilities::FireboltPermission, - fb_openrpc::{ - CapabilitySet, FireboltOpenRpc, FireboltOpenRpcMethod, FireboltSemanticVersion, - FireboltVersionManifest, OpenRPCParser, - }, - provider::ProviderAttributes, +use ripple_sdk::api::{ + firebolt::{ + fb_capabilities::FireboltPermission, + fb_openrpc::{ + CapabilitySet, FireboltOpenRpc, FireboltOpenRpcMethod, FireboltSemanticVersion, + FireboltVersionManifest, OpenRPCParser, }, - manifest::exclusory::{Exclusory, ExclusoryImpl}, + provider::ProviderAttributes, }, - semver::Op, + manifest::exclusory::{Exclusory, ExclusoryImpl}, }; +use ripple_sdk::log::{debug, error}; +use ripple_sdk::{api::firebolt::fb_openrpc::CapabilityPolicy, serde_json}; use std::{ collections::HashMap, sync::{Arc, RwLock}, diff --git a/core/sdk/src/api/firebolt/provider.rs b/core/sdk/src/api/firebolt/provider.rs index 2dadc5905..3e41fbfa5 100644 --- a/core/sdk/src/api/firebolt/provider.rs +++ b/core/sdk/src/api/firebolt/provider.rs @@ -23,9 +23,7 @@ use crate::api::device::entertainment_data::{ use super::{ fb_keyboard::{KeyboardSessionRequest, KeyboardSessionResponse}, - fb_pin::{ - PinChallengeRequest, PinChallengeResponse, PIN_CHALLENGE_CAPABILITY, PIN_CHALLENGE_EVENT, - }, + fb_pin::{PinChallengeRequest, PinChallengeResponse}, }; pub const ACK_CHALLENGE_EVENT: &str = "acknowledgechallenge.onRequestChallenge"; From 9654bceaa22d8d04950c224c9eb76db53d35e56c Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sat, 13 Jul 2024 22:24:14 -0400 Subject: [PATCH 05/24] x-provided-by Pull pattern: Refactored ProviderRegistrar --- .../firebolt/handlers/provider_registrar.rs | 714 +++++++++--------- 1 file changed, 342 insertions(+), 372 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 2a100a711..079ef6a7d 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -38,8 +38,8 @@ use ripple_sdk::{ fb_pin::PinChallengeResponse, provider::{ ChallengeResponse, ExternalProviderError, ExternalProviderResponse, FocusRequest, - ProviderRequestPayload, ProviderResponse, ProviderResponsePayload, - ProviderResponsePayloadType, + ProviderAttributes, ProviderRequestPayload, ProviderResponse, + ProviderResponsePayload, ProviderResponsePayloadType, }, }, gateway::rpc_gateway_api::{CallContext, CallerSession}, @@ -265,410 +265,380 @@ impl ProviderRegistrar { // } // } // } + + fn register_method_app_event_listener( + method_name: &'static str, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method_app_event_listener: method_name={}", + method_name + ); + info!( + "register_method_app_event_listener: method_name={}", + method_name + ); + + rpc_module + .register_async_method(method_name, move |params, context| async move { + println!( + "*** _DEBUG: App event listener registration: method={}", + context.method + ); + info!("App event listener registration: method={}", context.method); + + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: ListenRequest = params_sequence.next().unwrap(); + let listen = request.listen; + + AppEvents::add_listener( + &context.platform_state, + context.method.clone(), + call_context, + request, + ); + Ok(ListenerResponse { + listening: listen, + event: context.method.clone(), + }) + }) + .unwrap(); + } + + fn register_method_provider( + method_name: &'static str, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method_provider: method_name={}", + method_name + ); + info!("register_method_provider: method_name={}", method_name); + rpc_module + .register_async_method(method_name, move |params, context| async move { + println!( + "*** _DEBUG: Provider registration: method={}", + context.method + ); + info!("Provider registration: method={}", context.method); + + if let Some(provides) = &context.provider_set.provides { + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: ListenRequest = params_sequence.next().unwrap(); + let listening = request.listen; + + ProviderBroker::register_or_unregister_provider( + &context.platform_state, + provides.clone(), + context.method.clone(), + context.method.clone(), + call_context, + request, + ) + .await; + + Ok(ListenerResponse { + listening, + event: context.method.clone(), + }) + } else { + Err(Error::Custom("Missing provides attribute".to_string())) + } + }) + .unwrap(); + } + + fn register_method_app_event_emitter( + method_name: &'static str, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method_app_event_emitter: method_name={}", + method_name + ); + info!( + "register_method_app_event_emitter: method_name={}", + method_name + ); + + rpc_module + .register_async_method(method_name, move |params, context| async move { + println!("*** _DEBUG: App event emitter: method={}", context.method); + info!("App event emitter: method={}", context.method); + if let Some(event) = &context.provider_set.provides_to { + let mut params_sequence = params.sequence(); + let _call_context: CallContext = params_sequence.next().unwrap(); + let result: Value = params_sequence.next().unwrap(); + + AppEvents::emit(&context.platform_state, event, &result).await; + } + + Ok(None) as RpcResult> + }) + .unwrap(); + } + + fn register_method_error( + method_name: &'static str, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method_error: method_name={}", + method_name + ); + info!("register_method_error: method_name={}", method_name); + + rpc_module + .register_async_method(method_name, move |params, context| async move { + println!("*** _DEBUG: Provider error: method={}", context.method); + info!("Provider error: method={}", context.method); + let params_sequence = params.sequence(); + + if let Some(attributes) = context.provider_set.attributes { + if let Some(provider_response) = ProviderRegistrar::get_provider_response( + attributes.error_payload_type.clone(), + params_sequence, + ) { + ProviderBroker::provider_response( + &context.platform_state, + provider_response, + ) + .await; + } + } else { + println!( + "*** _DEBUG: Provider error: NO ATTRIBUTES: method_name={}", + method_name + ); + error!("Provider error: NO ATTRIBUTES: method_name={}", method_name); + return Err(Error::Custom(String::from("Missing provider attributes"))); + } + + Ok(None) as RpcResult> + }) + .unwrap(); + } + + fn register_method_provider_invoker( + method_name: &'static str, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method_provider_invoker: method_name={}", + method_name + ); + info!( + "register_method_provider_invoker: method_name={}", + method_name + ); + + rpc_module + .register_async_method(method_name, move |params, context| async move { + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let params: Value = params_sequence.next().unwrap(); + + println!("*** _DEBUG: Provider invoker: method={}", context.method); + info!("Provider invoker: method={}", context.method); + + if let Some(provided_by) = &context.provider_set.provided_by { + let provider_map = context.platform_state.open_rpc_state.get_provider_map(); + + if let Some(provided_by_set) = provider_map.get( + &FireboltOpenRpcMethod::name_with_lowercase_module(provided_by), + ) { + if let Some(capability) = &provided_by_set.provides { + let (provider_response_payload_tx, provider_response_payload_rx) = + oneshot::channel::(); + + let caller = CallerSession { + session_id: Some(call_context.session_id.clone()), + app_id: Some(call_context.app_id.clone()), + }; + + let provider_broker_request = ProviderBrokerRequest { + capability: capability.clone(), + method: provided_by.clone(), + caller, + request: ProviderRequestPayload::Generic(params.to_string()), + tx: provider_response_payload_tx, + app_id: None, + }; + + ProviderBroker::invoke_method( + &context.platform_state, + provider_broker_request, + ) + .await; + + match timeout( + Duration::from_millis(DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS), + provider_response_payload_rx, + ) + .await + { + Ok(result) => match result { + Ok(provider_response_payload) => { + return Ok(provider_response_payload.as_value()); + } + Err(_) => { + return Err(Error::Custom(String::from( + "Error returning from provider", + ))); + } + }, + Err(_) => { + return Err(Error::Custom(String::from( + "Provider response timeout", + ))); + } + } + } + } + } + + Err(Error::Custom(String::from( + "Unexpected schema configuration", + ))) + }) + .unwrap(); + } + + fn register_method_focus( + method_name: &'static str, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method_focus: method_name={}", + method_name + ); + info!("register_method_focus: method_name={}", method_name); + + rpc_module + .register_async_method(method_name, move |params, context| async move { + println!("*** _DEBUG: Provider focus: method={}", context.method); + info!("Provider focus: method={}", context.method); + + if let Some(provides) = &context.provider_set.provides { + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: FocusRequest = params_sequence.next().unwrap(); + + ProviderBroker::focus( + &context.platform_state, + call_context, + provides.clone(), + request, + ) + .await; + + Ok(None) as RpcResult> + } else { + Err(Error::Custom("Missing provides attribute".to_string())) + } + }) + .unwrap(); + } + + fn register_method_response( + method_name: &'static str, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method_response: method_name={}", + method_name + ); + info!("register_method_response: method_name={}", method_name); + + rpc_module + .register_async_method(method_name, move |params, context| async move { + println!("*** _DEBUG: Provider response: method={}", context.method); + info!("Provider response: method={}", context.method); + + let params_sequence = params.sequence(); + + if let Some(attributes) = context.provider_set.attributes { + if let Some(provider_response) = ProviderRegistrar::get_provider_response( + attributes.response_payload_type.clone(), + params_sequence, + ) { + ProviderBroker::provider_response( + &context.platform_state, + provider_response, + ) + .await; + } + } else { + println!( + "*** _DEBUG: Provider response: NO ATTRIBUTES: method_name={}", + method_name + ); + error!( + "Provider response: NO ATTRIBUTES: method_name={}", + method_name + ); + return Err(Error::Custom(String::from("Missing provider attributes"))); + } + + Ok(None) as RpcResult> + }) + .unwrap(); + } + pub fn register(platform_state: &PlatformState, methods: &mut Methods) { let provider_map = platform_state.open_rpc_state.get_provider_map(); for method_name in provider_map.clone().keys() { if let Some(provider_set) = provider_map.get(method_name) { - let method_name_lc = + let method_name_lcm = FireboltOpenRpcMethod::name_with_lowercase_module(method_name).leak(); let rpc_module_context = RpcModuleContext::new( platform_state.clone(), - method_name_lc.into(), + method_name_lcm.into(), provider_set.clone(), ); + let mut rpc_module = RpcModule::new(rpc_module_context.clone()); if provider_set.event { - if let Some(provided_by) = &provider_set.provided_by { + if provider_set.provided_by.is_some() { // Register app event listener - println!("*** _DEBUG: Registering method: App event listener: method_name={}, provided_by={}", - method_name, provided_by - ); - info!("Registering method: App event listener: method_name={}, provided_by={}", - method_name, provided_by - ); - rpc_module - .register_async_method( - method_name_lc, - move |params, context| async move { - println!( - "*** _DEBUG: App event listener registration: method={}", - context.method - ); - info!( - "App event listener registration: method={}", - context.method - ); - - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let request: ListenRequest = params_sequence.next().unwrap(); - let listen = request.listen; - - AppEvents::add_listener( - &context.platform_state, - context.method.clone(), - call_context, - request, - ); - Ok(ListenerResponse { - listening: listen, - event: context.method.clone(), - }) - }, - ) - .unwrap(); + Self::register_method_app_event_listener(method_name_lcm, &mut rpc_module); } else if provider_set.provides.is_some() || provider_set.provides_to.is_some() { - println!( - "*** _DEBUG: Registering method: Provider: method_name={}", - method_name - ); - info!("Registering method: Provider: method_name={}", method_name); - rpc_module - .register_async_method( - method_name_lc, - move |params, context| async move { - println!( - "*** _DEBUG: Provider registration: method={}", - context.method - ); - info!("Provider registration: method={}", context.method); - - if let Some(provides) = &context.provider_set.provides { - let mut params_sequence = params.sequence(); - let call_context: CallContext = - params_sequence.next().unwrap(); - let request: ListenRequest = - params_sequence.next().unwrap(); - let listening = request.listen; - - ProviderBroker::register_or_unregister_provider( - &context.platform_state, - provides.clone(), - context.method.clone(), - context.method.clone(), - call_context, - request, - ) - .await; - - Ok(ListenerResponse { - listening, - event: context.method.clone(), - }) - } else { - Err(Error::Custom("Missing provides attribute".to_string())) - } - }, - ) - .unwrap(); + // Register provider + Self::register_method_provider(method_name_lcm, &mut rpc_module); } } else { if provider_set.provides_to.is_some() { - println!( - "*** _DEBUG: Registering method: App event emitter: method_name={}", - method_name - ); - info!( - "Registering method: App event emitter: method_name={}", - method_name - ); - rpc_module - .register_async_method( - method_name_lc, - move |params, context| async move { - println!( - "*** _DEBUG: App event emitter: method={}", - context.method - ); - info!("App event emitter: method={}", context.method); - if let Some(event) = &context.provider_set.provides_to { - let mut params_sequence = params.sequence(); - let _call_context: CallContext = - params_sequence.next().unwrap(); - let result: Value = params_sequence.next().unwrap(); - - AppEvents::emit(&context.platform_state, event, &result) - .await; - } - - Ok(None) as RpcResult> - }, - ) - .unwrap(); + // Register app event emitter + Self::register_method_app_event_emitter(method_name_lcm, &mut rpc_module); } if provider_set.error_for.is_some() { - if let Some(attributes) = provider_set.attributes { - println!( - "*** _DEBUG: Registering method: Error function: method_name={}", - method_name - ); - info!( - "Registering method: Error function: method_name={}", - method_name - ); - - rpc_module - .register_async_method( - //error_method, - method_name_lc, - move |params, context| async move { - println!( - "*** _DEBUG: Provider error: method={}", - context.method - ); - info!("Provider error: method={}", context.method); - let params_sequence = params.sequence(); - - if let Some(provider_response) = - ProviderRegistrar::get_provider_response( - attributes.error_payload_type.clone(), - params_sequence, - ) - { - ProviderBroker::provider_response( - &context.platform_state, - provider_response, - ) - .await; - } - - Ok(None) as RpcResult> - }, - ) - .unwrap(); - } else { - println!("*** _DEBUG: Registering method: Error function: NO ATTRIBUTES: method_name={}", method_name); - error!( - "Registering method: Error function: NO ATTRIBUTES: method_name={}", - method_name - ); - } + // Register error function + Self::register_method_error(method_name_lcm, &mut rpc_module); } if provider_set.provided_by.is_some() { - println!( - "*** _DEBUG: Registering method: Provider invoker: method_name={}", - method_name - ); - info!( - "Registering method: Provider invoker: method_name={}", - method_name - ); - - rpc_module - .register_async_method( - method_name_lc, - move |params, context| async move { - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let params: Value = params_sequence.next().unwrap(); - - println!( - "*** _DEBUG: Provider invoker: method={}", - context.method - ); - info!("Provider invoker: method={}", context.method); - - if let Some(provided_by) = &context.provider_set.provided_by { - let provider_map = context - .platform_state - .open_rpc_state - .get_provider_map(); - - if let Some(provided_by_set) = provider_map.get( - &FireboltOpenRpcMethod::name_with_lowercase_module( - provided_by, - ), - ) { - if let Some(capability) = &provided_by_set.provides { - let ( - provider_response_payload_tx, - provider_response_payload_rx, - ) = oneshot::channel::(); - - let caller = CallerSession { - session_id: Some( - call_context.session_id.clone(), - ), - app_id: Some(call_context.app_id.clone()), - }; - - let provider_broker_request = - ProviderBrokerRequest { - capability: capability.clone(), - method: provided_by.clone(), - caller, - request: ProviderRequestPayload::Generic( - params.to_string(), - ), - tx: provider_response_payload_tx, - app_id: None, - }; - - ProviderBroker::invoke_method( - &context.platform_state, - provider_broker_request, - ) - .await; - - // match provider_response_payload_rx.await { - // Ok(provider_response_payload) => { - // return Ok( - // provider_response_payload.as_value(), // TODO: Is this right? - // ); - // } - // Err(e) => { - // return Err(Error::Custom(String::from( - // "Error returning from provider", - // ))); - // } - // } - - // let provider_response = timeout( - // Duration::from_millis( - // DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS, - // ), - // provider_response_payload_rx, - // ) - // .await - // .map_err(|_| { - // Err(Error::Custom(String::from( - // "Privder response timeout", - // ))) - // })?; - // return provider_response; - - match timeout( - Duration::from_millis( - DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS, - ), - provider_response_payload_rx, - ) - .await - { - Ok(result) => match result { - Ok(provider_response_payload) => { - return Ok(provider_response_payload - .as_value()); - } - Err(_) => { - return Err(Error::Custom( - String::from( - "Error returning from provider", - ), - )); - } - }, - Err(_) => { - return Err(Error::Custom(String::from( - "Provider response timeout", - ))); - } - } - } - } - } - - Err(Error::Custom(String::from( - "Unexpected schema configuration", - ))) - }, - ) - .unwrap(); + // Register provider invoker + Self::register_method_provider_invoker(method_name_lcm, &mut rpc_module); } } - // Register focus function if provider_set.focus.is_some() { - println!( - "*** _DEBUG: Registering method: Focus function: method_name={}", - method_name - ); - info!( - "Registering method: Focus function: method_name={}", - method_name - ); - - // let focus_method = - // FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); - - rpc_module - .register_async_method(method_name_lc, move |params, context| async move { - println!("*** _DEBUG: Provider focus: method={}", context.method); - info!("Provider focus: method={}", context.method); - - if let Some(provides) = &context.provider_set.provides { - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let request: FocusRequest = params_sequence.next().unwrap(); - - ProviderBroker::focus( - &context.platform_state, - call_context, - provides.clone(), - request, - ) - .await; - - Ok(None) as RpcResult> - } else { - Err(Error::Custom("Missing provides attribute".to_string())) - } - }) - .unwrap(); + // Register focus function + Self::register_method_focus(method_name_lcm, &mut rpc_module); } - if let Some(attributes) = provider_set.attributes { + if let Some(method) = provider_set.response.clone() { // Register response function - if let Some(method) = provider_set.response.clone() { - println!( - "*** _DEBUG: Registering method: Response function: method_name={}", - method_name - ); - info!( - "Registering method: Response function: method_name={}", - method_name - ); - - let response_method = - FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); - - rpc_module - .register_async_method( - response_method, - move |params, context| async move { - println!( - "*** _DEBUG: Provider response: method={}", - context.method - ); - info!("Provider response: method={}", context.method); - - let params_sequence = params.sequence(); - - if let Some(provider_response) = - ProviderRegistrar::get_provider_response( - attributes.response_payload_type.clone(), - params_sequence, - ) - { - ProviderBroker::provider_response( - &context.platform_state, - provider_response, - ) - .await; - } - - Ok(None) as RpcResult> - }, - ) - .unwrap(); - } + Self::register_method_response(method_name_lcm, &mut rpc_module); } + methods .merge(register_aliases(platform_state, rpc_module)) .ok(); From f138a806fb10a35acc31112a0b811a3d32d667db Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sun, 14 Jul 2024 13:09:15 -0400 Subject: [PATCH 06/24] x-provided-by Pull pattern: Clean-up --- core/main/src/firebolt/handlers/provider_registrar.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 079ef6a7d..ac5df3307 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -38,8 +38,8 @@ use ripple_sdk::{ fb_pin::PinChallengeResponse, provider::{ ChallengeResponse, ExternalProviderError, ExternalProviderResponse, FocusRequest, - ProviderAttributes, ProviderRequestPayload, ProviderResponse, - ProviderResponsePayload, ProviderResponsePayloadType, + ProviderRequestPayload, ProviderResponse, ProviderResponsePayload, + ProviderResponsePayloadType, }, }, gateway::rpc_gateway_api::{CallContext, CallerSession}, @@ -634,7 +634,7 @@ impl ProviderRegistrar { Self::register_method_focus(method_name_lcm, &mut rpc_module); } - if let Some(method) = provider_set.response.clone() { + if provider_set.response.is_some() { // Register response function Self::register_method_response(method_name_lcm, &mut rpc_module); } From 9d2a91ab1ed875c1b49f696bc1f09807aeee1a3e Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sun, 14 Jul 2024 14:19:46 -0400 Subject: [PATCH 07/24] x-provided-by Pull pattern: Refactored for testability --- .../firebolt/handlers/provider_registrar.rs | 460 ++++++++++-------- core/main/src/service/user_grants.rs | 4 +- core/sdk/src/api/firebolt/fb_openrpc.rs | 3 + 3 files changed, 251 insertions(+), 216 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index ac5df3307..5d07b23ad 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -15,7 +15,7 @@ // SPDX-License-Identifier: Apache-2.0 // -use std::time::Duration; +use std::{sync::Arc, time::Duration}; use crate::{ firebolt::rpc::register_aliases, @@ -27,7 +27,7 @@ use crate::{ }; use jsonrpsee::{ core::{server::rpc_module::Methods, Error, RpcResult}, - types::{error::CallError, ParamsSequence}, + types::{error::CallError, Params, ParamsSequence}, RpcModule, }; use ripple_sdk::{ @@ -266,6 +266,33 @@ impl ProviderRegistrar { // } // } + async fn callback_app_event_listener( + params: Params<'static>, + context: Arc, + ) -> Result { + println!( + "*** _DEBUG: callback_app_event_listener: method={}", + context.method + ); + info!("callback_app_event_listener: method={}", context.method); + + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: ListenRequest = params_sequence.next().unwrap(); + let listen = request.listen; + + AppEvents::add_listener( + &context.platform_state, + context.method.clone(), + call_context, + request, + ); + Ok(ListenerResponse { + listening: listen, + event: context.method.clone(), + }) + } + fn register_method_app_event_listener( method_name: &'static str, rpc_module: &mut RpcModule, @@ -280,32 +307,45 @@ impl ProviderRegistrar { ); rpc_module - .register_async_method(method_name, move |params, context| async move { - println!( - "*** _DEBUG: App event listener registration: method={}", - context.method - ); - info!("App event listener registration: method={}", context.method); - - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let request: ListenRequest = params_sequence.next().unwrap(); - let listen = request.listen; - - AppEvents::add_listener( - &context.platform_state, - context.method.clone(), - call_context, - request, - ); - Ok(ListenerResponse { - listening: listen, - event: context.method.clone(), - }) - }) + .register_async_method(method_name, Self::callback_app_event_listener) .unwrap(); } + async fn callback_register_provider( + params: Params<'static>, + context: Arc, + ) -> Result { + println!( + "*** _DEBUG: callback_register_provider: method={}", + context.method + ); + info!("callback_register_provider: method={}", context.method); + + if let Some(provides) = &context.provider_set.provides { + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: ListenRequest = params_sequence.next().unwrap(); + let listening = request.listen; + + ProviderBroker::register_or_unregister_provider( + &context.platform_state, + provides.clone(), + context.method.clone(), + context.method.clone(), + call_context, + request, + ) + .await; + + Ok(ListenerResponse { + listening, + event: context.method.clone(), + }) + } else { + Err(Error::Custom("Missing provides attribute".to_string())) + } + } + fn register_method_provider( method_name: &'static str, rpc_module: &mut RpcModule, @@ -316,40 +356,31 @@ impl ProviderRegistrar { ); info!("register_method_provider: method_name={}", method_name); rpc_module - .register_async_method(method_name, move |params, context| async move { - println!( - "*** _DEBUG: Provider registration: method={}", - context.method - ); - info!("Provider registration: method={}", context.method); - - if let Some(provides) = &context.provider_set.provides { - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let request: ListenRequest = params_sequence.next().unwrap(); - let listening = request.listen; - - ProviderBroker::register_or_unregister_provider( - &context.platform_state, - provides.clone(), - context.method.clone(), - context.method.clone(), - call_context, - request, - ) - .await; - - Ok(ListenerResponse { - listening, - event: context.method.clone(), - }) - } else { - Err(Error::Custom("Missing provides attribute".to_string())) - } - }) + .register_async_method(method_name, Self::callback_register_provider) .unwrap(); } + async fn callback_app_event_emitter( + params: Params<'static>, + context: Arc, + ) -> Result, Error> { + println!( + "*** _DEBUG: callback_app_event_emitter: method={}", + context.method + ); + info!("callback_app_event_emitter: method={}", context.method); + if let Some(event) = &context.provider_set.provides_to { + let mut params_sequence = params.sequence(); + let _call_context: CallContext = params_sequence.next().unwrap(); + let result: Value = params_sequence.next().unwrap(); + + AppEvents::emit(&context.platform_state, event, &result).await; + } + + //Ok(None) as RpcResult> + Ok(None) + } + fn register_method_app_event_emitter( method_name: &'static str, rpc_module: &mut RpcModule, @@ -364,22 +395,40 @@ impl ProviderRegistrar { ); rpc_module - .register_async_method(method_name, move |params, context| async move { - println!("*** _DEBUG: App event emitter: method={}", context.method); - info!("App event emitter: method={}", context.method); - if let Some(event) = &context.provider_set.provides_to { - let mut params_sequence = params.sequence(); - let _call_context: CallContext = params_sequence.next().unwrap(); - let result: Value = params_sequence.next().unwrap(); - - AppEvents::emit(&context.platform_state, event, &result).await; - } - - Ok(None) as RpcResult> - }) + .register_async_method(method_name, Self::callback_app_event_emitter) .unwrap(); } + async fn callback_error( + params: Params<'static>, + context: Arc, + ) -> Result, Error> { + println!("*** _DEBUG: callback_error: method={}", context.method); + info!("callback_error: method={}", context.method); + let params_sequence = params.sequence(); + + if let Some(attributes) = context.provider_set.attributes { + if let Some(provider_response) = ProviderRegistrar::get_provider_response( + attributes.error_payload_type.clone(), + params_sequence, + ) { + ProviderBroker::provider_response(&context.platform_state, provider_response).await; + } + } else { + println!( + "*** _DEBUG: callback_error: NO ATTRIBUTES: context.method={}", + context.method + ); + error!( + "callback_error: NO ATTRIBUTES: context.method={}", + context.method + ); + return Err(Error::Custom(String::from("Missing provider attributes"))); + } + + Ok(None) as RpcResult> + } + fn register_method_error( method_name: &'static str, rpc_module: &mut RpcModule, @@ -391,34 +440,78 @@ impl ProviderRegistrar { info!("register_method_error: method_name={}", method_name); rpc_module - .register_async_method(method_name, move |params, context| async move { - println!("*** _DEBUG: Provider error: method={}", context.method); - info!("Provider error: method={}", context.method); - let params_sequence = params.sequence(); - - if let Some(attributes) = context.provider_set.attributes { - if let Some(provider_response) = ProviderRegistrar::get_provider_response( - attributes.error_payload_type.clone(), - params_sequence, - ) { - ProviderBroker::provider_response( - &context.platform_state, - provider_response, - ) + .register_async_method(method_name, Self::callback_error) + .unwrap(); + } + + async fn callback_provider_invoker( + params: Params<'static>, + context: Arc, + ) -> Result { + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let params: Value = params_sequence.next().unwrap(); + + println!( + "*** _DEBUG: callback_provider_invoker: method={}", + context.method + ); + info!("callback_provider_invoker: method={}", context.method); + + if let Some(provided_by) = &context.provider_set.provided_by { + let provider_map = context.platform_state.open_rpc_state.get_provider_map(); + + if let Some(provided_by_set) = provider_map.get( + &FireboltOpenRpcMethod::name_with_lowercase_module(provided_by), + ) { + if let Some(capability) = &provided_by_set.provides { + let (provider_response_payload_tx, provider_response_payload_rx) = + oneshot::channel::(); + + let caller = CallerSession { + session_id: Some(call_context.session_id.clone()), + app_id: Some(call_context.app_id.clone()), + }; + + let provider_broker_request = ProviderBrokerRequest { + capability: capability.clone(), + method: provided_by.clone(), + caller, + request: ProviderRequestPayload::Generic(params.to_string()), + tx: provider_response_payload_tx, + app_id: None, + }; + + ProviderBroker::invoke_method(&context.platform_state, provider_broker_request) .await; + + match timeout( + Duration::from_millis(DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS), + provider_response_payload_rx, + ) + .await + { + Ok(result) => match result { + Ok(provider_response_payload) => { + return Ok(provider_response_payload.as_value()); + } + Err(_) => { + return Err(Error::Custom(String::from( + "Error returning from provider", + ))); + } + }, + Err(_) => { + return Err(Error::Custom(String::from("Provider response timeout"))); + } } - } else { - println!( - "*** _DEBUG: Provider error: NO ATTRIBUTES: method_name={}", - method_name - ); - error!("Provider error: NO ATTRIBUTES: method_name={}", method_name); - return Err(Error::Custom(String::from("Missing provider attributes"))); } + } + } - Ok(None) as RpcResult> - }) - .unwrap(); + Err(Error::Custom(String::from( + "Unexpected schema configuration", + ))) } fn register_method_provider_invoker( @@ -435,77 +528,36 @@ impl ProviderRegistrar { ); rpc_module - .register_async_method(method_name, move |params, context| async move { - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let params: Value = params_sequence.next().unwrap(); - - println!("*** _DEBUG: Provider invoker: method={}", context.method); - info!("Provider invoker: method={}", context.method); - - if let Some(provided_by) = &context.provider_set.provided_by { - let provider_map = context.platform_state.open_rpc_state.get_provider_map(); - - if let Some(provided_by_set) = provider_map.get( - &FireboltOpenRpcMethod::name_with_lowercase_module(provided_by), - ) { - if let Some(capability) = &provided_by_set.provides { - let (provider_response_payload_tx, provider_response_payload_rx) = - oneshot::channel::(); - - let caller = CallerSession { - session_id: Some(call_context.session_id.clone()), - app_id: Some(call_context.app_id.clone()), - }; - - let provider_broker_request = ProviderBrokerRequest { - capability: capability.clone(), - method: provided_by.clone(), - caller, - request: ProviderRequestPayload::Generic(params.to_string()), - tx: provider_response_payload_tx, - app_id: None, - }; - - ProviderBroker::invoke_method( - &context.platform_state, - provider_broker_request, - ) - .await; - - match timeout( - Duration::from_millis(DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS), - provider_response_payload_rx, - ) - .await - { - Ok(result) => match result { - Ok(provider_response_payload) => { - return Ok(provider_response_payload.as_value()); - } - Err(_) => { - return Err(Error::Custom(String::from( - "Error returning from provider", - ))); - } - }, - Err(_) => { - return Err(Error::Custom(String::from( - "Provider response timeout", - ))); - } - } - } - } - } - - Err(Error::Custom(String::from( - "Unexpected schema configuration", - ))) - }) + .register_async_method(method_name, Self::callback_provider_invoker) .unwrap(); } + async fn callback_focus( + params: Params<'static>, + context: Arc, + ) -> Result, Error> { + println!("*** _DEBUG: callback_focus: method={}", context.method); + info!("callback_focus: method={}", context.method); + + if let Some(provides) = &context.provider_set.provides { + let mut params_sequence = params.sequence(); + let call_context: CallContext = params_sequence.next().unwrap(); + let request: FocusRequest = params_sequence.next().unwrap(); + + ProviderBroker::focus( + &context.platform_state, + call_context, + provides.clone(), + request, + ) + .await; + + Ok(None) as RpcResult> + } else { + Err(Error::Custom("Missing provides attribute".to_string())) + } + } + fn register_method_focus( method_name: &'static str, rpc_module: &mut RpcModule, @@ -517,31 +569,41 @@ impl ProviderRegistrar { info!("register_method_focus: method_name={}", method_name); rpc_module - .register_async_method(method_name, move |params, context| async move { - println!("*** _DEBUG: Provider focus: method={}", context.method); - info!("Provider focus: method={}", context.method); - - if let Some(provides) = &context.provider_set.provides { - let mut params_sequence = params.sequence(); - let call_context: CallContext = params_sequence.next().unwrap(); - let request: FocusRequest = params_sequence.next().unwrap(); - - ProviderBroker::focus( - &context.platform_state, - call_context, - provides.clone(), - request, - ) - .await; - - Ok(None) as RpcResult> - } else { - Err(Error::Custom("Missing provides attribute".to_string())) - } - }) + .register_async_method(method_name, Self::callback_focus) .unwrap(); } + async fn callback_response( + params: Params<'static>, + context: Arc, + ) -> Result, Error> { + println!("*** _DEBUG: callback_response: method={}", context.method); + info!("callback_response: method={}", context.method); + + let params_sequence = params.sequence(); + + if let Some(attributes) = context.provider_set.attributes { + if let Some(provider_response) = ProviderRegistrar::get_provider_response( + attributes.response_payload_type.clone(), + params_sequence, + ) { + ProviderBroker::provider_response(&context.platform_state, provider_response).await; + } + } else { + println!( + "*** _DEBUG: callback_response: NO ATTRIBUTES: context.method={}", + context.method + ); + error!( + "callback_response: NO ATTRIBUTES: context.method={}", + context.method + ); + return Err(Error::Custom(String::from("Missing provider attributes"))); + } + + Ok(None) as RpcResult> + } + fn register_method_response( method_name: &'static str, rpc_module: &mut RpcModule, @@ -553,37 +615,7 @@ impl ProviderRegistrar { info!("register_method_response: method_name={}", method_name); rpc_module - .register_async_method(method_name, move |params, context| async move { - println!("*** _DEBUG: Provider response: method={}", context.method); - info!("Provider response: method={}", context.method); - - let params_sequence = params.sequence(); - - if let Some(attributes) = context.provider_set.attributes { - if let Some(provider_response) = ProviderRegistrar::get_provider_response( - attributes.response_payload_type.clone(), - params_sequence, - ) { - ProviderBroker::provider_response( - &context.platform_state, - provider_response, - ) - .await; - } - } else { - println!( - "*** _DEBUG: Provider response: NO ATTRIBUTES: method_name={}", - method_name - ); - error!( - "Provider response: NO ATTRIBUTES: method_name={}", - method_name - ); - return Err(Error::Custom(String::from("Missing provider attributes"))); - } - - Ok(None) as RpcResult> - }) + .register_async_method(method_name, Self::callback_response) .unwrap(); } diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index 702b67c76..3dc2f89cb 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -2002,7 +2002,7 @@ mod tests { &state_c, String::from(PIN_CHALLENGE_CAPABILITY), String::from("challenge"), - PIN_CHALLENGE_EVENT, + String::from(PIN_CHALLENGE_EVENT), ctx_c.clone(), ListenRequest { listen: true }, ) @@ -2012,7 +2012,7 @@ mod tests { &state_c, String::from(ACK_CHALLENGE_CAPABILITY), String::from("challenge"), - ACK_CHALLENGE_EVENT, + String::from(ACK_CHALLENGE_EVENT), ctx_c.clone(), ListenRequest { listen: true }, ) diff --git a/core/sdk/src/api/firebolt/fb_openrpc.rs b/core/sdk/src/api/firebolt/fb_openrpc.rs index f12e186c7..6be8903c7 100644 --- a/core/sdk/src/api/firebolt/fb_openrpc.rs +++ b/core/sdk/src/api/firebolt/fb_openrpc.rs @@ -808,6 +808,7 @@ mod tests { error_for: None, allow_focus: None, allow_focus_for: None, + provided_by: None, }; assert_eq!( @@ -851,6 +852,7 @@ mod tests { error_for: None, allow_focus: None, allow_focus_for: None, + provided_by: None, }]), }; @@ -871,6 +873,7 @@ mod tests { error_for: None, allow_focus: None, allow_focus_for: None, + provided_by: None, }]); assert_eq!(method.get_allow_value(), None); From f71207fd601a421a7a4650ef074fed5a08eb1c87 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sun, 14 Jul 2024 17:00:55 -0400 Subject: [PATCH 08/24] x-provided-by Pull pattern: Refactored for testability --- .../firebolt/handlers/provider_registrar.rs | 233 +++++++----------- core/main/src/state/openrpc_state.rs | 52 ++-- 2 files changed, 130 insertions(+), 155 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 5d07b23ad..f8ed98d54 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -25,6 +25,7 @@ use crate::{ }, state::{openrpc_state::ProviderSet, platform_state::PlatformState}, }; +use futures::Future; use jsonrpsee::{ core::{server::rpc_module::Methods, Error, RpcResult}, types::{error::CallError, Params, ParamsSequence}, @@ -52,6 +53,17 @@ use serde_json::Value; // // TODO: Add to config const DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS: u64 = 5000; + +#[derive(Debug)] +enum MethodType { + AppEventListener, + Provider, + AppEventEmitter, + Error, + ProviderInvoker, + Focus, + Response, +} // #[derive(Clone)] @@ -266,6 +278,59 @@ impl ProviderRegistrar { // } // } + fn register_method( + method_name: &'static str, + method_type: MethodType, + rpc_module: &mut RpcModule, + ) { + println!( + "*** _DEBUG: register_method: method_name={}, method_type={:?}", + method_name, method_type + ); + info!( + "*** _DEBUG: register_method: method_name={}, method_type={:?}", + method_name, method_type + ); + + match method_type { + MethodType::AppEventEmitter => { + rpc_module + .register_async_method(method_name, Self::callback_app_event_emitter) + .unwrap(); + } + MethodType::AppEventListener => { + rpc_module + .register_async_method(method_name, Self::callback_app_event_listener) + .unwrap(); + } + MethodType::Error => { + rpc_module + .register_async_method(method_name, Self::callback_error) + .unwrap(); + } + MethodType::Focus => { + rpc_module + .register_async_method(method_name, Self::callback_focus) + .unwrap(); + } + MethodType::Provider => { + rpc_module + .register_async_method(method_name, Self::callback_register_provider) + .unwrap(); + } + MethodType::ProviderInvoker => { + rpc_module + .register_async_method(method_name, Self::callback_provider_invoker) + .unwrap(); + } + MethodType::Response => { + rpc_module + .register_async_method(method_name, Self::callback_response) + .unwrap(); + } + } + } + async fn callback_app_event_listener( params: Params<'static>, context: Arc, @@ -293,24 +358,6 @@ impl ProviderRegistrar { }) } - fn register_method_app_event_listener( - method_name: &'static str, - rpc_module: &mut RpcModule, - ) { - println!( - "*** _DEBUG: register_method_app_event_listener: method_name={}", - method_name - ); - info!( - "register_method_app_event_listener: method_name={}", - method_name - ); - - rpc_module - .register_async_method(method_name, Self::callback_app_event_listener) - .unwrap(); - } - async fn callback_register_provider( params: Params<'static>, context: Arc, @@ -346,20 +393,6 @@ impl ProviderRegistrar { } } - fn register_method_provider( - method_name: &'static str, - rpc_module: &mut RpcModule, - ) { - println!( - "*** _DEBUG: register_method_provider: method_name={}", - method_name - ); - info!("register_method_provider: method_name={}", method_name); - rpc_module - .register_async_method(method_name, Self::callback_register_provider) - .unwrap(); - } - async fn callback_app_event_emitter( params: Params<'static>, context: Arc, @@ -377,28 +410,9 @@ impl ProviderRegistrar { AppEvents::emit(&context.platform_state, event, &result).await; } - //Ok(None) as RpcResult> Ok(None) } - fn register_method_app_event_emitter( - method_name: &'static str, - rpc_module: &mut RpcModule, - ) { - println!( - "*** _DEBUG: register_method_app_event_emitter: method_name={}", - method_name - ); - info!( - "register_method_app_event_emitter: method_name={}", - method_name - ); - - rpc_module - .register_async_method(method_name, Self::callback_app_event_emitter) - .unwrap(); - } - async fn callback_error( params: Params<'static>, context: Arc, @@ -429,25 +443,11 @@ impl ProviderRegistrar { Ok(None) as RpcResult> } - fn register_method_error( - method_name: &'static str, - rpc_module: &mut RpcModule, - ) { - println!( - "*** _DEBUG: register_method_error: method_name={}", - method_name - ); - info!("register_method_error: method_name={}", method_name); - - rpc_module - .register_async_method(method_name, Self::callback_error) - .unwrap(); - } - async fn callback_provider_invoker( params: Params<'static>, context: Arc, ) -> Result { + // TODO: Is 'Value' correct here? otherwise need to do bespoke return types? let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); let params: Value = params_sequence.next().unwrap(); @@ -514,24 +514,6 @@ impl ProviderRegistrar { ))) } - fn register_method_provider_invoker( - method_name: &'static str, - rpc_module: &mut RpcModule, - ) { - println!( - "*** _DEBUG: register_method_provider_invoker: method_name={}", - method_name - ); - info!( - "register_method_provider_invoker: method_name={}", - method_name - ); - - rpc_module - .register_async_method(method_name, Self::callback_provider_invoker) - .unwrap(); - } - async fn callback_focus( params: Params<'static>, context: Arc, @@ -558,21 +540,6 @@ impl ProviderRegistrar { } } - fn register_method_focus( - method_name: &'static str, - rpc_module: &mut RpcModule, - ) { - println!( - "*** _DEBUG: register_method_focus: method_name={}", - method_name - ); - info!("register_method_focus: method_name={}", method_name); - - rpc_module - .register_async_method(method_name, Self::callback_focus) - .unwrap(); - } - async fn callback_response( params: Params<'static>, context: Arc, @@ -601,22 +568,7 @@ impl ProviderRegistrar { return Err(Error::Custom(String::from("Missing provider attributes"))); } - Ok(None) as RpcResult> - } - - fn register_method_response( - method_name: &'static str, - rpc_module: &mut RpcModule, - ) { - println!( - "*** _DEBUG: register_method_response: method_name={}", - method_name - ); - info!("register_method_response: method_name={}", method_name); - - rpc_module - .register_async_method(method_name, Self::callback_response) - .unwrap(); + Ok(None) } pub fn register(platform_state: &PlatformState, methods: &mut Methods) { @@ -637,38 +589,41 @@ impl ProviderRegistrar { if provider_set.event { if provider_set.provided_by.is_some() { - // Register app event listener - Self::register_method_app_event_listener(method_name_lcm, &mut rpc_module); + Self::register_method( + method_name_lcm, + MethodType::AppEventListener, + &mut rpc_module, + ); } else if provider_set.provides.is_some() || provider_set.provides_to.is_some() { - // Register provider - Self::register_method_provider(method_name_lcm, &mut rpc_module); + Self::register_method( + method_name_lcm, + MethodType::Provider, + &mut rpc_module, + ); } } else { if provider_set.provides_to.is_some() { - // Register app event emitter - Self::register_method_app_event_emitter(method_name_lcm, &mut rpc_module); - } - - if provider_set.error_for.is_some() { - // Register error function - Self::register_method_error(method_name_lcm, &mut rpc_module); + Self::register_method( + method_name_lcm, + MethodType::AppEventEmitter, + &mut rpc_module, + ); + } else if provider_set.error_for.is_some() { + Self::register_method(method_name_lcm, MethodType::Error, &mut rpc_module); + } else if provider_set.provided_by.is_some() { + Self::register_method( + method_name_lcm, + MethodType::ProviderInvoker, + &mut rpc_module, + ); } - - if provider_set.provided_by.is_some() { - // Register provider invoker - Self::register_method_provider_invoker(method_name_lcm, &mut rpc_module); - } - } - - if provider_set.focus.is_some() { - // Register focus function - Self::register_method_focus(method_name_lcm, &mut rpc_module); } - if provider_set.response.is_some() { - // Register response function - Self::register_method_response(method_name_lcm, &mut rpc_module); + if provider_set.allow_focus_for.is_some() { + Self::register_method(method_name_lcm, MethodType::Focus, &mut rpc_module); + } else if provider_set.response_for.is_some() { + Self::register_method(method_name_lcm, MethodType::Response, &mut rpc_module); } methods diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index bbca60bcc..6e04ec01e 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -43,9 +43,15 @@ pub enum ApiSurface { #[derive(Debug, Clone, Default)] pub struct ProviderSet { - pub request: Option, - pub focus: Option, - pub response: Option, + // + //pub request: Option, + // + // 4 + //pub focus: Option, + //pub response: Option, + pub allow_focus_for: Option, + pub response_for: Option, + // // 2 //pub error: Option, pub error_for: Option, @@ -149,8 +155,12 @@ pub fn build_provider_sets( if let Some(tags) = &method.tags { let mut has_event = false; let mut has_caps = false; - let mut has_x_allow_focus_for = false; - let mut has_x_response_for = false; + // 4 + //let mut has_x_allow_focus_for = false; + //let mut has_x_response_for = false; + let mut x_allow_focus_for = None; + let mut x_response_for = None; + // // 2 //let mut has_x_error_for = false; let mut x_error_for = None; @@ -165,8 +175,12 @@ pub fn build_provider_sets( } else if tag.name.eq("capabilities") { has_caps = true; has_x_provides = tag.get_provides(); - has_x_allow_focus_for |= tag.allow_focus_for.is_some(); - has_x_response_for |= tag.response_for.is_some(); + // 4 + //has_x_allow_focus_for |= tag.allow_focus_for.is_some(); + //has_x_response_for |= tag.response_for.is_some(); + x_allow_focus_for = tag.allow_focus_for.clone(); + x_response_for = tag.response_for.clone(); + // // 2 //has_x_error_for |= tag.error_for.is_some(); x_error_for = tag.error_for.clone(); @@ -185,15 +199,21 @@ pub fn build_provider_sets( .clone(); if let Some(_capability) = has_x_provides { - if has_event && has_caps { - provider_set.request = Some(method.clone()); - } - if has_x_allow_focus_for { - provider_set.focus = Some(method.clone()); - } - if has_x_response_for { - provider_set.response = Some(method.clone()); - } + // + // if has_event && has_caps { + // provider_set.request = Some(method.clone()); + // } + // + // 4 + // if has_x_allow_focus_for { + // provider_set.focus = Some(method.clone()); + // } + // if has_x_response_for { + // provider_set.response = Some(method.clone()); + // } + provider_set.allow_focus_for = x_allow_focus_for; + provider_set.response_for = x_response_for; + // // 2 // if has_x_error_for { // provider_set.error = Some(method.clone()); From 7e238c69bd27d335cead7acb71642df86f1d43f9 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sun, 14 Jul 2024 20:17:02 -0400 Subject: [PATCH 09/24] x-provided-by Pull pattern: Added unit tests --- .../src/bootstrap/start_fbgateway_step.rs | 8 +- .../firebolt/handlers/provider_registrar.rs | 387 ++++++++++++++++-- core/main/src/state/openrpc_state.rs | 10 +- 3 files changed, 359 insertions(+), 46 deletions(-) diff --git a/core/main/src/bootstrap/start_fbgateway_step.rs b/core/main/src/bootstrap/start_fbgateway_step.rs index 1ffe06531..872069f6c 100644 --- a/core/main/src/bootstrap/start_fbgateway_step.rs +++ b/core/main/src/bootstrap/start_fbgateway_step.rs @@ -49,11 +49,9 @@ impl FireboltGatewayStep { async fn init_handlers(&self, state: PlatformState, extn_methods: Methods) -> Methods { let mut methods = Methods::new(); - // This loads up all of the firebolt methods - - // TODO: Ultimately this should be able to register all provider below, for now just does - // AcknowledgeChallenge and PinChallenge. - ProviderRegistrar::register(&state, &mut methods); + // TODO: Ultimately this may be able to register all providers below, for now just does + // those included by build_provider_sets(). + ProviderRegistrar::register_methods(&state, &mut methods); // let _ = methods.merge(DeviceRPCProvider::provide_with_alias(state.clone())); diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index f8ed98d54..09471ee85 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -25,7 +25,6 @@ use crate::{ }, state::{openrpc_state::ProviderSet, platform_state::PlatformState}, }; -use futures::Future; use jsonrpsee::{ core::{server::rpc_module::Methods, Error, RpcResult}, types::{error::CallError, Params, ParamsSequence}, @@ -282,51 +281,49 @@ impl ProviderRegistrar { method_name: &'static str, method_type: MethodType, rpc_module: &mut RpcModule, - ) { + ) -> bool { println!( "*** _DEBUG: register_method: method_name={}, method_type={:?}", method_name, method_type ); info!( - "*** _DEBUG: register_method: method_name={}, method_type={:?}", + "register_method: method_name={}, method_type={:?}", method_name, method_type ); - match method_type { + let result = match method_type { MethodType::AppEventEmitter => { - rpc_module - .register_async_method(method_name, Self::callback_app_event_emitter) - .unwrap(); + rpc_module.register_async_method(method_name, Self::callback_app_event_emitter) } MethodType::AppEventListener => { - rpc_module - .register_async_method(method_name, Self::callback_app_event_listener) - .unwrap(); + rpc_module.register_async_method(method_name, Self::callback_app_event_listener) } MethodType::Error => { - rpc_module - .register_async_method(method_name, Self::callback_error) - .unwrap(); + rpc_module.register_async_method(method_name, Self::callback_error) } MethodType::Focus => { - rpc_module - .register_async_method(method_name, Self::callback_focus) - .unwrap(); + rpc_module.register_async_method(method_name, Self::callback_focus) } MethodType::Provider => { - rpc_module - .register_async_method(method_name, Self::callback_register_provider) - .unwrap(); + rpc_module.register_async_method(method_name, Self::callback_register_provider) } MethodType::ProviderInvoker => { - rpc_module - .register_async_method(method_name, Self::callback_provider_invoker) - .unwrap(); + rpc_module.register_async_method(method_name, Self::callback_provider_invoker) } MethodType::Response => { - rpc_module - .register_async_method(method_name, Self::callback_response) - .unwrap(); + rpc_module.register_async_method(method_name, Self::callback_response) + } + }; + + match result { + Ok(_) => true, + Err(e) => { + error!("register_method: Error registering method: {:?}", e); + println!( + "*** _DEBUG: register_method: Error registering method: {:?}", + e + ); + false } } } @@ -571,11 +568,14 @@ impl ProviderRegistrar { Ok(None) } - pub fn register(platform_state: &PlatformState, methods: &mut Methods) { + pub fn register_methods(platform_state: &PlatformState, methods: &mut Methods) -> u32 { let provider_map = platform_state.open_rpc_state.get_provider_map(); + let mut registered_methods = 0; for method_name in provider_map.clone().keys() { if let Some(provider_set) = provider_map.get(method_name) { + let mut registered = false; + let method_name_lcm = FireboltOpenRpcMethod::name_with_lowercase_module(method_name).leak(); @@ -589,14 +589,14 @@ impl ProviderRegistrar { if provider_set.event { if provider_set.provided_by.is_some() { - Self::register_method( + registered = Self::register_method( method_name_lcm, MethodType::AppEventListener, &mut rpc_module, ); } else if provider_set.provides.is_some() || provider_set.provides_to.is_some() { - Self::register_method( + registered = Self::register_method( method_name_lcm, MethodType::Provider, &mut rpc_module, @@ -604,15 +604,19 @@ impl ProviderRegistrar { } } else { if provider_set.provides_to.is_some() { - Self::register_method( + registered = Self::register_method( method_name_lcm, MethodType::AppEventEmitter, &mut rpc_module, ); } else if provider_set.error_for.is_some() { - Self::register_method(method_name_lcm, MethodType::Error, &mut rpc_module); + registered = Self::register_method( + method_name_lcm, + MethodType::Error, + &mut rpc_module, + ); } else if provider_set.provided_by.is_some() { - Self::register_method( + registered = Self::register_method( method_name_lcm, MethodType::ProviderInvoker, &mut rpc_module, @@ -620,17 +624,324 @@ impl ProviderRegistrar { } } - if provider_set.allow_focus_for.is_some() { - Self::register_method(method_name_lcm, MethodType::Focus, &mut rpc_module); - } else if provider_set.response_for.is_some() { - Self::register_method(method_name_lcm, MethodType::Response, &mut rpc_module); + if !registered { + if provider_set.allow_focus_for.is_some() { + registered = Self::register_method( + method_name_lcm, + MethodType::Focus, + &mut rpc_module, + ); + } else if provider_set.response_for.is_some() { + registered = Self::register_method( + method_name_lcm, + MethodType::Response, + &mut rpc_module, + ); + } } - methods - .merge(register_aliases(platform_state, rpc_module)) - .ok(); + if registered { + methods + .merge(register_aliases(platform_state, rpc_module)) + .ok(); + + registered_methods += 1; + } } } + + registered_methods } // } + +#[cfg(test)] +mod tests { + use std::collections::HashMap; + + use crate::{state::openrpc_state::OpenRpcState, utils::test_utils}; + + use super::*; + use jsonrpsee::core::server::rpc_module::Methods; + use ripple_sdk::api::firebolt::provider; + + #[test] + fn test_register_methods() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), ProviderSet::new()); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 0); + } + + #[test] + fn test_register_method_event_provided_by() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + event: true, + provided_by: Some("some.other_method".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_event_provides() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + event: true, + provides: Some("some.capability".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_event_provides_to() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + event: true, + provides_to: Some("some.other.method".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_provides_to() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + event: true, + provides_to: Some("some.other.method".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_error_for() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + error_for: Some("some.other.method".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_provided_by() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + provided_by: Some("some.other.method".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_allow_focus_for() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + allow_focus_for: Some("some.other.method".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_response_for() { + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + response_for: Some("some.other.method".to_string()), + ..Default::default() + }; + + let mut provider_map: HashMap = HashMap::new(); + provider_map.insert("some.method".to_string(), provider_set); + + runtime + .platform_state + .open_rpc_state + .set_provider_map(provider_map); + + let registered_methods = + ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); + + println!("*** _DEBUG: registered_methods={}", registered_methods); + + assert!(registered_methods == 1); + } + + #[test] + fn test_register_method_duplicate() { + const METHOD_NAME: &str = "some.method"; + + let mut methods = Methods::new(); + let mut runtime = test_utils::MockRuntime::new(); + runtime.platform_state.open_rpc_state = OpenRpcState::new(None); + + let provider_set = ProviderSet { + response_for: Some("some.other.method".to_string()), + ..Default::default() + }; + + let rpc_module_context = RpcModuleContext::new( + runtime.platform_state.clone(), + String::from(METHOD_NAME), + provider_set.clone(), + ); + + let mut rpc_module = RpcModule::new(rpc_module_context.clone()); + + let result = ProviderRegistrar::register_method( + METHOD_NAME, + MethodType::AppEventEmitter, + &mut rpc_module, + ); + + assert!(result); + + let result = ProviderRegistrar::register_method( + METHOD_NAME, + MethodType::ProviderInvoker, + &mut rpc_module, + ); + + assert!(!result); + } +} diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 6e04ec01e..b4ef8df4f 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -143,7 +143,7 @@ pub fn build_provider_sets( for method in openrpc_methods { let mut has_x_provides = None; - // Only build provider sets for AcknowledgeChallenge and PinChallenge methods for now + // Only build provider sets for AcknowledgeChallenge, PinChallenge methods, Discovery, and Content for now if !method.name.starts_with("AcknowledgeChallenge.") && !method.name.starts_with("PinChallenge.") && !method.name.starts_with("Discovery.") @@ -154,7 +154,6 @@ pub fn build_provider_sets( if let Some(tags) = &method.tags { let mut has_event = false; - let mut has_caps = false; // 4 //let mut has_x_allow_focus_for = false; //let mut has_x_response_for = false; @@ -173,7 +172,6 @@ pub fn build_provider_sets( if tag.name.eq("event") { has_event = true; } else if tag.name.eq("capabilities") { - has_caps = true; has_x_provides = tag.get_provides(); // 4 //has_x_allow_focus_for |= tag.allow_focus_for.is_some(); @@ -487,6 +485,12 @@ impl OpenRpcState { self.provider_map.read().unwrap().clone() } + // + pub fn set_provider_map(&self, provider_map: HashMap) { + *self.provider_map.write().unwrap() = provider_map; + } + // + pub fn get_version(&self) -> FireboltSemanticVersion { self.open_rpc.info.clone() } From 5385b4191721a9ded98295f1539a134da049a3e7 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Sun, 14 Jul 2024 21:14:32 -0400 Subject: [PATCH 10/24] x-provided-by Pull pattern: Removed debug --- .../src/bootstrap/start_fbgateway_step.rs | 1 - core/main/src/firebolt/firebolt_ws.rs | 1 - .../src/firebolt/handlers/discovery_rpc.rs | 6 - .../src/firebolt/handlers/keyboard_rpc.rs | 3 - core/main/src/firebolt/handlers/lcm_rpc.rs | 6 - .../src/firebolt/handlers/localization_rpc.rs | 3 - .../firebolt/handlers/provider_registrar.rs | 236 ++---------------- core/main/src/service/apps/provider_broker.rs | 34 +-- core/main/src/state/openrpc_state.rs | 110 -------- core/sdk/src/api/firebolt/fb_openrpc.rs | 2 - core/sdk/src/api/firebolt/provider.rs | 25 -- 11 files changed, 16 insertions(+), 411 deletions(-) diff --git a/core/main/src/bootstrap/start_fbgateway_step.rs b/core/main/src/bootstrap/start_fbgateway_step.rs index 872069f6c..77be93c8b 100644 --- a/core/main/src/bootstrap/start_fbgateway_step.rs +++ b/core/main/src/bootstrap/start_fbgateway_step.rs @@ -52,7 +52,6 @@ impl FireboltGatewayStep { // TODO: Ultimately this may be able to register all providers below, for now just does // those included by build_provider_sets(). ProviderRegistrar::register_methods(&state, &mut methods); - // let _ = methods.merge(DeviceRPCProvider::provide_with_alias(state.clone())); let _ = methods.merge(WifiRPCProvider::provide_with_alias(state.clone())); diff --git a/core/main/src/firebolt/firebolt_ws.rs b/core/main/src/firebolt/firebolt_ws.rs index 49ba3fdc9..5fe2fec69 100644 --- a/core/main/src/firebolt/firebolt_ws.rs +++ b/core/main/src/firebolt/firebolt_ws.rs @@ -289,7 +289,6 @@ impl FireboltWs { } } } - error!("*** _DEBUG: invalid message {}", req_text); error!("invalid message {}", req_text) } } diff --git a/core/main/src/firebolt/handlers/discovery_rpc.rs b/core/main/src/firebolt/handlers/discovery_rpc.rs index 0aa327f86..9ed265f4b 100644 --- a/core/main/src/firebolt/handlers/discovery_rpc.rs +++ b/core/main/src/firebolt/handlers/discovery_rpc.rs @@ -625,10 +625,7 @@ impl DiscoveryServer for DiscoveryImpl { &self.state, FireboltCap::Short(ENTITY_INFO_CAPABILITY.into()).as_str(), String::from("entityInfo"), - // 3 - //ENTITY_INFO_EVENT, String::from(ENTITY_INFO_EVENT), - // ctx, request, ) @@ -700,10 +697,7 @@ impl DiscoveryServer for DiscoveryImpl { &self.state, FireboltCap::Short(PURCHASED_CONTENT_CAPABILITY.into()).as_str(), String::from("purchasedContent"), - // 3 - //PURCHASED_CONTENT_EVENT, String::from(PURCHASED_CONTENT_EVENT), - // ctx, request, ) diff --git a/core/main/src/firebolt/handlers/keyboard_rpc.rs b/core/main/src/firebolt/handlers/keyboard_rpc.rs index 3ed1a220b..a5717968a 100644 --- a/core/main/src/firebolt/handlers/keyboard_rpc.rs +++ b/core/main/src/firebolt/handlers/keyboard_rpc.rs @@ -302,10 +302,7 @@ impl KeyboardImpl { &self.platform_state, String::from(KEYBOARD_PROVIDER_CAPABILITY), method, - // 3 - //event_name, String::from(event_name), - // ctx, request, ) diff --git a/core/main/src/firebolt/handlers/lcm_rpc.rs b/core/main/src/firebolt/handlers/lcm_rpc.rs index a89b001e3..bb0e50f85 100644 --- a/core/main/src/firebolt/handlers/lcm_rpc.rs +++ b/core/main/src/firebolt/handlers/lcm_rpc.rs @@ -113,10 +113,7 @@ impl LifecycleManagementImpl { &self.state, FireboltCap::short("app:lifecycle").as_str(), method.into(), - // 3 - //event_name, String::from(event_name), - // ctx, request, ) @@ -124,10 +121,7 @@ impl LifecycleManagementImpl { Ok(ListenerResponse { listening: listen, - // 3 - //event: event_name.into(), event: String::from(event_name), - // }) } } diff --git a/core/main/src/firebolt/handlers/localization_rpc.rs b/core/main/src/firebolt/handlers/localization_rpc.rs index 93acb63d8..4661e9406 100644 --- a/core/main/src/firebolt/handlers/localization_rpc.rs +++ b/core/main/src/firebolt/handlers/localization_rpc.rs @@ -221,10 +221,7 @@ impl LocalizationImpl { // TODO update with Firebolt Cap in later effort "xrn:firebolt:capability:localization:locale".into(), method.into(), - // 3 - //event_name, String::from(event_name), - // ctx, request, ) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 09471ee85..8c4ca293c 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -49,7 +49,6 @@ use ripple_sdk::{ }; use serde_json::Value; -// // TODO: Add to config const DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS: u64 = 5000; @@ -63,22 +62,15 @@ enum MethodType { Focus, Response, } -// #[derive(Clone)] struct RpcModuleContext { platform_state: PlatformState, - // method: String, provider_set: ProviderSet, - // } impl RpcModuleContext { - // - // fn new(platform_state: PlatformState) -> Self { - // RpcModuleContext { platform_state } - // } fn new(platform_state: PlatformState, method: String, provider_set: ProviderSet) -> Self { RpcModuleContext { method, @@ -86,7 +78,6 @@ impl RpcModuleContext { provider_set, } } - // } pub struct ProviderRegistrar; @@ -141,151 +132,11 @@ impl ProviderRegistrar { None } - // - // pub fn register(platform_state: &PlatformState, methods: &mut Methods) { - // let provider_map = platform_state.open_rpc_state.get_provider_map(); - // for method_name in provider_map.clone().keys() { - // if let Some(provider_set) = provider_map.get(method_name) { - // if let Some(attributes) = provider_set.attributes { - // let rpc_module_context = RpcModuleContext::new(platform_state.clone()); - // let mut rpc_module = RpcModule::new(rpc_module_context.clone()); - - // // Register request function - // if let Some(method) = provider_set.request.clone() { - // let request_method = - // FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); - - // rpc_module - // .register_async_method( - // request_method, - // move |params, context| async move { - // let mut params_sequence = params.sequence(); - // let call_context: CallContext = params_sequence.next().unwrap(); - // let request: ListenRequest = params_sequence.next().unwrap(); - // let listening = request.listen; - - // ProviderBroker::register_or_unregister_provider( - // &context.platform_state, - // attributes.capability.into(), - // attributes.method.into(), - // attributes.event, - // call_context, - // request, - // ) - // .await; - - // Ok(ListenerResponse { - // listening, - // event: attributes.event.into(), - // }) - // }, - // ) - // .unwrap(); - // } - - // // Register focus function - // if let Some(method) = provider_set.focus.clone() { - // let focus_method = - // FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); - - // rpc_module - // .register_async_method( - // focus_method, - // move |params, context| async move { - // let mut params_sequence = params.sequence(); - // let call_context: CallContext = params_sequence.next().unwrap(); - // let request: FocusRequest = params_sequence.next().unwrap(); - - // ProviderBroker::focus( - // &context.platform_state, - // call_context, - // attributes.capability.into(), - // request, - // ) - // .await; - // Ok(None) as RpcResult> - // }, - // ) - // .unwrap(); - // } - - // // Register response function - // if let Some(method) = provider_set.response.clone() { - // let response_method = - // FireboltOpenRpcMethod::name_with_lowercase_module(&method.name).leak(); - - // rpc_module - // .register_async_method( - // response_method, - // move |params, context| async move { - // let params_sequence = params.sequence(); - - // if let Some(provider_response) = - // ProviderRegistrar::get_provider_response( - // attributes.response_payload_type.clone(), - // params_sequence, - // ) - // { - // ProviderBroker::provider_response( - // &context.platform_state, - // provider_response, - // ) - // .await; - // } - - // Ok(None) as RpcResult> - // }, - // ) - // .unwrap(); - // } - - // // Register error function - // if let Some(method) = provider_set.error.clone() { - // let error_method = method.name.clone().leak(); - - // rpc_module - // .register_async_method( - // error_method, - // move |params, context| async move { - // let params_sequence = params.sequence(); - - // if let Some(provider_response) = - // ProviderRegistrar::get_provider_response( - // attributes.error_payload_type.clone(), - // params_sequence, - // ) - // { - // ProviderBroker::provider_response( - // &context.platform_state, - // provider_response, - // ) - // .await; - // } - - // Ok(None) as RpcResult> - // }, - // ) - // .unwrap(); - // } - - // //methods.merge(rpc_module.clone()).ok(); - // methods - // .merge(register_aliases(platform_state, rpc_module)) - // .ok(); - // } - // } - // } - // } - fn register_method( method_name: &'static str, method_type: MethodType, rpc_module: &mut RpcModule, ) -> bool { - println!( - "*** _DEBUG: register_method: method_name={}, method_type={:?}", - method_name, method_type - ); info!( "register_method: method_name={}, method_type={:?}", method_name, method_type @@ -319,10 +170,6 @@ impl ProviderRegistrar { Ok(_) => true, Err(e) => { error!("register_method: Error registering method: {:?}", e); - println!( - "*** _DEBUG: register_method: Error registering method: {:?}", - e - ); false } } @@ -332,10 +179,6 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result { - println!( - "*** _DEBUG: callback_app_event_listener: method={}", - context.method - ); info!("callback_app_event_listener: method={}", context.method); let mut params_sequence = params.sequence(); @@ -359,10 +202,6 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result { - println!( - "*** _DEBUG: callback_register_provider: method={}", - context.method - ); info!("callback_register_provider: method={}", context.method); if let Some(provides) = &context.provider_set.provides { @@ -394,10 +233,6 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result, Error> { - println!( - "*** _DEBUG: callback_app_event_emitter: method={}", - context.method - ); info!("callback_app_event_emitter: method={}", context.method); if let Some(event) = &context.provider_set.provides_to { let mut params_sequence = params.sequence(); @@ -414,7 +249,6 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result, Error> { - println!("*** _DEBUG: callback_error: method={}", context.method); info!("callback_error: method={}", context.method); let params_sequence = params.sequence(); @@ -426,10 +260,6 @@ impl ProviderRegistrar { ProviderBroker::provider_response(&context.platform_state, provider_response).await; } } else { - println!( - "*** _DEBUG: callback_error: NO ATTRIBUTES: context.method={}", - context.method - ); error!( "callback_error: NO ATTRIBUTES: context.method={}", context.method @@ -449,10 +279,6 @@ impl ProviderRegistrar { let call_context: CallContext = params_sequence.next().unwrap(); let params: Value = params_sequence.next().unwrap(); - println!( - "*** _DEBUG: callback_provider_invoker: method={}", - context.method - ); info!("callback_provider_invoker: method={}", context.method); if let Some(provided_by) = &context.provider_set.provided_by { @@ -515,7 +341,6 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result, Error> { - println!("*** _DEBUG: callback_focus: method={}", context.method); info!("callback_focus: method={}", context.method); if let Some(provides) = &context.provider_set.provides { @@ -541,7 +366,6 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result, Error> { - println!("*** _DEBUG: callback_response: method={}", context.method); info!("callback_response: method={}", context.method); let params_sequence = params.sequence(); @@ -554,10 +378,6 @@ impl ProviderRegistrar { ProviderBroker::provider_response(&context.platform_state, provider_response).await; } } else { - println!( - "*** _DEBUG: callback_response: NO ATTRIBUTES: context.method={}", - context.method - ); error!( "callback_response: NO ATTRIBUTES: context.method={}", context.method @@ -602,26 +422,21 @@ impl ProviderRegistrar { &mut rpc_module, ); } - } else { - if provider_set.provides_to.is_some() { - registered = Self::register_method( - method_name_lcm, - MethodType::AppEventEmitter, - &mut rpc_module, - ); - } else if provider_set.error_for.is_some() { - registered = Self::register_method( - method_name_lcm, - MethodType::Error, - &mut rpc_module, - ); - } else if provider_set.provided_by.is_some() { - registered = Self::register_method( - method_name_lcm, - MethodType::ProviderInvoker, - &mut rpc_module, - ); - } + } else if provider_set.provides_to.is_some() { + registered = Self::register_method( + method_name_lcm, + MethodType::AppEventEmitter, + &mut rpc_module, + ); + } else if provider_set.error_for.is_some() { + registered = + Self::register_method(method_name_lcm, MethodType::Error, &mut rpc_module); + } else if provider_set.provided_by.is_some() { + registered = Self::register_method( + method_name_lcm, + MethodType::ProviderInvoker, + &mut rpc_module, + ); } if !registered { @@ -652,7 +467,6 @@ impl ProviderRegistrar { registered_methods } - // } #[cfg(test)] @@ -663,7 +477,6 @@ mod tests { use super::*; use jsonrpsee::core::server::rpc_module::Methods; - use ripple_sdk::api::firebolt::provider; #[test] fn test_register_methods() { @@ -682,8 +495,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 0); } @@ -710,8 +521,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -738,8 +547,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -766,8 +573,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -794,8 +599,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -821,8 +624,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -848,8 +649,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -875,8 +674,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -902,8 +699,6 @@ mod tests { let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); - println!("*** _DEBUG: registered_methods={}", registered_methods); - assert!(registered_methods == 1); } @@ -911,7 +706,6 @@ mod tests { fn test_register_method_duplicate() { const METHOD_NAME: &str = "some.method"; - let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); diff --git a/core/main/src/service/apps/provider_broker.rs b/core/main/src/service/apps/provider_broker.rs index a2a8ab958..21cee5e7d 100644 --- a/core/main/src/service/apps/provider_broker.rs +++ b/core/main/src/service/apps/provider_broker.rs @@ -76,10 +76,7 @@ pub struct ProviderBroker {} #[derive(Clone, Debug)] struct ProviderMethod { - // 3 - //event_name: &'static str, event_name: String, - // provider: CallContext, } @@ -124,10 +121,7 @@ impl ProviderBroker { pst: &PlatformState, capability: String, method: String, - // 3 - //event_name: &'static str, event_name: String, - // provider: CallContext, listen_request: ListenRequest, ) { @@ -170,10 +164,7 @@ impl ProviderBroker { pst: &PlatformState, capability: String, method: String, - // 3 - //event_name: &'static str, event_name: String, - // provider: CallContext, listen_request: ListenRequest, ) { @@ -182,15 +173,7 @@ impl ProviderBroker { capability, method, event_name ); let cap_method = format!("{}:{}", capability, method); - AppEvents::add_listener( - pst, - // 3 - //event_name.to_string(), - event_name.clone(), - // - provider.clone(), - listen_request, - ); + AppEvents::add_listener(pst, event_name.clone(), provider.clone(), listen_request); { let mut provider_methods = pst.provider_broker_state.provider_methods.write().unwrap(); provider_methods.insert( @@ -224,17 +207,11 @@ impl ProviderBroker { for cap in all_caps { if let Some(provider) = provider_methods.get(&cap) { if let Some(list) = result.get_mut(&provider.provider.app_id) { - // 3 - //list.push(String::from(provider.event_name)); list.push(provider.event_name.clone()); - // } else { result.insert( provider.provider.app_id.clone(), - // 3 - //vec![String::from(provider.event_name)], vec![provider.event_name.clone()], - // ); } } @@ -251,10 +228,7 @@ impl ProviderBroker { provider_methods.get(&cap_method).cloned() }; if let Some(provider) = provider_opt { - // 3 - //let event_name = provider.event_name; let event_name = provider.event_name.clone(); - // let req_params = request.request.clone(); let app_id_opt = request.app_id.clone(); let c_id = ProviderBroker::start_provider_session(pst, request, provider); @@ -263,10 +237,7 @@ impl ProviderBroker { AppEvents::emit_to_app( pst, app_id, - // 3 - //event_name, &event_name, - // &serde_json::to_value(ProviderRequest { correlation_id: c_id, parameters: req_params, @@ -278,10 +249,7 @@ impl ProviderBroker { debug!("Broadcasting request to all the apps!!"); AppEvents::emit( pst, - // 3 - //event_name, &event_name, - // &serde_json::to_value(ProviderRequest { correlation_id: c_id, parameters: req_params, diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index b4ef8df4f..26851d89c 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -43,27 +43,15 @@ pub enum ApiSurface { #[derive(Debug, Clone, Default)] pub struct ProviderSet { - // - //pub request: Option, - // - // 4 - //pub focus: Option, - //pub response: Option, pub allow_focus_for: Option, pub response_for: Option, - // - // 2 - //pub error: Option, pub error_for: Option, - // pub attributes: Option<&'static ProviderAttributes>, - // pub provides: Option, pub provided_by: Option, pub uses: Option>, pub event: bool, pub provides_to: Option, - // } impl ProviderSet { @@ -72,69 +60,6 @@ impl ProviderSet { } } -// -// pub fn build_provider_sets( -// openrpc_methods: &Vec, -// ) -> HashMap { -// let mut provider_sets = HashMap::default(); - -// for method in openrpc_methods { -// let mut has_x_provides = None; - -// // Only build provider sets for AcknowledgeChallenge and PinChallenge methods for now -// if !method.name.starts_with("AcknowledgeChallenge.") -// && !method.name.starts_with("PinChallenge.") -// { -// continue; -// } - -// if let Some(tags) = &method.tags { -// let mut has_event = false; -// let mut has_caps = false; -// let mut has_x_allow_focus_for = false; -// let mut has_x_response_for = false; -// let mut has_x_error_for = false; - -// for tag in tags { -// if tag.name.eq("event") { -// has_event = true; -// } else if tag.name.eq("capabilities") { -// has_caps = true; -// has_x_provides = tag.get_provides(); -// has_x_allow_focus_for |= tag.allow_focus_for.is_some(); -// has_x_response_for |= tag.response_for.is_some(); -// has_x_error_for |= tag.error_for.is_some(); -// } -// } - -// if let Some(p) = has_x_provides { -// let mut provider_set = provider_sets -// .get(&p.as_str()) -// .unwrap_or(&ProviderSet::new()) -// .clone(); - -// if has_event && has_caps { -// provider_set.request = Some(method.clone()); -// } -// if has_x_allow_focus_for { -// provider_set.focus = Some(method.clone()); -// } -// if has_x_response_for { -// provider_set.response = Some(method.clone()); -// } -// if has_x_error_for { -// provider_set.error = Some(method.clone()); -// } - -// let module: Vec<&str> = method.name.split('.').collect(); -// provider_set.attributes = ProviderAttributes::get(module[0]); - -// provider_sets.insert(p.as_str(), provider_set.to_owned()); -// } -// } -// } -// provider_sets -// } pub fn build_provider_sets( openrpc_methods: &Vec, ) -> HashMap { @@ -154,16 +79,9 @@ pub fn build_provider_sets( if let Some(tags) = &method.tags { let mut has_event = false; - // 4 - //let mut has_x_allow_focus_for = false; - //let mut has_x_response_for = false; let mut x_allow_focus_for = None; let mut x_response_for = None; - // - // 2 - //let mut has_x_error_for = false; let mut x_error_for = None; - // let mut x_provided_by = None; let mut x_provides = None; let mut x_uses = None; @@ -173,16 +91,9 @@ pub fn build_provider_sets( has_event = true; } else if tag.name.eq("capabilities") { has_x_provides = tag.get_provides(); - // 4 - //has_x_allow_focus_for |= tag.allow_focus_for.is_some(); - //has_x_response_for |= tag.response_for.is_some(); x_allow_focus_for = tag.allow_focus_for.clone(); x_response_for = tag.response_for.clone(); - // - // 2 - //has_x_error_for |= tag.error_for.is_some(); x_error_for = tag.error_for.clone(); - // x_provided_by = tag.provided_by.clone(); x_provides = tag.provides.clone(); x_uses = tag.uses.clone(); @@ -197,27 +108,9 @@ pub fn build_provider_sets( .clone(); if let Some(_capability) = has_x_provides { - // - // if has_event && has_caps { - // provider_set.request = Some(method.clone()); - // } - // - // 4 - // if has_x_allow_focus_for { - // provider_set.focus = Some(method.clone()); - // } - // if has_x_response_for { - // provider_set.response = Some(method.clone()); - // } provider_set.allow_focus_for = x_allow_focus_for; provider_set.response_for = x_response_for; - // - // 2 - // if has_x_error_for { - // provider_set.error = Some(method.clone()); - // } provider_set.error_for = x_error_for; - // provider_set.provides = x_provides; } else { // x-provided-by can only be set if x-provides isn't. @@ -252,7 +145,6 @@ pub fn build_provider_sets( provider_sets } -// #[derive(Debug, Clone)] pub struct OpenRpcState { @@ -485,11 +377,9 @@ impl OpenRpcState { self.provider_map.read().unwrap().clone() } - // pub fn set_provider_map(&self, provider_map: HashMap) { *self.provider_map.write().unwrap() = provider_map; } - // pub fn get_version(&self) -> FireboltSemanticVersion { self.open_rpc.info.clone() diff --git a/core/sdk/src/api/firebolt/fb_openrpc.rs b/core/sdk/src/api/firebolt/fb_openrpc.rs index 6be8903c7..6ee5adfcd 100644 --- a/core/sdk/src/api/firebolt/fb_openrpc.rs +++ b/core/sdk/src/api/firebolt/fb_openrpc.rs @@ -307,10 +307,8 @@ pub struct FireboltOpenRpcTag { pub manages: Option>, #[serde(rename = "x-provides")] pub provides: Option, - // #[serde(rename = "x-provided-by")] pub provided_by: Option, - // #[serde(rename = "x-alternative")] pub alternative: Option, #[serde(rename = "x-since")] diff --git a/core/sdk/src/api/firebolt/provider.rs b/core/sdk/src/api/firebolt/provider.rs index 3e41fbfa5..e48bd1626 100644 --- a/core/sdk/src/api/firebolt/provider.rs +++ b/core/sdk/src/api/firebolt/provider.rs @@ -118,7 +118,6 @@ impl ProviderResponsePayload { } } - // pub fn as_value(&self) -> serde_json::Value { match self { ProviderResponsePayload::ChallengeResponse(res) => serde_json::to_value(res).unwrap(), @@ -133,7 +132,6 @@ impl ProviderResponsePayload { } } } - // } #[derive(Serialize, Deserialize)] @@ -174,17 +172,8 @@ pub struct ExternalProviderError { #[derive(Debug, Clone, Serialize)] pub struct ProviderAttributes { - // - //pub event: &'static str, - // pub response_payload_type: ProviderResponsePayloadType, pub error_payload_type: ProviderResponsePayloadType, - // - //pub capability: &'static str, - // - // 3 - //pub method: &'static str, - // } impl ProviderAttributes { @@ -198,27 +187,13 @@ impl ProviderAttributes { } pub const ACKNOWLEDGE_CHALLENGE_ATTRIBS: ProviderAttributes = ProviderAttributes { - // - //event: ACK_CHALLENGE_EVENT, - // response_payload_type: ProviderResponsePayloadType::ChallengeResponse, error_payload_type: ProviderResponsePayloadType::ChallengeError, - // - //capability: ACK_CHALLENGE_CAPABILITY, - //method: "challenge", - // }; pub const PIN_CHALLENGE_ATTRIBS: ProviderAttributes = ProviderAttributes { - // - //event: PIN_CHALLENGE_EVENT, - // response_payload_type: ProviderResponsePayloadType::PinChallengeResponse, error_payload_type: ProviderResponsePayloadType::ChallengeError, - // - //capability: PIN_CHALLENGE_CAPABILITY, - //method: "challenge", - // }; #[derive(Debug, Serialize, Deserialize, Clone)] From ef3e756d015dc897a41070383acf5e380888b043 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 17 Jul 2024 10:25:48 -0400 Subject: [PATCH 11/24] x-provided-by Pull pattern: Renamed provider map/set --> provider relation map/set --- .../src/bootstrap/start_fbgateway_step.rs | 2 +- .../firebolt/handlers/provider_registrar.rs | 130 ++++++++++-------- core/main/src/state/openrpc_state.rs | 63 +++++---- core/sdk/src/api/manifest/app_library.rs | 11 +- 4 files changed, 111 insertions(+), 95 deletions(-) diff --git a/core/main/src/bootstrap/start_fbgateway_step.rs b/core/main/src/bootstrap/start_fbgateway_step.rs index 108ce7fba..e8fc25556 100644 --- a/core/main/src/bootstrap/start_fbgateway_step.rs +++ b/core/main/src/bootstrap/start_fbgateway_step.rs @@ -49,7 +49,7 @@ impl FireboltGatewayStep { let mut methods = Methods::new(); // TODO: Ultimately this may be able to register all providers below, for now just does - // those included by build_provider_sets(). + // those included by build_provider_relation_sets(). ProviderRegistrar::register_methods(&state, &mut methods); let _ = methods.merge(DeviceRPCProvider::provide_with_alias(state.clone())); diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 8c4ca293c..4dae26593 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -23,7 +23,7 @@ use crate::{ app_events::AppEvents, provider_broker::{ProviderBroker, ProviderBrokerRequest}, }, - state::{openrpc_state::ProviderSet, platform_state::PlatformState}, + state::{openrpc_state::ProviderRelationSet, platform_state::PlatformState}, }; use jsonrpsee::{ core::{server::rpc_module::Methods, Error, RpcResult}, @@ -67,15 +67,19 @@ enum MethodType { struct RpcModuleContext { platform_state: PlatformState, method: String, - provider_set: ProviderSet, + provider_relation_set: ProviderRelationSet, } impl RpcModuleContext { - fn new(platform_state: PlatformState, method: String, provider_set: ProviderSet) -> Self { + fn new( + platform_state: PlatformState, + method: String, + provider_relation_set: ProviderRelationSet, + ) -> Self { RpcModuleContext { method, platform_state, - provider_set, + provider_relation_set, } } } @@ -204,7 +208,7 @@ impl ProviderRegistrar { ) -> Result { info!("callback_register_provider: method={}", context.method); - if let Some(provides) = &context.provider_set.provides { + if let Some(provides) = &context.provider_relation_set.provides { let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); let request: ListenRequest = params_sequence.next().unwrap(); @@ -234,7 +238,7 @@ impl ProviderRegistrar { context: Arc, ) -> Result, Error> { info!("callback_app_event_emitter: method={}", context.method); - if let Some(event) = &context.provider_set.provides_to { + if let Some(event) = &context.provider_relation_set.provides_to { let mut params_sequence = params.sequence(); let _call_context: CallContext = params_sequence.next().unwrap(); let result: Value = params_sequence.next().unwrap(); @@ -252,7 +256,7 @@ impl ProviderRegistrar { info!("callback_error: method={}", context.method); let params_sequence = params.sequence(); - if let Some(attributes) = context.provider_set.attributes { + if let Some(attributes) = context.provider_relation_set.attributes { if let Some(provider_response) = ProviderRegistrar::get_provider_response( attributes.error_payload_type.clone(), params_sequence, @@ -281,10 +285,13 @@ impl ProviderRegistrar { info!("callback_provider_invoker: method={}", context.method); - if let Some(provided_by) = &context.provider_set.provided_by { - let provider_map = context.platform_state.open_rpc_state.get_provider_map(); + if let Some(provided_by) = &context.provider_relation_set.provided_by { + let provider_relation_map = context + .platform_state + .open_rpc_state + .get_provider_relation_map(); - if let Some(provided_by_set) = provider_map.get( + if let Some(provided_by_set) = provider_relation_map.get( &FireboltOpenRpcMethod::name_with_lowercase_module(provided_by), ) { if let Some(capability) = &provided_by_set.provides { @@ -343,7 +350,7 @@ impl ProviderRegistrar { ) -> Result, Error> { info!("callback_focus: method={}", context.method); - if let Some(provides) = &context.provider_set.provides { + if let Some(provides) = &context.provider_relation_set.provides { let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); let request: FocusRequest = params_sequence.next().unwrap(); @@ -370,7 +377,7 @@ impl ProviderRegistrar { let params_sequence = params.sequence(); - if let Some(attributes) = context.provider_set.attributes { + if let Some(attributes) = context.provider_relation_set.attributes { if let Some(provider_response) = ProviderRegistrar::get_provider_response( attributes.response_payload_type.clone(), params_sequence, @@ -389,11 +396,11 @@ impl ProviderRegistrar { } pub fn register_methods(platform_state: &PlatformState, methods: &mut Methods) -> u32 { - let provider_map = platform_state.open_rpc_state.get_provider_map(); + let provider_relation_map = platform_state.open_rpc_state.get_provider_relation_map(); let mut registered_methods = 0; - for method_name in provider_map.clone().keys() { - if let Some(provider_set) = provider_map.get(method_name) { + for method_name in provider_relation_map.clone().keys() { + if let Some(provider_relation_set) = provider_relation_map.get(method_name) { let mut registered = false; let method_name_lcm = @@ -402,19 +409,20 @@ impl ProviderRegistrar { let rpc_module_context = RpcModuleContext::new( platform_state.clone(), method_name_lcm.into(), - provider_set.clone(), + provider_relation_set.clone(), ); let mut rpc_module = RpcModule::new(rpc_module_context.clone()); - if provider_set.event { - if provider_set.provided_by.is_some() { + if provider_relation_set.event { + if provider_relation_set.provided_by.is_some() { registered = Self::register_method( method_name_lcm, MethodType::AppEventListener, &mut rpc_module, ); - } else if provider_set.provides.is_some() || provider_set.provides_to.is_some() + } else if provider_relation_set.provides.is_some() + || provider_relation_set.provides_to.is_some() { registered = Self::register_method( method_name_lcm, @@ -422,16 +430,16 @@ impl ProviderRegistrar { &mut rpc_module, ); } - } else if provider_set.provides_to.is_some() { + } else if provider_relation_set.provides_to.is_some() { registered = Self::register_method( method_name_lcm, MethodType::AppEventEmitter, &mut rpc_module, ); - } else if provider_set.error_for.is_some() { + } else if provider_relation_set.error_for.is_some() { registered = Self::register_method(method_name_lcm, MethodType::Error, &mut rpc_module); - } else if provider_set.provided_by.is_some() { + } else if provider_relation_set.provided_by.is_some() { registered = Self::register_method( method_name_lcm, MethodType::ProviderInvoker, @@ -440,13 +448,13 @@ impl ProviderRegistrar { } if !registered { - if provider_set.allow_focus_for.is_some() { + if provider_relation_set.allow_focus_for.is_some() { registered = Self::register_method( method_name_lcm, MethodType::Focus, &mut rpc_module, ); - } else if provider_set.response_for.is_some() { + } else if provider_relation_set.response_for.is_some() { registered = Self::register_method( method_name_lcm, MethodType::Response, @@ -484,13 +492,13 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), ProviderSet::new()); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), ProviderRelationSet::new()); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -504,19 +512,19 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { event: true, provided_by: Some("some.other_method".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -530,19 +538,19 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { event: true, provides: Some("some.capability".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -556,19 +564,19 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { event: true, provides_to: Some("some.other.method".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -582,19 +590,19 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { event: true, provides_to: Some("some.other.method".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -608,18 +616,18 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { error_for: Some("some.other.method".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -633,18 +641,18 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { provided_by: Some("some.other.method".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -658,18 +666,18 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { allow_focus_for: Some("some.other.method".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -683,18 +691,18 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { response_for: Some("some.other.method".to_string()), ..Default::default() }; - let mut provider_map: HashMap = HashMap::new(); - provider_map.insert("some.method".to_string(), provider_set); + let mut provider_relation_map: HashMap = HashMap::new(); + provider_relation_map.insert("some.method".to_string(), provider_relation_set); runtime .platform_state .open_rpc_state - .set_provider_map(provider_map); + .set_provider_relation_map(provider_relation_map); let registered_methods = ProviderRegistrar::register_methods(&runtime.platform_state, &mut methods); @@ -709,7 +717,7 @@ mod tests { let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); - let provider_set = ProviderSet { + let provider_relation_set = ProviderRelationSet { response_for: Some("some.other.method".to_string()), ..Default::default() }; @@ -717,7 +725,7 @@ mod tests { let rpc_module_context = RpcModuleContext::new( runtime.platform_state.clone(), String::from(METHOD_NAME), - provider_set.clone(), + provider_relation_set.clone(), ); let mut rpc_module = RpcModule::new(rpc_module_context.clone()); diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 26851d89c..3e6a2682b 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -42,7 +42,7 @@ pub enum ApiSurface { } #[derive(Debug, Clone, Default)] -pub struct ProviderSet { +pub struct ProviderRelationSet { pub allow_focus_for: Option, pub response_for: Option, pub error_for: Option, @@ -54,16 +54,16 @@ pub struct ProviderSet { pub provides_to: Option, } -impl ProviderSet { - pub fn new() -> ProviderSet { - ProviderSet::default() +impl ProviderRelationSet { + pub fn new() -> ProviderRelationSet { + ProviderRelationSet::default() } } -pub fn build_provider_sets( +pub fn build_provider_relation_sets( openrpc_methods: &Vec, -) -> HashMap { - let mut provider_sets = HashMap::default(); +) -> HashMap { + let mut provider_relation_sets = HashMap::default(); for method in openrpc_methods { let mut has_x_provides = None; @@ -100,50 +100,50 @@ pub fn build_provider_sets( } } - let mut provider_set = provider_sets + let mut provider_relation_set = provider_relation_sets .get(&FireboltOpenRpcMethod::name_with_lowercase_module( &method.name, )) - .unwrap_or(&ProviderSet::new()) + .unwrap_or(&ProviderRelationSet::new()) .clone(); if let Some(_capability) = has_x_provides { - provider_set.allow_focus_for = x_allow_focus_for; - provider_set.response_for = x_response_for; - provider_set.error_for = x_error_for; - provider_set.provides = x_provides; + provider_relation_set.allow_focus_for = x_allow_focus_for; + provider_relation_set.response_for = x_response_for; + provider_relation_set.error_for = x_error_for; + provider_relation_set.provides = x_provides; } else { // x-provided-by can only be set if x-provides isn't. - provider_set.provided_by = x_provided_by.clone(); + provider_relation_set.provided_by = x_provided_by.clone(); if let Some(provided_by) = x_provided_by { - let mut provided_by_set = provider_sets + let mut provided_by_set = provider_relation_sets .get(&provided_by) - .unwrap_or(&ProviderSet::new()) + .unwrap_or(&ProviderRelationSet::new()) .clone(); provided_by_set.provides_to = Some(method.name.clone()); - provider_sets.insert( + provider_relation_sets.insert( FireboltOpenRpcMethod::name_with_lowercase_module(&provided_by), provided_by_set.to_owned(), ); } } - provider_set.uses = x_uses; - provider_set.event = has_event; + provider_relation_set.uses = x_uses; + provider_relation_set.event = has_event; let module: Vec<&str> = method.name.split('.').collect(); - provider_set.attributes = ProviderAttributes::get(module[0]); + provider_relation_set.attributes = ProviderAttributes::get(module[0]); - provider_sets.insert( + provider_relation_sets.insert( FireboltOpenRpcMethod::name_with_lowercase_module(&method.name), - provider_set.to_owned(), + provider_relation_set.to_owned(), ); } } - provider_sets + provider_relation_sets } #[derive(Debug, Clone)] @@ -154,7 +154,7 @@ pub struct OpenRpcState { ripple_cap_map: Arc>>, cap_policies: Arc>>, extended_rpc: Arc>>, - provider_map: Arc>>, + provider_relation_map: Arc>>, openrpc_validator: Arc>, } @@ -225,7 +225,9 @@ impl OpenRpcState { cap_policies: Arc::new(RwLock::new(version_manifest.capabilities)), open_rpc: firebolt_open_rpc.clone(), extended_rpc: Arc::new(RwLock::new(Vec::new())), - provider_map: Arc::new(RwLock::new(build_provider_sets(&firebolt_open_rpc.methods))), + provider_relation_map: Arc::new(RwLock::new(build_provider_relation_sets( + &firebolt_open_rpc.methods, + ))), openrpc_validator: Arc::new(RwLock::new(openrpc_validator)), } } @@ -373,12 +375,15 @@ impl OpenRpcState { self.open_rpc.clone() } - pub fn get_provider_map(&self) -> HashMap { - self.provider_map.read().unwrap().clone() + pub fn get_provider_relation_map(&self) -> HashMap { + self.provider_relation_map.read().unwrap().clone() } - pub fn set_provider_map(&self, provider_map: HashMap) { - *self.provider_map.write().unwrap() = provider_map; + pub fn set_provider_relation_map( + &self, + provider_relation_map: HashMap, + ) { + *self.provider_relation_map.write().unwrap() = provider_relation_map; } pub fn get_version(&self) -> FireboltSemanticVersion { diff --git a/core/sdk/src/api/manifest/app_library.rs b/core/sdk/src/api/manifest/app_library.rs index 4e713385e..b4d452c79 100644 --- a/core/sdk/src/api/manifest/app_library.rs +++ b/core/sdk/src/api/manifest/app_library.rs @@ -44,7 +44,7 @@ pub struct AppLibrary {} impl AppLibraryState { pub fn new(default_apps: Vec) -> AppLibraryState { - let providers = AppLibrary::generate_provider_map(&default_apps); + let providers = AppLibrary::generate_provider_relation_map(&default_apps); AppLibraryState { default_apps, providers, @@ -90,7 +90,7 @@ impl AppLibrary { } } - fn generate_provider_map(apps: &[AppLibraryEntry]) -> HashMap { + fn generate_provider_relation_map(apps: &[AppLibraryEntry]) -> HashMap { let mut map = HashMap::new(); for app in apps.iter() { @@ -103,7 +103,10 @@ impl AppLibrary { map.insert(capability.clone(), app.app_id.clone()); } } else { - warn!("generate_provider_map: Not supported: {:?}", app.manifest); + warn!( + "generate_provider_relation_map: Not supported: {:?}", + app.manifest + ); } } @@ -137,7 +140,7 @@ mod tests { let app_library_state = AppLibraryState::new(default_apps.clone()); assert_eq!(app_library_state.default_apps, default_apps); - let providers = AppLibrary::generate_provider_map(&default_apps); + let providers = AppLibrary::generate_provider_relation_map(&default_apps); assert_eq!(app_library_state.providers, providers); } From 8e96069d039d98d6ed1036fd0514cb27ff03191c Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 17 Jul 2024 11:25:34 -0400 Subject: [PATCH 12/24] x-provided-by Pull pattern: Fixed broken tests due to sync --- .../firebolt/handlers/provider_registrar.rs | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 4dae26593..76d86347f 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -485,9 +485,10 @@ mod tests { use super::*; use jsonrpsee::core::server::rpc_module::Methods; + use ripple_sdk::tokio; - #[test] - fn test_register_methods() { + #[tokio::test] + async fn test_register_methods() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -506,8 +507,8 @@ mod tests { assert!(registered_methods == 0); } - #[test] - fn test_register_method_event_provided_by() { + #[tokio::test] + async fn test_register_method_event_provided_by() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -532,8 +533,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_event_provides() { + #[tokio::test] + async fn test_register_method_event_provides() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -558,8 +559,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_event_provides_to() { + #[tokio::test] + async fn test_register_method_event_provides_to() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -584,8 +585,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_provides_to() { + #[tokio::test] + async fn test_register_method_provides_to() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -610,8 +611,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_error_for() { + #[tokio::test] + async fn test_register_method_error_for() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -635,8 +636,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_provided_by() { + #[tokio::test] + async fn test_register_method_provided_by() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -660,8 +661,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_allow_focus_for() { + #[tokio::test] + async fn test_register_method_allow_focus_for() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -685,8 +686,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_response_for() { + #[tokio::test] + async fn test_register_method_response_for() { let mut methods = Methods::new(); let mut runtime = test_utils::MockRuntime::new(); runtime.platform_state.open_rpc_state = OpenRpcState::new(None); @@ -710,8 +711,8 @@ mod tests { assert!(registered_methods == 1); } - #[test] - fn test_register_method_duplicate() { + #[tokio::test] + async fn test_register_method_duplicate() { const METHOD_NAME: &str = "some.method"; let mut runtime = test_utils::MockRuntime::new(); From 11d2dbcd160dc3518063afe99b1cfcb9b38997bf Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Mon, 22 Jul 2024 18:48:43 -0400 Subject: [PATCH 13/24] x-provided-by Pull pattern: Fixed provider response flow --- .../firebolt/handlers/provider_registrar.rs | 48 ++++++++++++++----- core/main/src/service/user_grants.rs | 6 +-- core/main/src/state/cap/generic_cap_state.rs | 2 +- core/sdk/src/api/firebolt/provider.rs | 6 ++- 4 files changed, 42 insertions(+), 20 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 76d86347f..56361a6d9 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -130,6 +130,17 @@ impl ProviderRegistrar { }); } } + ProviderResponsePayloadType::Generic => { + let external_provider_response: Result, CallError> = + params_sequence.next(); + + if let Ok(r) = external_provider_response { + return Some(ProviderResponse { + correlation_id: r.correlation_id, + result: ProviderResponsePayload::Generic(r.result), + }); + } + } _ => error!("get_provider_response: Unsupported payload type"), } @@ -237,13 +248,21 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result, Error> { - info!("callback_app_event_emitter: method={}", context.method); + info!( + "callback_app_event_emitter: method={}, event={:?}", + context.method, &context.provider_relation_set.provides_to + ); if let Some(event) = &context.provider_relation_set.provides_to { let mut params_sequence = params.sequence(); let _call_context: CallContext = params_sequence.next().unwrap(); let result: Value = params_sequence.next().unwrap(); - AppEvents::emit(&context.platform_state, event, &result).await; + AppEvents::emit( + &context.platform_state, + &FireboltOpenRpcMethod::name_with_lowercase_module(event), + &result, + ) + .await; } Ok(None) @@ -278,7 +297,6 @@ impl ProviderRegistrar { params: Params<'static>, context: Arc, ) -> Result { - // TODO: Is 'Value' correct here? otherwise need to do bespoke return types? let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); let params: Value = params_sequence.next().unwrap(); @@ -307,7 +325,7 @@ impl ProviderRegistrar { capability: capability.clone(), method: provided_by.clone(), caller, - request: ProviderRequestPayload::Generic(params.to_string()), + request: ProviderRequestPayload::Generic(params), tx: provider_response_payload_tx, app_id: None, }; @@ -377,19 +395,23 @@ impl ProviderRegistrar { let params_sequence = params.sequence(); - if let Some(attributes) = context.provider_relation_set.attributes { - if let Some(provider_response) = ProviderRegistrar::get_provider_response( - attributes.response_payload_type.clone(), - params_sequence, - ) { - ProviderBroker::provider_response(&context.platform_state, provider_response).await; - } + let response_payload_type = match &context.provider_relation_set.attributes { + Some(attributes) => attributes.response_payload_type.clone(), + None => ProviderResponsePayloadType::Generic, + }; + + if let Some(provider_response) = + ProviderRegistrar::get_provider_response(response_payload_type, params_sequence) + { + ProviderBroker::provider_response(&context.platform_state, provider_response).await; } else { error!( - "callback_response: NO ATTRIBUTES: context.method={}", + "callback_response: Could not resolve response payload type: context.method={}", context.method ); - return Err(Error::Custom(String::from("Missing provider attributes"))); + return Err(Error::Custom(String::from( + "Couldn't resolve response payload type", + ))); } Ok(None) diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index 3dc2f89cb..c67caced7 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -1883,15 +1883,11 @@ impl GrantStepExecutor { * This is for any other capability, hoping it to deduce its necessary params from a json string * and has a challenge method. */ - let param_str = match param { - None => "".to_owned(), - Some(val) => val.to_string(), - }; Some(ProviderBrokerRequest { capability: p_cap.as_str(), method: String::from("challenge"), caller: caller_session.clone(), - request: ProviderRequestPayload::Generic(param_str), + request: ProviderRequestPayload::Generic(param.clone().unwrap_or(Value::Null)), tx: session_tx, app_id: None, }) diff --git a/core/main/src/state/cap/generic_cap_state.rs b/core/main/src/state/cap/generic_cap_state.rs index 48af66cf5..63db1590f 100644 --- a/core/main/src/state/cap/generic_cap_state.rs +++ b/core/main/src/state/cap/generic_cap_state.rs @@ -49,7 +49,7 @@ impl GenericCapState { FireboltCap::Short("token:platform".to_owned()), FireboltCap::Short("usergrant:acknowledgechallenge".to_owned()), FireboltCap::Short("usergrant:pinchallenge".to_owned()), - // Do we need to add "discovery:interest" etc. here or just in the manifests? + FireboltCap::Short("discovery:interest".to_owned()), ]; cap_state.ingest_availability(caps, false); cap_state diff --git a/core/sdk/src/api/firebolt/provider.rs b/core/sdk/src/api/firebolt/provider.rs index e48bd1626..dd25e2517 100644 --- a/core/sdk/src/api/firebolt/provider.rs +++ b/core/sdk/src/api/firebolt/provider.rs @@ -37,7 +37,7 @@ pub enum ProviderRequestPayload { AckChallenge(Challenge), EntityInfoRequest(EntityInfoParameters), PurchasedContentRequest(PurchasedContentParameters), - Generic(String), + Generic(serde_json::Value), } #[derive(Debug, Clone, Serialize)] @@ -48,6 +48,7 @@ pub enum ProviderResponsePayloadType { KeyboardResult, EntityInfoResponse, PurchasedContentResponse, + Generic, } impl ToString for ProviderResponsePayloadType { @@ -61,6 +62,7 @@ impl ToString for ProviderResponsePayloadType { ProviderResponsePayloadType::PurchasedContentResponse => { "PurchasedContentResponse".into() } + ProviderResponsePayloadType::Generic => "GenericResponse".into(), } } } @@ -75,6 +77,7 @@ pub enum ProviderResponsePayload { KeyboardResult(KeyboardSessionResponse), EntityInfoResponse(Option), PurchasedContentResponse(PurchasedContentResult), + Generic(serde_json::Value), } impl ProviderResponsePayload { @@ -130,6 +133,7 @@ impl ProviderResponsePayload { ProviderResponsePayload::PurchasedContentResponse(res) => { serde_json::to_value(res).unwrap() } + ProviderResponsePayload::Generic(res) => res.clone(), } } } From 1e2c1824d06fb344ec0912340e22f8aa09f83714 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Tue, 23 Jul 2024 14:20:32 -0400 Subject: [PATCH 14/24] x-provided-by Pull pattern: Fixed user grant provider invocation --- core/main/src/service/user_grants.rs | 182 +++++++++++++++++++++++++-- core/main/src/state/openrpc_state.rs | 5 + 2 files changed, 178 insertions(+), 9 deletions(-) diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index c67caced7..c13c6527a 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -1811,9 +1811,143 @@ impl GrantStepExecutor { app_name } + // + // pub async fn invoke_capability( + // platform_state: &PlatformState, + // // call_ctx: &CallContext, + // caller_session: &CallerSession, + // app_requested_for: &AppIdentification, + // cap: &FireboltCap, + // param: &Option, + // permission: &FireboltPermission, + // ) -> Result<(), DenyReasonWithCap> { + // let (session_tx, session_rx) = oneshot::channel::(); + // let p_cap = cap.clone(); + // /* + // * We have a concrete struct defined for ack challenge and pin challenge hence handling them separately. If any new + // * caps are introduced in future, the assumption is that capability provider has a method "challenge" and it can + // * deduce its params from a string. + // */ + // /* + // * this might be weird looking as_str().as_str(), FireboltCap returns String but has a function named as_str. + // * We call as_str on String to convert String to str to perform our match + // */ + // let for_app_id = &app_requested_for.app_id; + // let app_name = Self::get_app_name(platform_state, for_app_id.clone()).await; + // let pr_msg_opt = match p_cap.as_str().as_str() { + // "xrn:firebolt:capability:usergrant:acknowledgechallenge" => { + // let challenge = Challenge { + // capability: permission.cap.as_str(), + // requestor: ChallengeRequestor { + // id: for_app_id.clone(), + // name: app_name, + // }, + // }; + // Some(ProviderBrokerRequest { + // capability: p_cap.as_str(), + // method: String::from("challenge"), + // caller: caller_session.to_owned(), + // request: ProviderRequestPayload::AckChallenge(challenge), + // tx: session_tx, + // app_id: None, + // }) + // } + // "xrn:firebolt:capability:usergrant:pinchallenge" => { + // let pin_space_res = serde_json::from_value::( + // param.as_ref().unwrap_or(&Value::Null).clone(), + // ); + // if pin_space_res.is_err() { + // error!("Missing pin space for {}", permission.cap.as_str()); + // } + // pin_space_res.map_or(None, |pin_conf| { + // let challenge = PinChallengeRequest { + // pin_space: pin_conf.pin_space, + // requestor: ChallengeRequestor { + // id: for_app_id.clone(), + // name: app_name, + // }, + // capability: Some(permission.cap.as_str()), + // }; + // Some(ProviderBrokerRequest { + // capability: p_cap.as_str(), + // method: "challenge".to_owned(), + // caller: caller_session.clone(), + // request: ProviderRequestPayload::PinChallenge(challenge), + // tx: session_tx, + // app_id: None, + // }) + // }) + // } + // _ => { + // /* + // * This is for any other capability, hoping it to deduce its necessary params from a json string + // * and has a challenge method. + // */ + // Some(ProviderBrokerRequest { + // capability: p_cap.as_str(), + // method: String::from("challenge"), + // caller: caller_session.clone(), + // request: ProviderRequestPayload::Generic(param.clone().unwrap_or(Value::Null)), + // tx: session_tx, + // app_id: None, + // }) + // } + // }; + + // let result = if let Some(pr_msg) = pr_msg_opt { + // println!( + // "*** _DEBUG: invoke_capability: cap={}, method={}", + // pr_msg.capability, pr_msg.method + // ); + // ProviderBroker::invoke_method(&platform_state.clone(), pr_msg).await; + // match session_rx.await { + // Ok(result) => match result.as_challenge_response() { + // Some(res) => { + // match res.granted { + // Some(true) => { + // debug!("returning ok from invoke_capability"); + // Ok(()) + // } + // Some(false) => { + // debug!("returning err from invoke_capability"); + // Err(DenyReason::GrantDenied) + // } + // None => { + // debug!("Challenge left unanswered. Returning err from invoke_capability"); + // Err(DenyReason::Ungranted) + // } + // } + // } + // None => { + // debug!("Received reponse that is not convertable to challenge response"); + // Err(DenyReason::Ungranted) + // } + // }, + // Err(_) => { + // debug!("Receive error in channel"); + // Err(DenyReason::Ungranted) + // } + // } + // } else { + // /* + // * We would reach here if the cap is ack or pin + // * and we are not able to parse the configuration in the manifest + // * as pinchallenge or ackchallenge. + // */ + // Err(DenyReason::Ungranted) + // }; + + // if let Err(reason) = result { + // Err(DenyReasonWithCap { + // reason, + // caps: vec![permission.cap.clone()], + // }) + // } else { + // Ok(()) + // } + // } pub async fn invoke_capability( platform_state: &PlatformState, - // call_ctx: &CallContext, caller_session: &CallerSession, app_requested_for: &AppIdentification, cap: &FireboltCap, @@ -1822,11 +1956,36 @@ impl GrantStepExecutor { ) -> Result<(), DenyReasonWithCap> { let (session_tx, session_rx) = oneshot::channel::(); let p_cap = cap.clone(); - /* - * We have a concrete struct defined for ack challenge and pin challenge hence handling them separately. If any new - * caps are introduced in future, the assumption is that capability provider has a method "challenge" and it can - * deduce its params from a string. - */ + + let mut method_key: Option = None; + + for (key, provider_relation_set) in platform_state + .open_rpc_state + .get_provider_relation_map() + .iter() + { + if provider_relation_set.event { + if let Some(capability) = &provider_relation_set.provides { + if p_cap.as_str().eq(capability) { + method_key = Some(key.clone()); + } + } + } + } + + if method_key.is_none() { + error!( + "invoke_capability: Could not find provider for capability {}", + p_cap.as_str() + ); + return Err(DenyReasonWithCap { + reason: DenyReason::Ungranted, + caps: vec![permission.cap.clone()], + }); + } + + let method = method_key.unwrap(); + println!("*** _DEBUG: invoke_capability: method={}", method); /* * this might be weird looking as_str().as_str(), FireboltCap returns String but has a function named as_str. @@ -1845,7 +2004,7 @@ impl GrantStepExecutor { }; Some(ProviderBrokerRequest { capability: p_cap.as_str(), - method: String::from("challenge"), + method, caller: caller_session.to_owned(), request: ProviderRequestPayload::AckChallenge(challenge), tx: session_tx, @@ -1870,7 +2029,7 @@ impl GrantStepExecutor { }; Some(ProviderBrokerRequest { capability: p_cap.as_str(), - method: "challenge".to_owned(), + method, caller: caller_session.clone(), request: ProviderRequestPayload::PinChallenge(challenge), tx: session_tx, @@ -1885,7 +2044,7 @@ impl GrantStepExecutor { */ Some(ProviderBrokerRequest { capability: p_cap.as_str(), - method: String::from("challenge"), + method, caller: caller_session.clone(), request: ProviderRequestPayload::Generic(param.clone().unwrap_or(Value::Null)), tx: session_tx, @@ -1895,6 +2054,10 @@ impl GrantStepExecutor { }; let result = if let Some(pr_msg) = pr_msg_opt { + println!( + "*** _DEBUG: invoke_capability: cap={}, method={}", + pr_msg.capability, pr_msg.method + ); ProviderBroker::invoke_method(&platform_state.clone(), pr_msg).await; match session_rx.await { Ok(result) => match result.as_challenge_response() { @@ -1942,6 +2105,7 @@ impl GrantStepExecutor { Ok(()) } } + // } #[cfg(test)] diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 3e6a2682b..93602a0a5 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -143,6 +143,11 @@ pub fn build_provider_relation_sets( } } + println!( + "*** _DEBUG: provider_relation_sets: provider_relation_sets={:?}", + provider_relation_sets + ); + provider_relation_sets } From 35cae43c5f3382ca0cef477217544ab1a704f277 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Tue, 23 Jul 2024 16:10:36 -0400 Subject: [PATCH 15/24] x-provided-by Pull pattern: Bump --- core/main/src/service/user_grants.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index c13c6527a..badcd4df3 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -1969,6 +1969,9 @@ impl GrantStepExecutor { if p_cap.as_str().eq(capability) { method_key = Some(key.clone()); } + } else if let Some(provides_to) = &provider_relation_set.provides_to { + // YAH need to get and compare cap + // YAH run tests, failing in CI? } } } From 1aa96b974f1eb4f49ee84e03d9aa22701c350588 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Tue, 23 Jul 2024 16:19:36 -0400 Subject: [PATCH 16/24] x-provided-by Pull pattern: Clippy --- core/main/src/service/user_grants.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index badcd4df3..4aef7dfb6 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -1970,6 +1970,7 @@ impl GrantStepExecutor { method_key = Some(key.clone()); } } else if let Some(provides_to) = &provider_relation_set.provides_to { + println!("*** _DEBUG: invoke_capability: provides_to={}", provides_to); // YAH need to get and compare cap // YAH run tests, failing in CI? } From d3ece2facde9e6fc31e3d1ac98a38297474744d6 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Tue, 23 Jul 2024 16:29:17 -0400 Subject: [PATCH 17/24] x-provided-by Pull pattern: WIP --- core/main/src/service/user_grants.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index 4aef7dfb6..58002a17a 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -1982,10 +1982,14 @@ impl GrantStepExecutor { "invoke_capability: Could not find provider for capability {}", p_cap.as_str() ); - return Err(DenyReasonWithCap { - reason: DenyReason::Ungranted, - caps: vec![permission.cap.clone()], - }); + error!( + "*** _DEBUG: invoke_capability: Could not find provider for capability {}", + p_cap.as_str() + ); + // return Err(DenyReasonWithCap { + // reason: DenyReason::Ungranted, + // caps: vec![permission.cap.clone()], + // }); } let method = method_key.unwrap(); From 2b68524c0cf8be8dc126846a7dfdcc8031af2b15 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 24 Jul 2024 13:58:45 -0400 Subject: [PATCH 18/24] x-provided-by Pull pattern: Fixing provider capability mapping --- .../firebolt/handlers/provider_registrar.rs | 14 ++-- core/main/src/service/user_grants.rs | 27 +++---- core/main/src/state/openrpc_state.rs | 73 +++++++++++++++++-- 3 files changed, 87 insertions(+), 27 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 56361a6d9..443600b4c 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -219,7 +219,7 @@ impl ProviderRegistrar { ) -> Result { info!("callback_register_provider: method={}", context.method); - if let Some(provides) = &context.provider_relation_set.provides { + if let Some(capability) = &context.provider_relation_set.capability { let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); let request: ListenRequest = params_sequence.next().unwrap(); @@ -227,7 +227,7 @@ impl ProviderRegistrar { ProviderBroker::register_or_unregister_provider( &context.platform_state, - provides.clone(), + capability.clone(), context.method.clone(), context.method.clone(), call_context, @@ -312,7 +312,7 @@ impl ProviderRegistrar { if let Some(provided_by_set) = provider_relation_map.get( &FireboltOpenRpcMethod::name_with_lowercase_module(provided_by), ) { - if let Some(capability) = &provided_by_set.provides { + if let Some(capability) = &provided_by_set.capability { let (provider_response_payload_tx, provider_response_payload_rx) = oneshot::channel::(); @@ -368,7 +368,7 @@ impl ProviderRegistrar { ) -> Result, Error> { info!("callback_focus: method={}", context.method); - if let Some(provides) = &context.provider_relation_set.provides { + if let Some(capability) = &context.provider_relation_set.capability { let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); let request: FocusRequest = params_sequence.next().unwrap(); @@ -376,7 +376,7 @@ impl ProviderRegistrar { ProviderBroker::focus( &context.platform_state, call_context, - provides.clone(), + capability.clone(), request, ) .await; @@ -443,7 +443,7 @@ impl ProviderRegistrar { MethodType::AppEventListener, &mut rpc_module, ); - } else if provider_relation_set.provides.is_some() + } else if provider_relation_set.capability.is_some() || provider_relation_set.provides_to.is_some() { registered = Self::register_method( @@ -563,7 +563,7 @@ mod tests { let provider_relation_set = ProviderRelationSet { event: true, - provides: Some("some.capability".to_string()), + capability: Some("some.capability".to_string()), ..Default::default() }; diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index 58002a17a..30ce3e45f 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -1964,19 +1964,20 @@ impl GrantStepExecutor { .get_provider_relation_map() .iter() { - if provider_relation_set.event { - if let Some(capability) = &provider_relation_set.provides { - if p_cap.as_str().eq(capability) { - method_key = Some(key.clone()); - } - } else if let Some(provides_to) = &provider_relation_set.provides_to { - println!("*** _DEBUG: invoke_capability: provides_to={}", provides_to); - // YAH need to get and compare cap - // YAH run tests, failing in CI? + if let Some(provides) = &provider_relation_set.provides { + if provides.eq(&cap.as_str()) { + method_key = Some(key.clone()); + break; } } } + println!( + "*** _DEBUG: invoke_capability: cap={}, method_key={:?}", + cap.as_str(), + method_key + ); + if method_key.is_none() { error!( "invoke_capability: Could not find provider for capability {}", @@ -1986,10 +1987,10 @@ impl GrantStepExecutor { "*** _DEBUG: invoke_capability: Could not find provider for capability {}", p_cap.as_str() ); - // return Err(DenyReasonWithCap { - // reason: DenyReason::Ungranted, - // caps: vec![permission.cap.clone()], - // }); + return Err(DenyReasonWithCap { + reason: DenyReason::Ungranted, + caps: vec![permission.cap.clone()], + }); } let method = method_key.unwrap(); diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 93602a0a5..23e7ed306 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -43,15 +43,16 @@ pub enum ApiSurface { #[derive(Debug, Clone, Default)] pub struct ProviderRelationSet { - pub allow_focus_for: Option, - pub response_for: Option, - pub error_for: Option, + pub capability: Option, pub attributes: Option<&'static ProviderAttributes>, + pub event: bool, pub provides: Option, + pub provides_to: Option, pub provided_by: Option, pub uses: Option>, - pub event: bool, - pub provides_to: Option, + pub allow_focus_for: Option, + pub response_for: Option, + pub error_for: Option, } impl ProviderRelationSet { @@ -107,11 +108,14 @@ pub fn build_provider_relation_sets( .unwrap_or(&ProviderRelationSet::new()) .clone(); - if let Some(_capability) = has_x_provides { + // + //if let Some(_capability) = has_x_provides { + if has_x_provides.is_some() { + // provider_relation_set.allow_focus_for = x_allow_focus_for; provider_relation_set.response_for = x_response_for; provider_relation_set.error_for = x_error_for; - provider_relation_set.provides = x_provides; + provider_relation_set.capability = x_provides; } else { // x-provided-by can only be set if x-provides isn't. provider_relation_set.provided_by = x_provided_by.clone(); @@ -133,6 +137,12 @@ pub fn build_provider_relation_sets( provider_relation_set.uses = x_uses; provider_relation_set.event = has_event; + // + if provider_relation_set.event { + provider_relation_set.provides = provider_relation_set.capability.clone(); + } + // + let module: Vec<&str> = method.name.split('.').collect(); provider_relation_set.attributes = ProviderAttributes::get(module[0]); @@ -143,6 +153,55 @@ pub fn build_provider_relation_sets( } } + // + // Post-process sets to set 'provides' for methods that provide-to other methods. + + let provides_to_array: Vec<(String, String)> = provider_relation_sets + .iter() + .filter_map(|(method_name, provider_relation_set)| { + if let Some(provides_to) = &provider_relation_set.provides_to { + Some((method_name.clone(), provides_to.clone())) + } else { + None + } + }) + .collect(); + + for (provider_method, provides_to_method) in provides_to_array { + println!( + "*** _DEBUG: provides_method={}, provides_to_method={}", + provider_method, provides_to_method + ); + + let provided_to_capability = + if let Some(provided_to_set) = provider_relation_sets.get(&provides_to_method) { + provided_to_set.capability.clone() + } else { + None + }; + + if let Some(provider_set) = provider_relation_sets.get_mut(&provider_method) { + println!( + "*** _DEBUG: provider_set.provides={:?}, provider_set.capability={:?}, provided_to_capability={:?}", + provider_set.provides, provider_set.capability, provided_to_capability + ); + if provider_set.provides.is_none() { + provider_set.provides = provided_to_capability.clone(); + } + } + } + // + + // debug + for set in provider_relation_sets.iter() { + println!( + "*** _DEBUG: provider_relation_sets: method={}, provides={:?}", + set.0, set.1.provides + ); + } + + // + println!( "*** _DEBUG: provider_relation_sets: provider_relation_sets={:?}", provider_relation_sets From ca6b19f34cf92d3341a7903a7da8b7324b5d450f Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 24 Jul 2024 14:07:33 -0400 Subject: [PATCH 19/24] x-provided-by Pull pattern: Clippy --- core/main/src/state/openrpc_state.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 23e7ed306..32fcc5460 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -159,11 +159,15 @@ pub fn build_provider_relation_sets( let provides_to_array: Vec<(String, String)> = provider_relation_sets .iter() .filter_map(|(method_name, provider_relation_set)| { - if let Some(provides_to) = &provider_relation_set.provides_to { - Some((method_name.clone(), provides_to.clone())) - } else { - None - } + // if let Some(provides_to) = &provider_relation_set.provides_to { + // Some((method_name.clone(), provides_to.clone())) + // } else { + // None + // } + provider_relation_set + .provides_to + .as_ref() + .map(|provides_to| (method_name.clone(), provides_to.clone())) }) .collect(); From dec157c98d8c8b0ad808b21a23aeae6068e0e22c Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 24 Jul 2024 14:41:27 -0400 Subject: [PATCH 20/24] x-provided-by Pull pattern: Fixed tests --- core/main/src/processor/pin_processor.rs | 5 ++++- core/main/src/service/apps/provider_broker.rs | 1 + core/main/src/service/user_grants.rs | 10 ++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/core/main/src/processor/pin_processor.rs b/core/main/src/processor/pin_processor.rs index c7eadafe4..be1d6f4e1 100644 --- a/core/main/src/processor/pin_processor.rs +++ b/core/main/src/processor/pin_processor.rs @@ -87,7 +87,10 @@ impl ExtnRequestProcessor for PinProcessor { let (session_tx, session_rx) = oneshot::channel::(); let pr_msg = ProviderBrokerRequest { capability: String::from(PIN_CHALLENGE_CAPABILITY), - method: String::from("challenge"), + // + //method: String::from("challenge"), + method: String::from("pinchallenge.onRequestChallenge"), + // caller: pin_request.call_ctx.clone().into(), request: ProviderRequestPayload::PinChallenge(pin_request.into()), tx: session_tx, diff --git a/core/main/src/service/apps/provider_broker.rs b/core/main/src/service/apps/provider_broker.rs index 21cee5e7d..b4f79ca55 100644 --- a/core/main/src/service/apps/provider_broker.rs +++ b/core/main/src/service/apps/provider_broker.rs @@ -222,6 +222,7 @@ impl ProviderBroker { pub async fn invoke_method(pst: &PlatformState, request: ProviderBrokerRequest) { let cap_method = format!("{}:{}", request.capability, request.method); debug!("invoking provider for {}", cap_method); + println!("*** _DEBUG: invoke_method: pst.provider_broker_state.provider_methods.read().unwrap()={:?}", pst.provider_broker_state.provider_methods.read().unwrap()); let provider_opt = { let provider_methods = pst.provider_broker_state.provider_methods.read().unwrap(); diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index 30ce3e45f..11f1813e0 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -2170,7 +2170,10 @@ mod tests { ProviderBroker::register_or_unregister_provider( &state_c, String::from(PIN_CHALLENGE_CAPABILITY), - String::from("challenge"), + // + // String::from("challenge"), + String::from(PIN_CHALLENGE_EVENT), + // String::from(PIN_CHALLENGE_EVENT), ctx_c.clone(), ListenRequest { listen: true }, @@ -2180,7 +2183,10 @@ mod tests { ProviderBroker::register_or_unregister_provider( &state_c, String::from(ACK_CHALLENGE_CAPABILITY), - String::from("challenge"), + // + //String::from("challenge"), + String::from(ACK_CHALLENGE_EVENT), + // String::from(ACK_CHALLENGE_EVENT), ctx_c.clone(), ListenRequest { listen: true }, From f8d9aa0ce871dc5d825277cb28d1bd030492c307 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 24 Jul 2024 16:04:38 -0400 Subject: [PATCH 21/24] x-provided-by Pull pattern: Removed debug --- core/main/src/processor/pin_processor.rs | 3 - core/main/src/service/apps/provider_broker.rs | 1 - core/main/src/service/user_grants.rs | 157 ------------------ core/main/src/state/openrpc_state.rs | 37 +---- 4 files changed, 1 insertion(+), 197 deletions(-) diff --git a/core/main/src/processor/pin_processor.rs b/core/main/src/processor/pin_processor.rs index be1d6f4e1..2e5f4e84a 100644 --- a/core/main/src/processor/pin_processor.rs +++ b/core/main/src/processor/pin_processor.rs @@ -87,10 +87,7 @@ impl ExtnRequestProcessor for PinProcessor { let (session_tx, session_rx) = oneshot::channel::(); let pr_msg = ProviderBrokerRequest { capability: String::from(PIN_CHALLENGE_CAPABILITY), - // - //method: String::from("challenge"), method: String::from("pinchallenge.onRequestChallenge"), - // caller: pin_request.call_ctx.clone().into(), request: ProviderRequestPayload::PinChallenge(pin_request.into()), tx: session_tx, diff --git a/core/main/src/service/apps/provider_broker.rs b/core/main/src/service/apps/provider_broker.rs index b4f79ca55..21cee5e7d 100644 --- a/core/main/src/service/apps/provider_broker.rs +++ b/core/main/src/service/apps/provider_broker.rs @@ -222,7 +222,6 @@ impl ProviderBroker { pub async fn invoke_method(pst: &PlatformState, request: ProviderBrokerRequest) { let cap_method = format!("{}:{}", request.capability, request.method); debug!("invoking provider for {}", cap_method); - println!("*** _DEBUG: invoke_method: pst.provider_broker_state.provider_methods.read().unwrap()={:?}", pst.provider_broker_state.provider_methods.read().unwrap()); let provider_opt = { let provider_methods = pst.provider_broker_state.provider_methods.read().unwrap(); diff --git a/core/main/src/service/user_grants.rs b/core/main/src/service/user_grants.rs index 11f1813e0..ed0f9f7f3 100644 --- a/core/main/src/service/user_grants.rs +++ b/core/main/src/service/user_grants.rs @@ -1811,141 +1811,6 @@ impl GrantStepExecutor { app_name } - // - // pub async fn invoke_capability( - // platform_state: &PlatformState, - // // call_ctx: &CallContext, - // caller_session: &CallerSession, - // app_requested_for: &AppIdentification, - // cap: &FireboltCap, - // param: &Option, - // permission: &FireboltPermission, - // ) -> Result<(), DenyReasonWithCap> { - // let (session_tx, session_rx) = oneshot::channel::(); - // let p_cap = cap.clone(); - // /* - // * We have a concrete struct defined for ack challenge and pin challenge hence handling them separately. If any new - // * caps are introduced in future, the assumption is that capability provider has a method "challenge" and it can - // * deduce its params from a string. - // */ - // /* - // * this might be weird looking as_str().as_str(), FireboltCap returns String but has a function named as_str. - // * We call as_str on String to convert String to str to perform our match - // */ - // let for_app_id = &app_requested_for.app_id; - // let app_name = Self::get_app_name(platform_state, for_app_id.clone()).await; - // let pr_msg_opt = match p_cap.as_str().as_str() { - // "xrn:firebolt:capability:usergrant:acknowledgechallenge" => { - // let challenge = Challenge { - // capability: permission.cap.as_str(), - // requestor: ChallengeRequestor { - // id: for_app_id.clone(), - // name: app_name, - // }, - // }; - // Some(ProviderBrokerRequest { - // capability: p_cap.as_str(), - // method: String::from("challenge"), - // caller: caller_session.to_owned(), - // request: ProviderRequestPayload::AckChallenge(challenge), - // tx: session_tx, - // app_id: None, - // }) - // } - // "xrn:firebolt:capability:usergrant:pinchallenge" => { - // let pin_space_res = serde_json::from_value::( - // param.as_ref().unwrap_or(&Value::Null).clone(), - // ); - // if pin_space_res.is_err() { - // error!("Missing pin space for {}", permission.cap.as_str()); - // } - // pin_space_res.map_or(None, |pin_conf| { - // let challenge = PinChallengeRequest { - // pin_space: pin_conf.pin_space, - // requestor: ChallengeRequestor { - // id: for_app_id.clone(), - // name: app_name, - // }, - // capability: Some(permission.cap.as_str()), - // }; - // Some(ProviderBrokerRequest { - // capability: p_cap.as_str(), - // method: "challenge".to_owned(), - // caller: caller_session.clone(), - // request: ProviderRequestPayload::PinChallenge(challenge), - // tx: session_tx, - // app_id: None, - // }) - // }) - // } - // _ => { - // /* - // * This is for any other capability, hoping it to deduce its necessary params from a json string - // * and has a challenge method. - // */ - // Some(ProviderBrokerRequest { - // capability: p_cap.as_str(), - // method: String::from("challenge"), - // caller: caller_session.clone(), - // request: ProviderRequestPayload::Generic(param.clone().unwrap_or(Value::Null)), - // tx: session_tx, - // app_id: None, - // }) - // } - // }; - - // let result = if let Some(pr_msg) = pr_msg_opt { - // println!( - // "*** _DEBUG: invoke_capability: cap={}, method={}", - // pr_msg.capability, pr_msg.method - // ); - // ProviderBroker::invoke_method(&platform_state.clone(), pr_msg).await; - // match session_rx.await { - // Ok(result) => match result.as_challenge_response() { - // Some(res) => { - // match res.granted { - // Some(true) => { - // debug!("returning ok from invoke_capability"); - // Ok(()) - // } - // Some(false) => { - // debug!("returning err from invoke_capability"); - // Err(DenyReason::GrantDenied) - // } - // None => { - // debug!("Challenge left unanswered. Returning err from invoke_capability"); - // Err(DenyReason::Ungranted) - // } - // } - // } - // None => { - // debug!("Received reponse that is not convertable to challenge response"); - // Err(DenyReason::Ungranted) - // } - // }, - // Err(_) => { - // debug!("Receive error in channel"); - // Err(DenyReason::Ungranted) - // } - // } - // } else { - // /* - // * We would reach here if the cap is ack or pin - // * and we are not able to parse the configuration in the manifest - // * as pinchallenge or ackchallenge. - // */ - // Err(DenyReason::Ungranted) - // }; - - // if let Err(reason) = result { - // Err(DenyReasonWithCap { - // reason, - // caps: vec![permission.cap.clone()], - // }) - // } else { - // Ok(()) - // } - // } pub async fn invoke_capability( platform_state: &PlatformState, caller_session: &CallerSession, @@ -1972,21 +1837,11 @@ impl GrantStepExecutor { } } - println!( - "*** _DEBUG: invoke_capability: cap={}, method_key={:?}", - cap.as_str(), - method_key - ); - if method_key.is_none() { error!( "invoke_capability: Could not find provider for capability {}", p_cap.as_str() ); - error!( - "*** _DEBUG: invoke_capability: Could not find provider for capability {}", - p_cap.as_str() - ); return Err(DenyReasonWithCap { reason: DenyReason::Ungranted, caps: vec![permission.cap.clone()], @@ -1994,7 +1849,6 @@ impl GrantStepExecutor { } let method = method_key.unwrap(); - println!("*** _DEBUG: invoke_capability: method={}", method); /* * this might be weird looking as_str().as_str(), FireboltCap returns String but has a function named as_str. @@ -2063,10 +1917,6 @@ impl GrantStepExecutor { }; let result = if let Some(pr_msg) = pr_msg_opt { - println!( - "*** _DEBUG: invoke_capability: cap={}, method={}", - pr_msg.capability, pr_msg.method - ); ProviderBroker::invoke_method(&platform_state.clone(), pr_msg).await; match session_rx.await { Ok(result) => match result.as_challenge_response() { @@ -2114,7 +1964,6 @@ impl GrantStepExecutor { Ok(()) } } - // } #[cfg(test)] @@ -2170,10 +2019,7 @@ mod tests { ProviderBroker::register_or_unregister_provider( &state_c, String::from(PIN_CHALLENGE_CAPABILITY), - // - // String::from("challenge"), String::from(PIN_CHALLENGE_EVENT), - // String::from(PIN_CHALLENGE_EVENT), ctx_c.clone(), ListenRequest { listen: true }, @@ -2183,10 +2029,7 @@ mod tests { ProviderBroker::register_or_unregister_provider( &state_c, String::from(ACK_CHALLENGE_CAPABILITY), - // - //String::from("challenge"), String::from(ACK_CHALLENGE_EVENT), - // String::from(ACK_CHALLENGE_EVENT), ctx_c.clone(), ListenRequest { listen: true }, diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 32fcc5460..90a56678a 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -108,10 +108,7 @@ pub fn build_provider_relation_sets( .unwrap_or(&ProviderRelationSet::new()) .clone(); - // - //if let Some(_capability) = has_x_provides { if has_x_provides.is_some() { - // provider_relation_set.allow_focus_for = x_allow_focus_for; provider_relation_set.response_for = x_response_for; provider_relation_set.error_for = x_error_for; @@ -137,11 +134,10 @@ pub fn build_provider_relation_sets( provider_relation_set.uses = x_uses; provider_relation_set.event = has_event; - // + // If this is an event, then it provides the capability. if provider_relation_set.event { provider_relation_set.provides = provider_relation_set.capability.clone(); } - // let module: Vec<&str> = method.name.split('.').collect(); provider_relation_set.attributes = ProviderAttributes::get(module[0]); @@ -153,17 +149,11 @@ pub fn build_provider_relation_sets( } } - // // Post-process sets to set 'provides' for methods that provide-to other methods. let provides_to_array: Vec<(String, String)> = provider_relation_sets .iter() .filter_map(|(method_name, provider_relation_set)| { - // if let Some(provides_to) = &provider_relation_set.provides_to { - // Some((method_name.clone(), provides_to.clone())) - // } else { - // None - // } provider_relation_set .provides_to .as_ref() @@ -172,11 +162,6 @@ pub fn build_provider_relation_sets( .collect(); for (provider_method, provides_to_method) in provides_to_array { - println!( - "*** _DEBUG: provides_method={}, provides_to_method={}", - provider_method, provides_to_method - ); - let provided_to_capability = if let Some(provided_to_set) = provider_relation_sets.get(&provides_to_method) { provided_to_set.capability.clone() @@ -185,31 +170,11 @@ pub fn build_provider_relation_sets( }; if let Some(provider_set) = provider_relation_sets.get_mut(&provider_method) { - println!( - "*** _DEBUG: provider_set.provides={:?}, provider_set.capability={:?}, provided_to_capability={:?}", - provider_set.provides, provider_set.capability, provided_to_capability - ); if provider_set.provides.is_none() { provider_set.provides = provided_to_capability.clone(); } } } - // - - // debug - for set in provider_relation_sets.iter() { - println!( - "*** _DEBUG: provider_relation_sets: method={}, provides={:?}", - set.0, set.1.provides - ); - } - - // - - println!( - "*** _DEBUG: provider_relation_sets: provider_relation_sets={:?}", - provider_relation_sets - ); provider_relation_sets } From 21438b9db5b6b51ef9d7683b578a6807ba26eba8 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 24 Jul 2024 17:25:35 -0400 Subject: [PATCH 22/24] x-provided-by Pull pattern: Fixed problem with provider event string case --- core/main/src/firebolt/handlers/provider_registrar.rs | 6 +++++- core/main/src/service/apps/provider_broker.rs | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/main/src/firebolt/handlers/provider_registrar.rs b/core/main/src/firebolt/handlers/provider_registrar.rs index 443600b4c..4cfb06e7e 100644 --- a/core/main/src/firebolt/handlers/provider_registrar.rs +++ b/core/main/src/firebolt/handlers/provider_registrar.rs @@ -50,7 +50,7 @@ use ripple_sdk::{ use serde_json::Value; // TODO: Add to config -const DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS: u64 = 5000; +const DEFAULT_PROVIDER_RESPONSE_TIMEOUT_MS: u64 = 15000; #[derive(Debug)] enum MethodType { @@ -195,6 +195,10 @@ impl ProviderRegistrar { context: Arc, ) -> Result { info!("callback_app_event_listener: method={}", context.method); + info!( + "*** DEBUG: callback_app_event_listener: method/event={}", + context.method + ); let mut params_sequence = params.sequence(); let call_context: CallContext = params_sequence.next().unwrap(); diff --git a/core/main/src/service/apps/provider_broker.rs b/core/main/src/service/apps/provider_broker.rs index 21cee5e7d..b3728ff96 100644 --- a/core/main/src/service/apps/provider_broker.rs +++ b/core/main/src/service/apps/provider_broker.rs @@ -24,6 +24,7 @@ use ripple_sdk::{ fb_lifecycle_management::{ LifecycleManagementEventRequest, LifecycleManagementProviderEvent, }, + fb_openrpc::FireboltOpenRpcMethod, provider::{ FocusRequest, ProviderRequest, ProviderRequestPayload, ProviderResponse, ProviderResponsePayload, @@ -220,7 +221,11 @@ impl ProviderBroker { } pub async fn invoke_method(pst: &PlatformState, request: ProviderBrokerRequest) { - let cap_method = format!("{}:{}", request.capability, request.method); + let cap_method = format!( + "{}:{}", + request.capability, + FireboltOpenRpcMethod::name_with_lowercase_module(&request.method) + ); debug!("invoking provider for {}", cap_method); let provider_opt = { From 6c5f9d575c191cf372883b34e3fb1c91645c9d9d Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Wed, 24 Jul 2024 17:34:29 -0400 Subject: [PATCH 23/24] x-provided-by Pull pattern: Allow availability of discovery:interest so calls like Content.onUserInterest can be made before provider is launched. --- core/main/src/state/cap/generic_cap_state.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/main/src/state/cap/generic_cap_state.rs b/core/main/src/state/cap/generic_cap_state.rs index 63db1590f..7653b69db 100644 --- a/core/main/src/state/cap/generic_cap_state.rs +++ b/core/main/src/state/cap/generic_cap_state.rs @@ -49,7 +49,6 @@ impl GenericCapState { FireboltCap::Short("token:platform".to_owned()), FireboltCap::Short("usergrant:acknowledgechallenge".to_owned()), FireboltCap::Short("usergrant:pinchallenge".to_owned()), - FireboltCap::Short("discovery:interest".to_owned()), ]; cap_state.ingest_availability(caps, false); cap_state From 9e2d0444f0b1fee9abdd8f2a26c2156df07ba919 Mon Sep 17 00:00:00 2001 From: pahearn73 Date: Thu, 25 Jul 2024 13:09:51 -0400 Subject: [PATCH 24/24] x-provided-by Pull pattern: Only process user interest discovery methods --- core/main/src/state/openrpc_state.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/main/src/state/openrpc_state.rs b/core/main/src/state/openrpc_state.rs index 90a56678a..c0eec7680 100644 --- a/core/main/src/state/openrpc_state.rs +++ b/core/main/src/state/openrpc_state.rs @@ -72,8 +72,11 @@ pub fn build_provider_relation_sets( // Only build provider sets for AcknowledgeChallenge, PinChallenge methods, Discovery, and Content for now if !method.name.starts_with("AcknowledgeChallenge.") && !method.name.starts_with("PinChallenge.") - && !method.name.starts_with("Discovery.") - && !method.name.starts_with("Content.") + && !method.name.starts_with("Discovery.userInterest") + && !method.name.starts_with("Discovery.onRequestUserInterest") + && !method.name.starts_with("Discovery.userInterestResponse") + && !method.name.starts_with("Content.requestUserInterest") + && !method.name.starts_with("Content.onUserInterest") { continue; }