diff --git a/requirements/specifications/index.md b/requirements/specifications/index.md new file mode 100644 index 000000000..d2eda9457 --- /dev/null +++ b/requirements/specifications/index.md @@ -0,0 +1,70 @@ +# Firebolt Requirements + +Document Status: Working Draft + +See [Firebolt Requirements Governance](../governance.md) for more info. + +| Contributor | Organization | +| -------------- | -------------- | +| Jeremy LaCivita | Comcast | + +## 1. Overview +All of the documents in this `/requirements/` directory comprise the collected set of requirements for this Firebolt version. + +This document covers the Firebolt Specification JSON, [./firebolt-specification.json](./firebolt-specification.json), which is a JSON document outlining all Firebolt Capabilities and whether they MUST, SHOULD, or COULD be supported. + +This document is written using the [IETF Best Common Practice 14](https://www.rfc-editor.org/rfc/rfc2119.txt) and should include the following summary in the Overview section: + +The key words "**MUST**", "**MUST NOT**", "**REQUIRED**", "**SHALL**", "**SHALL NOT**", "**SHOULD**", "**SHOULD NOT**", "**RECOMMENDED**", "**NOT RECOMMENDED**", "**MAY**", and "**OPTIONAL**" in this document are to be interpreted as described in [BCP 14](https://www.rfc-editor.org/rfc/rfc2119.txt) [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here. + +## 2. Table of Contents +- [1. Overview](#1-overview) +- [2. Table of Contents](#2-table-of-contents) +- [3. Capabilities](#3-capabilities) + - [3.1. Capability Level](#31-capability-level) + - [3.1.1. Should vs Could](#311-should-vs-could) + - [3.1.2. Supported Capability](#312-supported-capability) + +## 3. Capabilities +All Firebolt APIs are assigned a capability by either using it, managing it, or providing it. + +See [Capabilities](./general/capabilities/capabilities.md) for more info. + +### 3.1. Capability Level +In the Firebolt Specification JSON, every capability is given a `level` of either: + +- `"must"` +- `"should"` +- `"could"` + +A capability with the level set to `"must"` **MUST** be supported or the implementation in question is not a certified Firebolt implementation. + +A capability with the level set to `"should"` **SHOULD** be supported, especially if the implementation supports similar requirements in a non-Firebolt interface. + +A capability with the level set to `"could"` **COULD** be supported at the implementations discretion. + +#### 3.1.1. Should vs Could +This section is non-normative and does not denote any requiements. + +"Should" means that not supporting a capability has a detremental impact on a Firebolt platform, and care should be taken when deciding not to support a `"should"` capability. + +For example, a capability supporting HDMI outputs would likely be assigned a level of `"should"`, since most STB devices have HDMI outputs, while most TV devices do not. + +If a capability has a level of `"should"` and the device exposes the functionality in question through hardware or software, then it should either be treated as a `"must"` or a really good reason should exist for why it was not implemented. + +If a device has HDMI output ports, then it probably should implement this capability. While a device w/out HDMI output ports does not need to. + +"Could" means that this API is truly optional, for example a capability to expose the postal code of the device might be assigned a `level` of `"could"` since Firebolt platforms can justifiably opt not to support this. + +#### 3.1.2. Supported Capability +A supported capability **MUST** have all of its `x-uses` APIs surfaced, supported, and passing Firebolt Compliance tests. + +A supported capability **MUST** have all of its `x-provides` APIs surfaced, supported, and passing Firebolt Compliance tests. + +A supported capability **MUST** either: + +> have all of its `x-manages` APIs surfaced, supported, and passing Firebolt Compliance tests. +> +> **OR** +> +> have a Firebolt Certification translation layer that translates all of its `x-manages` APIs into local, non-firebolt APIs so that certification can run. diff --git a/src/js/github.io/index.mjs b/src/js/github.io/index.mjs index 7681a9d7f..010fc49f7 100644 --- a/src/js/github.io/index.mjs +++ b/src/js/github.io/index.mjs @@ -116,32 +116,36 @@ const capabilities = () => { } }) - let manifest = '\n' + Object.entries(specification.capabilities).forEach( ([c, v]) => { + capabilities[c].level = v.level + }) + + let manifest = '\n## Capailities\n| Capability | Level | Uses | Provides | Manages |\n|-|-|-|-|-|\n' const linkify = (method) => `[${method}](./${corerpc.methods.find(m => m.name === method) ? 'core' : 'manage'}/${method.split('.').shift()}/#${method.match(/\.on[A-Z]/) ? method.split('.').pop().charAt(2).toLowerCase() + method.split('.').pop().substring(3).toLowerCase() : method.split('.').pop().toLowerCase()})` Object.keys(capabilities).sort().forEach(c => { - manifest += `### \`${c}\`\n` + const use = capabilities[c].uses.length ? `
${capabilities[c].uses.length}${capabilities[c].uses.map(linkify).join('
')}
` : '' + const man = capabilities[c].manages.length ? `
${capabilities[c].manages.length}${capabilities[c].manages.map(linkify).join('
')}
` : '' + const pro = capabilities[c].provides.length ? `
${capabilities[c].provides.length}${capabilities[c].provides.map(linkify).join('
')}
` : '' - if (capabilities[c].uses.length) { - manifest += '\n| Uses |\n' - manifest += '| ---- |\n' - manifest += `| ${capabilities[c].uses.map(linkify).join('
')} |\n` - manifest += '\n\n' - } + manifest += `| \`${c}\` | **${capabilities[c].level.toUpperCase()}** | ${use} | ${pro} | ${man} |\n` + }) - if (capabilities[c].manages.length) { - manifest += '\n| Manages |\n' - manifest += '| ------- |\n' - manifest += `| ${capabilities[c].manages.map(linkify).join('
')} |\n` - manifest += '\n\n' - } - if (capabilities[c].provides.length) { - manifest += '\n| Provides |\n' - manifest += '| -------- |\n' - manifest += `| ${capabilities[c].provides.map(linkify).join('
')} |\n` - manifest += '\n\n' - } + manifest += '\n## Methods\nCapability prefix `xrn:firebolt:capability` let off for readability.\n| Method | Level | Uses | Provides | Manages |\n|-|-|-|-|-|\n' + + openrpc.methods.forEach(method => { + let uses = Object.entries(capabilities).filter( ([c, v]) => v.uses.includes(method.name)).map(([c, v]) => c) + let mans = Object.entries(capabilities).filter( ([c, v]) => v.manages.includes(method.name)).map(([c, v]) => c) + let pros = Object.entries(capabilities).filter( ([c, v]) => v.provides.includes(method.name)).map(([c, v]) => c) + let level = Array.from(new Set(uses.concat(mans).concat(pros).map(c => capabilities[c].level))).join(', ') + + uses = uses.map(c => c.split(':').slice(3, 5).join(':')).map(c => `\`${c}\``).join(', ') + pros = pros.map(c => c.split(':').slice(3, 5).join(':')).map(c => `\`${c}\``).join(', ') + mans = mans.map(c => c.split(':').slice(3, 5).join(':')).map(c => `\`${c}\``).join(', ') + level = level.includes('must') ? 'must' : level.includes('should') ? 'should' : 'could' + + manifest += `| ${linkify(method.name)} | **${level.toUpperCase()}** | ${uses} | ${pros} | ${mans} |\n` }) diff --git a/src/js/version-specification/capabilities.mjs b/src/js/version-specification/capabilities.mjs index bd5120bdb..ec23a1825 100644 --- a/src/js/version-specification/capabilities.mjs +++ b/src/js/version-specification/capabilities.mjs @@ -49,7 +49,7 @@ const doImport = (source, target, clear=false, report=false) => { logSuccess(`${report ? 'Missing' : 'Adding'} capability ${capability}.`) } result.capabilities[capability] = Object.assign({ - level: 'must', + level: 'could', use: { public: uses.includes(capability), negotiable: true && uses.includes(capability) diff --git a/src/json/firebolt-specification.json b/src/json/firebolt-specification.json index 13f662b93..a6f9f717e 100644 --- a/src/json/firebolt-specification.json +++ b/src/json/firebolt-specification.json @@ -1,164 +1,213 @@ { - "schemaVersion": { - "major": 1, - "minor": 0, - "patch": 0, - "readable": "Firebolt Version Manifest v1.0.0" - }, - "capabilities": { - "xrn:firebolt:capability:lifecycle:initialize": { - "level": "must", - "use": { - "public": true, - "negotiable": false - }, - "manage": { - "public": false, - "negotiable": false - }, - "provide": { - "public": false, - "negotiable": false - } - }, - "xrn:firebolt:capability:lifecycle:ready": { - "level": "must", - "use": { - "public": true, - "negotiable": false - }, - "manage": { - "public": false, - "negotiable": false - }, - "provide": { - "public": false, - "negotiable": false - } - }, - "xrn:firebolt:capability:lifecycle:state": { - "level": "must", - "use": { - "public": true, - "negotiable": false - }, - "manage": { - "public": true, - "negotiable": true - }, - "provide": { - "public": true, - "negotiable": true - } - }, - "xrn:firebolt:capability:advertising:configuration": { - "level": "could" - }, - "xrn:firebolt:capability:account:id": { - "level": "could" - }, - "xrn:firebolt:capability:advertising:identifier": { - "level": "could" - }, - "xrn:firebolt:capability:discovery:sign-in-status": { - "level": "could" - }, - "xrn:firebolt:capability:discovery:watch-next": { - "level": "could" - }, - "xrn:firebolt:capability:discovery:watched": { - "level": "could" - }, - "xrn:firebolt:capability:input:keyboard": { - "level": "should" - }, - "xrn:firebolt:capability:grants:state": { - "level": "should" - }, - "xrn:firebolt:capability:localization:locality": { - "level": "could" - }, - "xrn:firebolt:capability:localization:location": { - "level": "could" - }, - "xrn:firebolt:capability:localization:postal-code": { - "level": "could" - }, - "xrn:firebolt:capability:localization:time-zone": { - "level": "could" - }, - "xrn:firebolt:capability:protocol:dial": { - "level": "could" - }, - "xrn:firebolt:capability:protocol:wifi": { - "level": "could" - }, - "xrn:firebolt:capability:privacy:settings": { - "level": "could" - }, - "xrn:firebolt:capability:token:account": { - "level": "should" - }, - "xrn:firebolt:capability:token:device": { - "level": "should" - }, - "xrn:firebolt:capability:token:platform": { - "level": "should" - }, - "xrn:firebolt:capability:token:root": { - "level": "should" - }, - "xrn:firebolt:capability:token:session": { - "level": "should" - }, - "xrn:firebolt:capability:usergrant:acknowledgechallenge": { - "level": "should" - }, - "xrn:firebolt:capability:usergrant:pinchallenge": { - "level": "should" - }, - "xrn:firebolt:capability:capabilities:info": { - "level": "must", - "use": { - "public": true, - "negotiable": false - }, - "manage": { - "public": false, - "negotiable": false - }, - "provide": { - "public": false, - "negotiable": false - } - }, - "xrn:firebolt:capability:capabilities:request": { - "level": "must", - "use": { - "public": true, - "negotiable": false - }, - "manage": { - "public": false, - "negotiable": false - }, - "provide": { - "public": false, - "negotiable": false - } - }, - "xrn:firebolt:capability:privacy:advertising": { - "level": "could" - }, - "xrn:firebolt:capability:metrics:distributor": { - "level": "could" - }, - "xrn:firebolt:capability:storage:secure": { - "level": "could" - }, - "xrn:firebolt:capability:account:uid": { - "level": "should" - } - }, - "apis": {} + "schemaVersion": { + "major": 1, + "minor": 0, + "patch": 0, + "readable": "Firebolt Version Manifest v1.0.0" + }, + "capabilities": { + "xrn:firebolt:capability:accessibility:audiodescriptions": { + "level": "must" + }, + "xrn:firebolt:capability:accessibility:closedcaptions": { + "level": "must" + }, + "xrn:firebolt:capability:accessibility:voiceguidance": { + "level": "must" + }, + "xrn:firebolt:capability:account:id": { + "level": "could" + }, + "xrn:firebolt:capability:account:uid": { + "level": "could" + }, + "xrn:firebolt:capability:advertising:configuration": { + "level": "could" + }, + "xrn:firebolt:capability:advertising:identifier": { + "level": "should" + }, + "xrn:firebolt:capability:approve:content": { + "level": "should" + }, + "xrn:firebolt:capability:approve:purchase": { + "level": "could" + }, + "xrn:firebolt:capability:capabilities:info": { + "level": "must", + "use": { + "public": true, + "negotiable": false + } + }, + "xrn:firebolt:capability:capabilities:request": { + "level": "must", + "use": { + "public": true, + "negotiable": false + } + }, + "xrn:firebolt:capability:device:distributor": { + "level": "must" + }, + "xrn:firebolt:capability:device:id": { + "level": "could" + }, + "xrn:firebolt:capability:device:info": { + "level": "must" + }, + "xrn:firebolt:capability:device:make": { + "level": "must" + }, + "xrn:firebolt:capability:device:model": { + "level": "must" + }, + "xrn:firebolt:capability:device:name": { + "level": "should" + }, + "xrn:firebolt:capability:device:sku": { + "level": "could" + }, + "xrn:firebolt:capability:device:uid": { + "level": "could" + }, + "xrn:firebolt:capability:discovery:content-access": { + "level": "must" + }, + "xrn:firebolt:capability:discovery:entity-info": { + "level": "could" + }, + "xrn:firebolt:capability:discovery:interest": { + "level": "must" + }, + "xrn:firebolt:capability:discovery:navigate-to": { + "level": "must" + }, + "xrn:firebolt:capability:discovery:policy": { + "level": "must" + }, + "xrn:firebolt:capability:discovery:purchased-content": { + "level": "could" + }, + "xrn:firebolt:capability:discovery:sign-in-status": { + "level": "must" + }, + "xrn:firebolt:capability:discovery:watch-next": { + "level": "could" + }, + "xrn:firebolt:capability:discovery:watched": { + "level": "must" + }, + "xrn:firebolt:capability:grants:state": { + "level": "must" + }, + "xrn:firebolt:capability:input:keyboard": { + "level": "should" + }, + "xrn:firebolt:capability:inputs:hdmi": { + "level": "should" + }, + "xrn:firebolt:capability:lifecycle:initialize": { + "level": "must", + "use": { + "public": true, + "negotiable": false + } + }, + "xrn:firebolt:capability:lifecycle:launch": { + "level": "could" + }, + "xrn:firebolt:capability:lifecycle:ready": { + "level": "must", + "use": { + "public": true, + "negotiable": false + } + }, + "xrn:firebolt:capability:lifecycle:state": { + "level": "must", + "use": { + "public": true, + "negotiable": false + } + }, + "xrn:firebolt:capability:localization:additional-info": { + "level": "could" + }, + "xrn:firebolt:capability:localization:country-code": { + "level": "must" + }, + "xrn:firebolt:capability:localization:language": { + "level": "must" + }, + "xrn:firebolt:capability:localization:locale": { + "level": "must" + }, + "xrn:firebolt:capability:localization:locality": { + "level": "could" + }, + "xrn:firebolt:capability:localization:location": { + "level": "could" + }, + "xrn:firebolt:capability:localization:postal-code": { + "level": "could" + }, + "xrn:firebolt:capability:localization:time-zone": { + "level": "must" + }, + "xrn:firebolt:capability:metrics:distributor": { + "level": "could" + }, + "xrn:firebolt:capability:metrics:general": { + "level": "must" + }, + "xrn:firebolt:capability:metrics:media": { + "level": "must" + }, + "xrn:firebolt:capability:network:status": { + "level": "must" + }, + "xrn:firebolt:capability:privacy:advertising": { + "level": "should" + }, + "xrn:firebolt:capability:privacy:settings": { + "level": "could" + }, + "xrn:firebolt:capability:profile:flags": { + "level": "could" + }, + "xrn:firebolt:capability:protocol:dial": { + "level": "could" + }, + "xrn:firebolt:capability:protocol:wifi": { + "level": "could" + }, + "xrn:firebolt:capability:rpc:discover": { + "level": "should" + }, + "xrn:firebolt:capability:storage:secure": { + "level": "should" + }, + "xrn:firebolt:capability:token:account": { + "level": "could" + }, + "xrn:firebolt:capability:token:device": { + "level": "could" + }, + "xrn:firebolt:capability:token:platform": { + "level": "could" + }, + "xrn:firebolt:capability:token:root": { + "level": "could" + }, + "xrn:firebolt:capability:token:session": { + "level": "could" + }, + "xrn:firebolt:capability:usergrant:acknowledgechallenge": { + "level": "must" + }, + "xrn:firebolt:capability:usergrant:pinchallenge": { + "level": "should" + } + } } \ No newline at end of file