Skip to content

Commit

Permalink
feat: App Pass-through (#188)
Browse files Browse the repository at this point in the history
This feature enables one Firebolt App to provide a capability that may be used by another Firebolt App, with the platform as a permission broker that passes the requests and responses to each app without feature-specific logic.

This document covers the App Pass-through Firebolt OpenRPC extension as well as how Firebolt implementations should detect and execute app provided pass-through APIs.

Some APIs require an app to fulfill the request on behalf of another app, e.g. to provide a UX or cross-app data sharing. Generally the calling app doesn't care, or have a say in, which other app provides the API, that is up to the Firebolt distributor.

To facilitate these APIs, Firebolt denotes an `x-provided-by` OpenRPC tag with OpenRPC extensions to connect the `provide` API to the `use` API.
  • Loading branch information
jlacivita authored Jun 6, 2024
1 parent 04a005a commit 83fa0fc
Show file tree
Hide file tree
Showing 22 changed files with 566 additions and 102 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/merge-to-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,4 @@ jobs:

steps:
- name: Post error message (To-Do)
run: echo "Next cannot be merged into main cleanly"
run: echo "Next cannot be merged into main cleanly"
2 changes: 1 addition & 1 deletion .github/workflows/merge-to-next-major.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ jobs:

steps:
- name: Post error message (To-Do)
run: echo "Next cannot be merged into next-major cleanly"
run: echo "Next cannot be merged into next-major cleanly"
3 changes: 3 additions & 0 deletions languages/markdown/templates/codeblocks/interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
interface ${name} {
${methods}
}
1 change: 1 addition & 0 deletions languages/markdown/templates/declarations/default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
function ${method.name}(${method.params}): Promise<${method.result.type}>
1 change: 1 addition & 0 deletions languages/markdown/templates/interfaces/default.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
${method.name}(${method.signature.params}, session: ProviderSession): Promise<${method.result.type}>
1 change: 1 addition & 0 deletions languages/markdown/templates/interfaces/focusable.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
${method.name}(${method.signature.params}, session: FocusableProviderSession): Promise<${method.result.type}>
3 changes: 0 additions & 3 deletions languages/markdown/templates/types/default.md

This file was deleted.

5 changes: 0 additions & 5 deletions languages/markdown/templates/types/enum.md

This file was deleted.

3 changes: 0 additions & 3 deletions languages/markdown/templates/types/object.md

This file was deleted.

70 changes: 33 additions & 37 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
],
"license": "Apache-2.0",
"dependencies": {
"ajv": "^8.3.0",
"ajv-formats": "^2.1.0",
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"array.prototype.groupby": "^1.1.0",
"crocks": "^0.12.4",
"deepmerge": "^4.2.2",
Expand Down
3 changes: 2 additions & 1 deletion src/cli.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ const knownOpts = {
'static-module': [String, Array],
'language': [path],
'examples': [path, Array],
'as-path': [Boolean]
'as-path': [Boolean],
'pass-throughs': [Boolean]
}

const shortHands = {
Expand Down
1 change: 1 addition & 0 deletions src/docs/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const run = async ({
examples: examples,
templatesPerModule: config.templatesPerModule,
templatesPerSchema: config.templatesPerSchema,
operators: config.operators,
libraryName: libraryName,
hidePrivate: false
})
Expand Down
37 changes: 37 additions & 0 deletions src/firebolt-openrpc.json
Original file line number Diff line number Diff line change
Expand Up @@ -1007,8 +1007,45 @@
},
"x-error-for": {
"type": "string"
},
"x-provided-by": {
"type": "string"
}
},
"if": {
"required": [
"x-provided-by"
]
},
"then": {
"not": {
"required": [
"x-provides"
]
},
"oneOf": [
{
"required": [ "x-manages"],
"properties": {
"x-manages": {
"type": "array",
"minItems": 1,
"maxItems": 1
}
}
},
{
"required": [ "x-uses"],
"properties": {
"x-uses": {
"type": "array",
"minItems": 1,
"maxItems": 1
}
}
}
]
},
"anyOf": [
{
"required": [ "x-uses"]
Expand Down
39 changes: 30 additions & 9 deletions src/macrofier/engine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,7 @@ const generateMacros = (obj, templates, languages, options = {}) => {
const eventsEnum = generateEvents(obj, templates)

const examples = generateExamples(obj, templates, languages)
const allMethodsArray = generateMethods(obj, examples, templates, options.type)
const allMethodsArray = generateMethods(obj, examples, templates, languages, options.type)

Array.from(new Set(['methods'].concat(config.additionalMethodTemplates))).filter(dir => dir).forEach(dir => {

Expand Down Expand Up @@ -879,7 +879,7 @@ function generateSchemas(json, templates, options) {
const schemas = JSON.parse(JSON.stringify(json.definitions || (json.components && json.components.schemas) || {}))

const generate = (name, schema, uri, { prefix = '' } = {}) => {
// these are internal schemas used by the firebolt-openrpc tooling, and not meant to be used in code/doc generation
// these are internal schemas used by the fireboltize-openrpc tooling, and not meant to be used in code/doc generation
if (['ListenResponse', 'ProviderRequest', 'ProviderResponse', 'FederatedResponse', 'FederatedRequest'].includes(name)) {
return
}
Expand Down Expand Up @@ -1145,7 +1145,7 @@ function generateMethodResult(type, templates) {
return result
}

function generateMethods(json = {}, examples = {}, templates = {}, type = '') {
function generateMethods(json = {}, examples = {}, templates = {}, languages = [], type = '') {
const methods = compose(
option([]),
getMethods
Expand Down Expand Up @@ -1174,7 +1174,7 @@ function generateMethods(json = {}, examples = {}, templates = {}, type = '') {
else if (dir.includes('methods') && (suffix && config.templateExtensionMap[dir] ? config.templateExtensionMap[dir].includes(suffix) : true)) {
const template = getTemplateForMethod(methodObj, templates, dir)
if (template && template.length) {
result.body[dir] = insertMethodMacros(template, methodObj, json, templates, type, examples)
result.body[dir] = insertMethodMacros(template, methodObj, json, templates, type, examples, languages)
}
}
})
Expand All @@ -1201,7 +1201,7 @@ function generateMethods(json = {}, examples = {}, templates = {}, type = '') {
}

// TODO: this is called too many places... let's reduce that to just generateMethods
function insertMethodMacros(template, methodObj, json, templates, type = '', examples = {}) {
function insertMethodMacros(template, methodObj, json, templates, type = '', examples = {}, languages = {}) {
const moduleName = getModuleName(json)

const info = {
Expand Down Expand Up @@ -1271,7 +1271,7 @@ function insertMethodMacros(template, methodObj, json, templates, type = '', exa
const subscriber = json.methods.find(method => method.tags.find(tag => tag['x-alternative'] === methodObj.name))
const subscriberTemplate = (subscriber ? insertMethodMacros(getTemplate('/codeblocks/subscriber', templates), subscriber, json, templates, type, examples) : '')
const setterFor = methodObj.tags.find(t => t.name === 'setter') && methodObj.tags.find(t => t.name === 'setter')['x-setter-for'] || ''
const pullsResult = (puller || pullsFor) ? localizeDependencies(pullsFor || methodObj, json).params[1].schema : null
const pullsResult = (puller || pullsFor) ? localizeDependencies(pullsFor || methodObj, json).params.findLast(x=>true).schema : null
const pullsParams = (puller || pullsFor) ? localizeDependencies(getPayloadFromEvent(puller || methodObj), json, null, { mergeAllOfs: true }).properties.parameters : null

const pullsResultType = (pullsResult && (type === 'methods')) ? types.getSchemaShape(pullsResult, json, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section }) : ''
Expand Down Expand Up @@ -1337,6 +1337,23 @@ function insertMethodMacros(template, methodObj, json, templates, type = '', exa
itemType = types.getSchemaType(result.schema.items, json, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section })
}

let signature

if (Object.keys(languages).length && template.indexOf('${method.signature}') >= 0) {
const lang = languages[Object.keys(languages)[0]]
signature = getTemplateForDeclaration(methodObj, templates, 'declarations')
types.setTemplates(lang)
const currentConfig = JSON.parse(JSON.stringify(config))
config.operators = config.operators || {}
config.operators.paramDelimiter = ', '
signature = insertMethodMacros(signature, methodObj, json, lang, type)
config = currentConfig
types.setTemplates(templates)
}
else {
signature = ''
}

template = insertExampleMacros(template, examples[methodObj.name] || [], methodObj, json, templates)
template = template.replace(/\$\{method\.name\}/g, method.name)
.replace(/\$\{method\.rpc\.name\}/g, methodObj.rpc_name || methodObj.name)
Expand All @@ -1362,6 +1379,7 @@ function insertMethodMacros(template, methodObj, json, templates, type = '', exa
.replace(/\$\{method\.params\.serialization\}/g, serializedParams)
.replace(/\$\{method\.params\.serialization\.with\.indent\}/g, indent(serializedParams, ' '))
// Typed signature stuff
.replace(/\$\{method\.signature\}/g, signature)
.replace(/\$\{method\.signature\.params\}/g, types.getMethodSignatureParams(methodObj, json, { destination: state.destination, section: state.section }))
.replace(/\$\{method\.signature\.result\}/g, types.getMethodSignatureResult(methodObj, json, { destination: state.destination, section: state.section }))
.replace(/\$\{method\.context\}/g, method.context.join(', '))
Expand Down Expand Up @@ -1650,7 +1668,7 @@ function insertParameterMacros(template, param, method, module) {
constraints = '<br/>' + constraints
}

return template
template = template
.replace(/\$\{method.param.name\}/g, param.name)
.replace(/\$\{method.param.Name\}/g, param.name[0].toUpperCase() + param.name.substring(1))
.replace(/\$\{method.param.summary\}/g, param.summary || '')
Expand All @@ -1659,7 +1677,10 @@ function insertParameterMacros(template, param, method, module) {
.replace(/\$\{json.param.type\}/g, jsonType)
.replace(/\$\{method.param.link\}/g, getLinkForSchema(param.schema, module)) //getType(param))
.replace(/\$\{method.param.constraints\}/g, constraints) //getType(param))
}

return template

}

function insertCapabilityMacros(template, capabilities, method, module) {
const content = []
Expand Down Expand Up @@ -1730,7 +1751,7 @@ function generateProviderInterfaces(json, templates) {
}

function getProviderInterfaceName(iface, capability, moduleJson = {}) {
const uglyName = capability.split(":").slice(-2).map(capitalize).reverse().join('') + "Provider"
const uglyName = capability.split(':').slice(-2).map(capitalize).map(x => x.split('-').map(capitalize).join('')).reverse().join('') + "Provider"
let name = iface.length === 1 ? iface[0].name.charAt(0).toUpperCase() + iface[0].name.substr(1) + "Provider" : uglyName

if (moduleJson.info['x-interface-names']) {
Expand Down
Loading

0 comments on commit 83fa0fc

Please sign in to comment.