diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a772877..4ea86268 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +# [1.7.0-next.3](https://github.com/rdkcentral/firebolt-openrpc/compare/v1.7.0-next.2...v1.7.0-next.3) (2022-10-31) + + +### Bug Fixes + +* Only generate docs for used schema files ([ff08978](https://github.com/rdkcentral/firebolt-openrpc/commit/ff08978477ef8eb048c9f4c9d9a96e2fe66fc868)) + +# [1.7.0-next.2](https://github.com/rdkcentral/firebolt-openrpc/compare/v1.7.0-next.1...v1.7.0-next.2) (2022-10-31) + + +### Bug Fixes + +* Interfaceless providers ([#68](https://github.com/rdkcentral/firebolt-openrpc/issues/68)) ([ceb3040](https://github.com/rdkcentral/firebolt-openrpc/commit/ceb304018c2e0eb7cf5ac6f9bcd5b44bab0cb083)) + # [1.7.0-next.1](https://github.com/rdkcentral/firebolt-openrpc/compare/v1.6.2-next.1...v1.7.0-next.1) (2022-10-24) diff --git a/package-lock.json b/package-lock.json index 68351224..2b385957 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@firebolt-js/openrpc", - "version": "1.7.0-next.1", + "version": "1.7.0-next.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@firebolt-js/openrpc", - "version": "1.7.0-next.1", + "version": "1.7.0-next.3", "license": "Apache-2.0", "dependencies": { "ajv": "^8.3.0", diff --git a/package.json b/package.json index a786d8d1..162c8628 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@firebolt-js/openrpc", - "version": "1.7.0-next.1", + "version": "1.7.0-next.3", "description": "The Firebolt SDK Code & Doc Generator", "main": "src/js/sdk/index.mjs", "type": "module", @@ -36,7 +36,7 @@ "@semantic-release/changelog": "^6.0.1", "@semantic-release/git": "^10.0.1", "@semantic-release/npm": "^9.0.1", - "husky": "^8.0.0", + "husky": "^8.0.1", "jest": "^27.3.1", "semantic-release": "^19.0.5" }, diff --git a/src/js/shared/Events/index.mjs b/src/js/shared/Events/index.mjs index a0ad596f..bfb5010f 100644 --- a/src/js/shared/Events/index.mjs +++ b/src/js/shared/Events/index.mjs @@ -229,7 +229,6 @@ export const prioritize = function(...args) { } const unsubscribe = (key) => { - console.log(key) const [module, event] = key.split('.').slice(0, 2) Transport.send(module, 'on' + event[0].toUpperCase() + event.substr(1), { listen: false }) } diff --git a/src/modules/advanced.json b/src/modules/advanced.json index 784a8eeb..e2ca576e 100644 --- a/src/modules/advanced.json +++ b/src/modules/advanced.json @@ -11,6 +11,10 @@ "tags": [ { "name": "event" + }, + { + "name": "capabilities", + "x-uses": ["xrn:firebolt:capability:test:test"] } ], "summary": "A temporal set method that lists Advanced objects.", @@ -61,7 +65,9 @@ }, { "name": "capabilities", - "x-uses": "xrn:firebolt:capability:test:test" + "x-uses": [ + "xrn:firebolt:capability:test:test" + ] } ], "params": [ @@ -100,6 +106,10 @@ "tags": [ { "name": "temporal-set" + }, + { + "name": "capabilities", + "x-uses": ["xrn:firebolt:capability:test:test"] } ], "summary": "A temporal set method that lists Advanced objects.", @@ -136,6 +146,12 @@ { "name": "action", "summary": "A method that takes an Advanced object.", + "tags": [ + { + "name": "capabilities", + "x-uses": ["xrn:firebolt:capability:test:test"] + } + ], "description": "A method for testing advanced method generation.", "params": [ { diff --git a/src/modules/simple.json b/src/modules/simple.json index e79ceca1..a7c52223 100644 --- a/src/modules/simple.json +++ b/src/modules/simple.json @@ -9,6 +9,12 @@ { "name": "method", "summary": "A method.", + "tags": [ + { + "name": "capabilities", + "x-uses": ["xrn:firebolt:capability:test:test"] + } + ], "description": "A method for testing basic method generation.", "params": [ { @@ -68,7 +74,9 @@ }, { "name": "capabilities", - "x-uses": "xrn:firebolt:capability:test:test" + "x-uses": [ + "xrn:firebolt:capability:test:test" + ] } ], "description": "A property for testing basic property generation.", @@ -119,6 +127,12 @@ "description": { "$ref": "file:../descriptions/modules/Simple/methodWithMarkdownDescription.md" }, + "tags": [ + { + "name": "capabilities", + "x-uses": ["xrn:firebolt:capability:test:test"] + } + ], "params": [ { "name": "parameter", @@ -172,6 +186,12 @@ "name": "methodWithSchema", "summary": "A method using a schema.", "description": "A method for testing schema-dependent method generation.", + "tags": [ + { + "name": "capabilities", + "x-uses": ["xrn:firebolt:capability:test:test"] + } + ], "params": [ { "name": "title", @@ -211,6 +231,12 @@ "name": "methodWithMethodAttribute", "summary": "A method using a method-attribute transform.", "description": "A method for testing sub-method generation.", + "tags": [ + { + "name": "capabilities", + "x-uses": ["xrn:firebolt:capability:test:test"] + } + ], "params": [ { "name": "title", diff --git a/src/schemas/firebolt-openrpc.json b/src/schemas/firebolt-openrpc.json index 2bf584c7..db953347 100644 --- a/src/schemas/firebolt-openrpc.json +++ b/src/schemas/firebolt-openrpc.json @@ -35,12 +35,22 @@ "Method": { "type": "object", "required": [ - "examples" + "examples", + "tags" ], "properties": { "examples": { "type": "array", "minItems": 1 + }, + "tags": { + "type": "array", + "items": { + "type": "object" + }, + "contains": { + "$ref": "#/definitions/CapabilitiesTag" + } } }, "allOf": [ @@ -78,8 +88,12 @@ }, { "if": { - "required": [ "tags"], + "required": [ "tags", "name"], "properties": { + "name": { + "type": "string", + "pattern": "^onRequest" + }, "tags": { "type": "array", "contains": { @@ -853,8 +867,28 @@ }, "x-allow-focus": { "type": "boolean" + }, + "x-response-for": { + "type": "string" + }, + "x-allow-focus-for": { + "type": "string" + }, + "x-error-for": { + "type": "string" } - } + }, + "anyOf": [ + { + "required": [ "x-uses"] + }, + { + "required": [ "x-manages"] + }, + { + "required": [ "x-provides"] + } + ] } ] }, diff --git a/util/docs/index.mjs b/util/docs/index.mjs index d6941e13..5ef242f4 100644 --- a/util/docs/index.mjs +++ b/util/docs/index.mjs @@ -56,9 +56,10 @@ const run = ({ // All the streams we care about. const combinedTemplates = combineStreamObjects(loadFilesIntoObject(sharedTemplateFolder, '.md', '/template/markdown/'), loadFilesIntoObject(templateFolder, '.md', '/template/markdown/')) const combinedSchemas = combineStreamObjects(schemaFetcher(sharedSchemasFolder), schemaFetcher(schemasFolder)) - + const usedSchemasFilter = (schema, modules) => Object.values(modules).find(module => JSON.stringify(module).match(new RegExp("\"" + schema.$id + "(#[^\"]+)?\""))) // filter out schemas nobody references... + const generateDocs = templates => modules => schemas => version => h(Object.entries(modules)) - .concat(Object.entries(schemas)) + .concat(Object.entries(schemas).filter(([_, schema]) => usedSchemasFilter(schema, modules))) .flatMap(([_, module]) => { const documentOptions = { asPath: asPath, diff --git a/util/docs/macros/index.mjs b/util/docs/macros/index.mjs index 4efca0b3..0ad95a45 100644 --- a/util/docs/macros/index.mjs +++ b/util/docs/macros/index.mjs @@ -27,7 +27,7 @@ import pointfree from 'crocks/pointfree/index.js' const { filter, option, map } = pointfree import isArray from 'crocks/predicates/isArray.js' import safe from 'crocks/Maybe/safe.js' -import { getProvidedCapabilities, isRPCOnlyMethod, isTemporalSetMethod } from '../../shared/modules.mjs' +import { getProvidedCapabilities, isRPCOnlyMethod, isTemporalSetMethod, isProviderInterfaceMethod } from '../../shared/modules.mjs' var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf8')); @@ -62,7 +62,6 @@ const hasNonEmptyEventAttribute = (method, attribute) => { const isEvent = method => hasTag(method, 'event') const isProviderFocusMethod = method => method.tags && method.tags.find(t => t['x-allow-focus-for']) const isProviderResponseMethod = method => method.tags && method.tags.find(t => t['x-response-for']) -const isProviderMethod = method => isEvent(method) && hasNonEmptyEventAttribute(method, 'x-provides') const isFullyDocumentedEvent = method => isEvent(method) && !hasTag(method, 'rpc-only') && !hasEventAttribute(method, 'x-alternative') && !hasEventAttribute(method, 'x-pulls-for') const isSetter = method => method.tags && method.tags.find(t => t['x-setter-for']) const isTocMethod = method => !isEvent(method) && !isProviderFocusMethod(method) && !isProviderResponseMethod(method) @@ -88,7 +87,7 @@ function insertMacros(data = '', moduleJson = {}, templates = {}, schemas = {}, const additionalEvents = moduleJson.methods && moduleJson.methods.filter( method => isEvent(method) && !isFullyDocumentedEvent(method)) const additionalMethods = moduleJson.methods && moduleJson.methods.filter( method => !isEvent(method) && isRPCOnlyMethod(method) ) const hasEvents = (additionalEvents && additionalEvents.length > 0) || (events && events.length > 0) //(methods && methods.find(method => isEvent(method) && !isProviderMethod(method))) - const providerMethods = moduleJson.methods && moduleJson.methods.filter( method => isProviderMethod(method)) + const providerMethods = moduleJson.methods && moduleJson.methods.filter( method => isProviderInterfaceMethod(method)) const hasProviderMethods = (providerMethods && providerMethods.length > 0) const capabilities = moduleJson.methods && getProvidedCapabilities(moduleJson) @@ -300,7 +299,6 @@ function insertMacros(data = '', moduleJson = {}, templates = {}, schemas = {}, if (methods) { data = data .replace(/\$\{toc.methods\}/g, methods.filter(isTocMethod).map(m => ' - [' + m.name + '](#' + m.name.toLowerCase() + ')').join('\n')) - // .replace(/\$\{toc.events\}/g, methods.filter(m => isEvent(m) && !isProviderMethod(m)).map(m => ' - [' + m.name[2].toLowerCase() + m.name.substr(3) + '](#' + m.name.substr(2).toLowerCase() + ')').join('\n')) .replace(/\$\{toc.providers\}/g, capabilities.map(c => ` - [${getProviderName(c, moduleJson, schemas)}](#${getProviderName(c, moduleJson, schemas).toLowerCase()})`).join('\n')) } @@ -985,7 +983,7 @@ function getExternalSchemaLinks(json = {}, schemas = {}, options = {}) { function generateJavaScriptExample(example, m, moduleJson = {}, templates = {}) { if (m.name.match(/^on[A-Z]/)) { - if (isProviderMethod(m)) { + if (isProviderInterfaceMethod(m)) { return generateProviderExample(m, moduleJson, templates) } else { return generateEventExample(m, moduleJson) diff --git a/util/sdk/macros/index.mjs b/util/sdk/macros/index.mjs index 15d2c73b..62d65f63 100644 --- a/util/sdk/macros/index.mjs +++ b/util/sdk/macros/index.mjs @@ -30,7 +30,7 @@ import predicates from 'crocks/predicates/index.js' import isNil from 'crocks/core/isNil.js' const { isObject, isArray, propEq, pathSatisfies, propSatisfies } = predicates -import { isExcludedMethod, isRPCOnlyMethod, isProviderMethod, getPayloadFromEvent, providerHasNoParameters, isTemporalSetMethod, hasMethodAttributes, getMethodAttributes, isEventMethodWithContext } from '../../shared/modules.mjs' +import { isExcludedMethod, isRPCOnlyMethod, isProviderInterfaceMethod, getPayloadFromEvent, providerHasNoParameters, isTemporalSetMethod, hasMethodAttributes, getMethodAttributes, isEventMethodWithContext } from '../../shared/modules.mjs' import { getTemplateForMethod } from '../../shared/template.mjs' import { getMethodSignatureParams } from '../../shared/javascript.mjs' import isEmpty from 'crocks/core/isEmpty.js' @@ -164,7 +164,7 @@ const providedCapabilitiesOrEmptyArray = compose( option([]), map(caps => [... new Set(caps)]), map(map(m => m.tags.find(t => t['x-provides'])['x-provides'])), // grab the capabilty it provides - map(filter(isProviderMethod)), + map(filter(isProviderInterfaceMethod)), getMethods ) @@ -180,7 +180,7 @@ const providersOrEmptyArray = compose( } return e })), - map(filter(isProviderMethod)), + map(filter(isProviderInterfaceMethod)), getMethods ) @@ -469,7 +469,7 @@ function generateMethods(json = {}, templates = {}, onlyEvents = false) { option([]), map(filter(m => !onlyEvents || isEventMethod(m))), map(filter(not(isRPCOnlyMethod))), - map(filter(not(isProviderMethod))), + map(filter(not(isProviderInterfaceMethod))), map(filter(not(isExcludedMethod))), getMethods )(json) diff --git a/util/shared/helpers.mjs b/util/shared/helpers.mjs index 47732b35..da7006b6 100644 --- a/util/shared/helpers.mjs +++ b/util/shared/helpers.mjs @@ -55,6 +55,7 @@ const clearDirectory = dir => fsRemoveDirectory(dir, {recursive: true}) const isFile = dir => fsStat(dir).map(statObj => statObj.isFile()) const logSuccess = message => console.log(`\x1b[32m ✓ \x1b[0m\x1b[2m ${message}\x1b[0m`) +const logInfo = message => console.log(`\x1b[38;5;202m ⓘ \x1b[0m\x1b[2m ${message}\x1b[0m`) const logError = message => console.log(`\x1b[31m ✗ \x1b[0m\x1b[2m ${message}\x1b[0m`) const logHeader = message => console.log(`\x1b[0m\x1b[7m\x1b[32m${message}\x1b[0m\n`) @@ -277,6 +278,7 @@ export { fsWriteFile, fsReadFile, logSuccess, + logInfo, logError, logHeader, getFilename, diff --git a/util/shared/modules.mjs b/util/shared/modules.mjs index f22a0c99..6495c268 100644 --- a/util/shared/modules.mjs +++ b/util/shared/modules.mjs @@ -54,26 +54,33 @@ const getMethods = compose( getPath(['methods']) ) -const isProviderMethod = compose( - option(false), - map(_ => true), - chain( - find( - and( - propEq('name', 'capabilities'), - propSatisfies('x-provides', not(isEmpty)) +const isProviderInterfaceMethod = compose( + and( + compose( + propSatisfies('name', name => name.startsWith('onRequest')) + ), + compose( + option(false), + map(_ => true), + chain( + find( + and( + propEq('name', 'capabilities'), + propSatisfies('x-provides', not(isEmpty)) + ) + ) + ), + getPath(['tags']) ) - ) - ), - getPath(['tags']) + ) ) const getProvidedCapabilities = (json) => { - return Array.from(new Set([...getMethods(json).filter(isProviderMethod).map(method => method.tags.find(tag => tag['x-provides'])['x-provides'])])) + return Array.from(new Set([...getMethods(json).filter(isProviderInterfaceMethod).map(method => method.tags.find(tag => tag['x-provides'])['x-provides'])])) } -const getMethodsThatProvide = (capability, json) => { - return getMethods(json).filter(method => method.tags && method.tags.find(tag => tag['x-provides'] === capability)) +const getProviderInterfaceMethods = (capability, json) => { + return getMethods(json).filter(method => method.name.startsWith("onRequest") && method.tags && method.tags.find(tag => tag['x-provides'] === capability)) } @@ -472,13 +479,8 @@ const createFocusFromProvider = provider => { const ready = JSON.parse(JSON.stringify(provider)) ready.name = ready.name.charAt(9).toLowerCase() + ready.name.substr(10) + 'Focus' ready.summary = `Internal API for ${provider.name.substr(9)} Provider to request focus for UX purposes.` - const old_tags = ready.tags - ready.tags = [ - { - 'name': 'rpc-only', - 'x-allow-focus-for': provider.name - } - ] + ready.tags = ready.tags.filter(t => t.name !== 'event') + ready.tags.find(t => t.name === 'capabilities')['x-allow-focus-for'] = provider.name ready.params = [] ready.result = { @@ -512,13 +514,9 @@ const createResponseFromProvider = (provider, type, json) => { const response = JSON.parse(JSON.stringify(provider)) response.name = response.name.charAt(9).toLowerCase() + response.name.substr(10) + type response.summary = `Internal API for ${provider.name.substr(9)} Provider to send back ${type.toLowerCase()}.` - const old_tags = response.tags - response.tags = [ - { - 'name': 'rpc-only' - } - ] - response.tags[`x-${type.toLowerCase()}-for`] = provider.name + + response.tags = response.tags.filter(t => t.name !== 'event') + response.tags.find(t => t.name === 'capabilities')[`x-${type.toLowerCase()}-for`] = provider.name const paramExamples = [] @@ -687,7 +685,7 @@ const generateTemporalSetMethods = json => { const generateProviderMethods = json => { - const providers = json.methods.filter( m => m.tags && m.tags.find( t => t.name == 'capabilities' && t['x-provides'])) || [] + const providers = json.methods.filter( m => m.name.startsWith('onRequest') && m.tags && m.tags.find( t => t.name == 'capabilities' && t['x-provides'])) || [] providers.forEach(provider => { if (! isRPCOnlyMethod(provider)) { @@ -803,13 +801,13 @@ export { isTemporalSetMethod, isExcludedMethod, isRPCOnlyMethod, - isProviderMethod, + isProviderInterfaceMethod, hasExamples, hasTitle, hasMethodAttributes, getMethodAttributes, getMethods, - getMethodsThatProvide, + getProviderInterfaceMethods, getProvidedCapabilities, getEnums, getTypes, diff --git a/util/shared/typescript.mjs b/util/shared/typescript.mjs index 9e585459..2f61b6fd 100644 --- a/util/shared/typescript.mjs +++ b/util/shared/typescript.mjs @@ -20,7 +20,7 @@ import { getPath, getSchema } from './json-schema.mjs' import deepmerge from 'deepmerge' import { localizeDependencies } from './json-schema.mjs' import { getLinkFromRef } from './helpers.mjs' -import { getMethodsThatProvide, getPayloadFromEvent } from './modules.mjs' +import { getProviderInterfaceMethods, getPayloadFromEvent } from './modules.mjs' const isSynchronous = m => !m.tags ? false : m.tags.map(t => t.name).find(s => s === 'synchronous') @@ -43,6 +43,10 @@ function getProviderName(capability, moduleJson, schemas) { return '' } + const prettyName = (moduleJson.info['x-interface-names'] || {})[capability] + + if (prettyName) return prettyName + const capitalize = str => str[0].toUpperCase() + str.substr(1) const uglyName = capability.split(":").slice(-2).map(capitalize).reverse().join('') + "Provider" @@ -85,7 +89,7 @@ interface FocusableProviderSession extends ProviderSession { function getProviderInterface(module, capability, schemas = {}) { module = JSON.parse(JSON.stringify(module)) - const iface = getMethodsThatProvide(capability, module).map(method => localizeDependencies(method, module, schemas, { mergeAllOfs: true })) + const iface = getProviderInterfaceMethods(capability, module).map(method => localizeDependencies(method, module, schemas, { mergeAllOfs: true })) iface.forEach(method => { const payload = getPayloadFromEvent(method, module, schemas) diff --git a/util/validate/validation/index.mjs b/util/validate/validation/index.mjs index 7a34b911..29644338 100644 --- a/util/validate/validation/index.mjs +++ b/util/validate/validation/index.mjs @@ -40,6 +40,7 @@ const addPrettyPath = (error, json) => { }) error.prettyPath = '/' + path.join('/') error.document = root + error.node = pointer return error } @@ -136,6 +137,7 @@ export const displayError = (error) => { // This is useful for debugging... please leave comment here for quick access :) // console.dir(error, {depth: 1000}) + // console.dir(error.node, {depth: 100}) console.error() }