From 05263db8814233f811030ade0bff4549a256c863 Mon Sep 17 00:00:00 2001 From: kpears201 Date: Fri, 23 Aug 2024 10:22:21 -0700 Subject: [PATCH] =?UTF-8?q?Adding=20application=20context=20sections,=20ad?= =?UTF-8?q?ding=20documentation=20on=20selectin=E2=80=A6=20(#290)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding application context sections, adding documentation on selecting the provider should a request parameter. --------- Co-authored-by: Jeremy LaCivita --- package-lock.json | 14 +-- package.json | 2 +- .../app-passthrough-apis.md | 113 +++++++++++++----- src/openrpc/content.json | 2 + 4 files changed, 92 insertions(+), 39 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8af224f25..142543654 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "devDependencies": { "@commitlint/cli": "^17.0.3", "@commitlint/config-conventional": "^17.0.3", - "@firebolt-js/openrpc": "3.1.0", + "@firebolt-js/openrpc": "3.1.1-next.2", "@firebolt-js/schemas": "2.0.0", "@saithodev/semantic-release-backmerge": "^3.2.0", "@semantic-release/changelog": "^6.0.1", @@ -1075,9 +1075,9 @@ "link": true }, "node_modules/@firebolt-js/openrpc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@firebolt-js/openrpc/-/openrpc-3.1.0.tgz", - "integrity": "sha512-OUbEInML9oF8d3P5brYN8iHUdfYxO5xBvnKm52ckyy6nJ7jYL2RJDobpeZa7flfeauuFXzkSK/ZMDtiOFxqQRg==", + "version": "3.1.1-next.2", + "resolved": "https://registry.npmjs.org/@firebolt-js/openrpc/-/openrpc-3.1.1-next.2.tgz", + "integrity": "sha512-D5b8ovtmRQNSlJ52LRJHtp0rmvlao6axzpmqYjW8wsljNeTj9QcAJ1xiUiYBt2uFitvGM6zQA04e3rbJhErJ+w==", "dev": true, "dependencies": { "ajv": "^8.12.0", @@ -16962,7 +16962,7 @@ }, "src/sdks/core": { "name": "@firebolt-js/sdk", - "version": "1.3.0", + "version": "1.3.1-next.1", "license": "Apache-2.0", "devDependencies": { "jest": "^28.1.0", @@ -16973,7 +16973,7 @@ }, "src/sdks/discovery": { "name": "@firebolt-js/discovery-sdk", - "version": "1.3.0", + "version": "1.3.1-next.1", "license": "Apache-2.0", "devDependencies": { "jest": "^28.1.0", @@ -16984,7 +16984,7 @@ }, "src/sdks/manage": { "name": "@firebolt-js/manage-sdk", - "version": "1.3.0", + "version": "1.3.1-next.1", "license": "Apache-2.0", "devDependencies": { "jest": "^28.1.0", diff --git a/package.json b/package.json index dc82211d3..240b0feb8 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "devDependencies": { "@commitlint/cli": "^17.0.3", "@commitlint/config-conventional": "^17.0.3", - "@firebolt-js/openrpc": "3.1.0", + "@firebolt-js/openrpc": "3.1.1-next.2", "@firebolt-js/schemas": "2.0.0", "@saithodev/semantic-release-backmerge": "^3.2.0", "@semantic-release/changelog": "^6.0.1", diff --git a/requirements/specifications/openrpc-extensions/app-passthrough-apis.md b/requirements/specifications/openrpc-extensions/app-passthrough-apis.md index 826f7bed3..51d7e460d 100644 --- a/requirements/specifications/openrpc-extensions/app-passthrough-apis.md +++ b/requirements/specifications/openrpc-extensions/app-passthrough-apis.md @@ -28,18 +28,20 @@ The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL - [2. Table of Contents](#2-table-of-contents) - [3. Open RPC Extensions](#3-open-rpc-extensions) - [3.1. Provided By Extension](#31-provided-by-extension) + - [3.2. Provider Selection Extension](#32-provider-selection-extension) - [4. Routing App pass-through APIs](#4-routing-app-pass-through-apis) - [4.1. No available providers](#41-no-available-providers) - [4.2. Direct pass-through](#42-direct-pass-through) - - [4.4. Pass-through notifications](#44-pass-through-notifications) + - [4.3. Pass-through notifications](#43-pass-through-notifications) - [5. Provider Candidates](#5-provider-candidates) - [6. Best Candidate](#6-best-candidate) -- [7. Result Transformations](#7-result-transformations) -- [8. Provider Parameter Injection](#8-provider-parameter-injection) -- [9. API Gateway](#9-api-gateway) -- [10. Example: User Interest](#10-example-user-interest) - - [10.1. User Interest Pull](#101-user-interest-pull) - - [10.2. User Interest Push](#102-user-interest-push) +- [7. Application Context](#7-application-context) + - [7.1. Application Context Surfacing](#71-application-context-surfacing) + - [7.2. Application Context Selection](#72-application-context-selection) +- [8. API Gateway](#8-api-gateway) +- [9. Example: User Interest](#9-example-user-interest) + - [9.1. User Interest Pull](#91-user-interest-pull) + - [9.2. User Interest Push](#92-user-interest-push) ## 3. Open RPC Extensions @@ -77,13 +79,46 @@ The provider method **MUST** provide the same capability that the platform metho If a platform method has no provider method then it is not a valid Firebolt OpenRPC method schema, and a validation error **MUST** be generated. +### 3.2. Provider Selection Extension +Firebolt OpenRPC **MUST** support a `string` `x-provider-selection` extension property on the `capabilities` tag that denotes how to pick the best provider candidate, e.g.: + +```json +{ + "methods": [ + { + "name": "Keyboard.standard", + "tags": [ + { + "name": "capabilities", + "x-provided-by": "Keyboard.onRequestStandard", + "x-provider-selection": "appId" + "x-uses": [ + "xrn:firebolt:capability:input:keyboard" + ] + } + ], + "parameters": [ + { + "name": "appId", + "type": "string" + } + ] + } + ] +} +``` + +The value of `x-provider-selection`, if defined, **MUST** be either `"appId"` or `"focus"`. + +Given the `x-provider-selection` extension has the value `"appId"`, then the method **MUST** have an `appId` `string` parameter. + ## 4. Routing App pass-through APIs App pass-through APIs may be routed in one of several ways. When an app calls a platform method, i.e. one with an `x-provided-by` extension, the platform **MUST** use one of the routing methods defined in this section based on various properties of the method. ### 4.1. No available providers -When an app calls a platform method with an `x-provided-by` extension, the platform **MUST** return an unavailable error if there is no [candidate app](#7-provider-candidates) to execute the provider method. +When an app calls a platform method with an `x-provided-by` extension, the platform **MUST** return an unavailable error if there is no [candidate app](#5-provider-candidates) to execute the provider method. ```json { @@ -107,17 +142,17 @@ The platform method result schema **MUST** either: > Match the `x-response` schema on the provider method so that the result can be passed through. > > or -> +> > Have a property that matches the `x-response-name` string and `x-response` schema on the > provider method so that the result can be composed and passed through. -The platform **MUST** call the provider method from the [best candidate](#8-best-candidate) app and acquire the result. +The platform **MUST** call the provider method from the [best candidate](#6-best-candidate) app and acquire the result. If the platform method result schema matches the `x-response` schema on the provider method then the value **MUST** be used as-is. -Otherwise if the platform method result schema has a property that matches the `x-response` schema on the provider method then the value **MUST** be composed into an object under the corresponding property name and the platform **MUST** apply any [result transformations](#9-result-transformations) to the composed result. +Otherwise if the platform method result schema has a property that matches the `x-response` schema on the provider method then the value **MUST** be composed into an object under the corresponding property name. -### 4.4. Pass-through notifications +### 4.3. Pass-through notifications Firebolt events have a synchronous subscriber registration method, e.g. `Lifecycle.onInactive(true)`, in addition to asynchronous notifications when the event actually happens. For events powered by an app pass-through, only the asynchronous notifications are passed in by the providing app. The initial event registration is handled by the platform, and the success response is not handled by the providing app. This section only applies to platform methods that have an `event` tag. @@ -205,11 +240,11 @@ Matching provider method: } ``` -When a provider app calls a provider method mapped to an event the platform **MUST** ignore the notification if the app is not a [candidate app](#7-provider-candidates) for this capability. +When a provider app calls a provider method mapped to an event the platform **MUST** ignore the notification if the app is not a [candidate app](#5-provider-candidates) for this capability. If the platform method result schema matches the *last* parameter schema on the provider method then the value **MUST** be used as-is. -Otherwise if the platform method result schema has a property that matches the *last* parameter schema on the provider method then the value **MUST** be composed into an object under the corresponding property name and the platform **MUST** apply any [result transformations](#9-result-transformations) to the composed result. +Otherwise if the platform method result schema has a property that matches the *last* parameter schema on the provider method then the value **MUST** be composed into an object under the corresponding property name. If the value was composed into the platform method result under a matching property, then any context parameter values from the provider method that correspond to a property name and schema in the platform method result **MUST** also be composed into the platform method result under those properties. @@ -221,32 +256,48 @@ all loaded apps that have permission to provide the capability **MUST** be considered as candidates to fulfill the method. ## 6. Best Candidate -Any provider candidates that have not registered to provide the method in -question **MUST NOT** be considered the best candidate and removed from -consideration. +A provider candidate app **MUST** include the capability in the `provide` section of the app manifest. + +If a provider candidate is currently running, it **MUST** have already registered to provide the capability in +question. -If there is only one candidate left then it **MUST** be the best candidate. +If there is exactly one provider candidate then it **MUST** be the best candidate. -If there is more than one candidate left, then the candidate app that most recently had RCU input focus **MUST** be the best candidate. +If there is more than one provider candidate then the following requirements **MUST** be used to select the best candidate, in order. -If none of the candidates have had focus yet, then the candidate app that was most recently launched **MUST** be the best candidate. +> Given there is an `x-provider-selection` extension on the platform method capabilities tag, when that extension is set to the value `"appId"` then the provider candidate with an appId that matches the value of the platform method `appId` parameter **MUST** be the best candidate. +> +> Given there is an `x-provider-selection` extension on the platform method capabilities tag, when that extension is set to the value `"focus"` then the provider candidate that currently has RCU focus **MUST** be the best candidate. +> +> Given there is no `x-provider-selection` extension, or it has a value not defined in this specification, when there is a provider candidate that was launched more recently then the other candidates then that provider candidate **MUST** be the best candidate. + +## 7. Application Context + +Application Context provides a mechanism for applications to know the identity of the applications they are communicating with. Application Context can be given in both directions. An application that uses a capability can know which app is providing that capability. An application that provides a capability can know which app is using that capability. + +### 7.1. Application Context Surfacing -## 7. Result Transformations -A platform method may be configured to insert the providing app id into composite values. This is not allowed in non-composite results to avoid collisions with the provder method sending an appId and Firebolt overriding it. +A method may be configured to surface application context by inserting the "other" appId into the request or response and is triggered by the presense of an `appId` in either the platform method or the provider method, but not both. If a "composite result" was used to wrap the provider method value and the platform method's schema has an `appId` `string` property at the top level then the property's value **MUST** be set to the the appId of the providing app for that result. -## 8. Provider Parameter Injection -If the provider method has a parameter named `appId` and the platform method *does not*, then the appId of the app calling the platform method **MUST** be sent to the provider in the `appId` parameter. +If a platform method is an `event` and the event result is a "composite result" with an `appId` `string` property at the top level, then the property **MUST** be set to the appId that initiated the provider (push) call. -If the platform method is an `event` and the provider method has context parameters then each context parameter from the provider that has a matching context parameter in the event **MUST** have it's value passed to that parameter. +If a platform method is an `event` and the event has an `appId` `string` context parameter and the provider method *does not* have an `appId` parameter, then the `appId` context parameter **MUST** be set to the appId that initiated the provider (push) call. -If the platform method is an `event` with a "composite result" and the provider method has context parameters then each context parameter from the provider that has a matching property on the `result` object **MUST** have it's value copied into that property. +If the provider method has an `appId` `string` parameter and the platform method does not have an `appId` parameter, then the id of app that initiated the platform method call **MUST** be used to set the `appId` in the provider method request. -## 9. API Gateway +### 7.2. Application Context Selection + +A method may be configured to enabled application context selection by exposing an appId parameter to allow the calling app to influence which app will provide it. + +When a platform method is invoked, the gateway will find the provider using the `Provider Candidate` rules as described above. However, some Firebolt APIs allow selecting the provider that should be used. If a platform method request schema is a "composite request" with `appId` `string` parameter but the provider method request schema is not a composite request, then the given appId shall be used to select the provider. +If "appId" is a required parameter in the platform method request schema, then it must be supplied. If it is not, then the request should fail with invalid parameters. If it is an optional parameter and it is not supplied, then the gateway should use rules in "Provider Candidate" section for selecting the candidate. + +## 8. API Gateway The Firebolt API Gateway **MUST** detect app-passthrough APIs and map the `use`/`manage` APIs to the corresponding `provide` APIs by parsing the Firebolt OpenRPC Specification and following the logic outline in this document. -## 10. Example: User Interest +## 9. Example: User Interest The following schemas are referenced by these examples: @@ -287,7 +338,7 @@ The following schemas are referenced by these examples: } ``` -### 10.1. User Interest Pull +### 9.1. User Interest Pull Platform method: @@ -370,7 +421,7 @@ Provider method: } ``` -### 10.2. User Interest Push +### 9.2. User Interest Push Provider method: @@ -454,7 +505,7 @@ Platform Method: "$ref": "https://meta.comcast.com/firebolt/entity#/definitions/EntityDetails" } } - + } } } diff --git a/src/openrpc/content.json b/src/openrpc/content.json index 0ffbeadbf..ce26b7c5a 100644 --- a/src/openrpc/content.json +++ b/src/openrpc/content.json @@ -12,6 +12,7 @@ { "name": "capabilities", "x-provided-by": "Discovery.onRequestUserInterest", + "x-provider-selection": "focus", "x-uses": [ "xrn:firebolt:capability:discovery:interest" ] @@ -95,6 +96,7 @@ { "name": "capabilities", "x-provided-by": "Discovery.userInterest", + "x-provider-selection": "focus", "x-uses": [ "xrn:firebolt:capability:discovery:interest" ]