diff --git a/languages/c/Types.mjs b/languages/c/Types.mjs index 1ee99224..4c092a92 100644 --- a/languages/c/Types.mjs +++ b/languages/c/Types.mjs @@ -358,6 +358,7 @@ function getSchemaTypeInfo(module = {}, json = {}, name = '', schemas = {}, pref structure.namespace = getModuleName(module) } } + return structure } diff --git a/languages/c/language.config.json b/languages/c/language.config.json index ee3604d7..b684e11b 100644 --- a/languages/c/language.config.json +++ b/languages/c/language.config.json @@ -12,5 +12,6 @@ "/src/module_common.cpp", "/src/jsondata_module.h" ], - "persistPermission": true + "persistPermission": true, + "createPolymorphicMethods": true } diff --git a/languages/c/templates/codeblocks/setter.c b/languages/c/templates/codeblocks/setter.c index 342a98d6..411d4542 100644 --- a/languages/c/templates/codeblocks/setter.c +++ b/languages/c/templates/codeblocks/setter.c @@ -1,7 +1,7 @@ -/* ${method.name} - ${method.description} */ +/* ${method.rpc.name} - ${method.description} */ uint32_t ${info.Title}_${method.Name}( ${method.signature.params} ) { - const string method = _T("${info.title}.${method.name}"); + const string method = _T("${info.title.lowercase}.${method.rpc.name}"); ${if.params}${method.params.serialization}${end.if.params} return FireboltSDK::Properties::Set(method, jsonParameters); } diff --git a/languages/c/templates/methods/default.c b/languages/c/templates/methods/default.c index 6fbca47e..881b592c 100644 --- a/languages/c/templates/methods/default.c +++ b/languages/c/templates/methods/default.c @@ -1,4 +1,4 @@ -/* ${method.name} - ${method.description} */ +/* ${method.rpc.name} - ${method.description} */ uint32_t ${info.Title}_${method.Name}( ${method.signature.params}${if.result}${if.params}, ${end.if.params}${method.result.type}* ${method.result.name}${end.if.result}${if.signature.empty}void${end.if.signature.empty} ) { uint32_t status = FireboltSDKErrorUnavailable; @@ -7,9 +7,9 @@ uint32_t ${info.Title}_${method.Name}( ${method.signature.params}${if.result}${i ${method.params.serialization.with.indent} ${method.result.json.type} jsonResult; - status = transport->Invoke("${info.title}.${method.name}", jsonParameters, jsonResult); + status = transport->Invoke("${info.title.lowercase}.${method.rpc.name}", jsonParameters, jsonResult); if (status == FireboltSDKErrorNone) { - FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully invoked"); + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.rpc.name} is successfully invoked"); ${method.result.instantiation} } diff --git a/languages/c/templates/methods/event.c b/languages/c/templates/methods/event.c index ec868cd5..c02e74c4 100644 --- a/languages/c/templates/methods/event.c +++ b/languages/c/templates/methods/event.c @@ -1,4 +1,4 @@ -/* ${method.name} - ${method.description} */ +/* ${method.rpc.name} - ${method.description} */ static void ${info.Title}${method.Name}InnerCallback( void* userCB, const void* userData, void* response ) { ${event.callback.params.serialization} @@ -11,7 +11,7 @@ static void ${info.Title}${method.Name}InnerCallback( void* userCB, const void* } uint32_t ${info.Title}_Register_${method.Name}( ${event.signature.params}${if.event.params}, ${end.if.event.params}${info.Title}${method.Name}Callback userCB, const void* userData ) { - const string eventName = _T("${info.title}.${method.name}"); + const string eventName = _T("${info.title.lowercase}.${method.rpc.name}"); uint32_t status = FireboltSDKErrorNone; if (userCB != nullptr) { @@ -22,5 +22,5 @@ uint32_t ${info.Title}_Register_${method.Name}( ${event.signature.params}${if.ev } uint32_t ${info.Title}_Unregister_${method.Name}( ${info.Title}${method.Name}Callback userCB) { - return FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title}.${method.name}"), reinterpret_cast(userCB)); + return FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title.lowercase}.${method.rpc.name}"), reinterpret_cast(userCB)); } diff --git a/languages/c/templates/methods/polymorphic-pull-event.c b/languages/c/templates/methods/polymorphic-pull-event.c index d2f5ef4c..7c833b71 100644 --- a/languages/c/templates/methods/polymorphic-pull-event.c +++ b/languages/c/templates/methods/polymorphic-pull-event.c @@ -1,4 +1,4 @@ -/* ${method.name} - ${method.description} */ +/* ${method.rpc.name} - ${method.description} */ static void ${info.Title}${method.Name}InnerCallback( void* userCB, const void* userData, void* response ) { ${event.callback.params.serialization} @@ -27,9 +27,9 @@ static void ${info.Title}${method.Name}InnerCallback( void* userCB, const void* FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); if (transport != nullptr) { WPEFramework::Core::JSON::Boolean jsonResult; - uint32_t status = transport->Invoke(_T("${info.title}.${method.pulls.for}"), jsonParameters, jsonResult); + uint32_t status = transport->Invoke(_T("${info.title.lowercase}.${method.pulls.for}"), jsonParameters, jsonResult); if (status == FireboltSDKErrorNone) { - FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully pushed with status as %d", jsonResult.Value()); + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.rpc.name} is successfully pushed with status as %d", jsonResult.Value()); status = (jsonResult.Value() == true) ? FireboltSDKErrorNone : FireboltSDKErrorNotSupported; } } else { @@ -39,7 +39,7 @@ static void ${info.Title}${method.Name}InnerCallback( void* userCB, const void* } uint32_t ${info.Title}_Register_${method.Name}( ${info.Title}${method.Name}Callback userCB, const void* userData ) { - const string eventName = _T("${info.title}.${method.name}"); + const string eventName = _T("${info.title.lowercase}.${method.rpc.name}"); uint32_t status = FireboltSDKErrorNone; if (userCB != nullptr) { @@ -50,5 +50,5 @@ uint32_t ${info.Title}_Register_${method.Name}( ${info.Title}${method.Name}Callb } uint32_t ${info.Title}_Unregister_${method.Name}( ${info.Title}${method.Name}Callback userCB) { - return FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title}.${method.name}"), reinterpret_cast(userCB)); + return FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title.lowercase}.${method.rpc.name}"), reinterpret_cast(userCB)); } diff --git a/languages/c/templates/methods/polymorphic-pull.c b/languages/c/templates/methods/polymorphic-pull.c index ec052e64..759b0ae9 100644 --- a/languages/c/templates/methods/polymorphic-pull.c +++ b/languages/c/templates/methods/polymorphic-pull.c @@ -1,16 +1,17 @@ -/* ${method.name} - ${method.description} */ +/* ${method.rpc.name} - ${method.description} */ uint32_t ${info.Title}_Push${method.Name}( ${method.signature.params} ) { uint32_t status = FireboltSDKErrorUnavailable; - string correlationId = ""; -${method.params.serialization} FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); if (transport != nullptr) { + string correlationId = ""; + ${method.params.serialization.with.indent} + WPEFramework::Core::JSON::Boolean jsonResult; - status = transport->Invoke(_T("${info.title}.${method.name}"), jsonParameters, jsonResult); + status = transport->Invoke(_T("${info.title.lowercase}.${method.rpc.name}"), jsonParameters, jsonResult); if (status == FireboltSDKErrorNone) { - FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully pushed with status as %d", jsonResult.Value()); + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.rpc.name} is successfully pushed with status as %d", jsonResult.Value()); status = (jsonResult.Value() == true) ? FireboltSDKErrorNone : FireboltSDKErrorNotSupported; } } else { diff --git a/languages/c/templates/methods/property.c b/languages/c/templates/methods/property.c index 9a92dce4..7fb5b148 100644 --- a/languages/c/templates/methods/property.c +++ b/languages/c/templates/methods/property.c @@ -1,7 +1,7 @@ -/* ${method.name} - ${method.description} */ +/* ${method.rpc.name} - ${method.description} */ uint32_t ${info.Title}_Get${method.Name}( ${method.signature.params}${if.params}, ${end.if.params}${method.result.type}* ${method.result.name} ) { - const string method = _T("${info.title}.${method.name}"); + const string method = _T("${info.title.lowercase}.${method.rpc.name}"); ${if.params}${method.params.serialization}${end.if.params} ${method.result.json} jsonResult; ${if.params}uint32_t status = FireboltSDK::Properties::Get(method, jsonParameters, jsonResult);${end.if.params} diff --git a/src/macrofier/engine.mjs b/src/macrofier/engine.mjs index 9a2da33f..1ad8b6e6 100644 --- a/src/macrofier/engine.mjs +++ b/src/macrofier/engine.mjs @@ -29,7 +29,7 @@ import isString from 'crocks/core/isString.js' import predicates from 'crocks/predicates/index.js' const { isObject, isArray, propEq, pathSatisfies, propSatisfies } = predicates -import { isRPCOnlyMethod, isProviderInterfaceMethod, getProviderInterface, getPayloadFromEvent, providerHasNoParameters, isTemporalSetMethod, isCallsMetricsMethod, isExcludedMethod, hasMethodAttributes, getMethodAttributes, isEventMethodWithContext, getSemanticVersion, getSetterFor, getProvidedCapabilities, isPolymorphicPullMethod, hasPublicAPIs } from '../shared/modules.mjs' +import { isRPCOnlyMethod, isProviderInterfaceMethod, getProviderInterface, getPayloadFromEvent, providerHasNoParameters, isTemporalSetMethod, hasMethodAttributes, getMethodAttributes, isEventMethodWithContext, getSemanticVersion, getSetterFor, getProvidedCapabilities, isPolymorphicPullMethod, hasPublicAPIs, createPolymorphicMethods } from '../shared/modules.mjs' import isEmpty from 'crocks/core/isEmpty.js' import { getLinkedSchemaPaths, getSchemaConstraints, isSchema, localizeDependencies, isDefinitionReferencedBySchema } from '../shared/json-schema.mjs' @@ -416,6 +416,21 @@ const generateMacros = (obj, templates, languages, options = {}) => { if (config.extractSubSchemas) { obj = promoteAndNameSubSchemas(obj) } + if (options.createPolymorphicMethods) { + let methods = [] + obj.methods && obj.methods.forEach(method => { + let polymorphicMethods = createPolymorphicMethods(method, obj) + if (polymorphicMethods.length > 1) { + polymorphicMethods.forEach(polymorphicMethod => { + methods.push(polymorphicMethod) + }) + } + else { + methods.push(method) + } + }) + obj.methods = methods + } // grab the options so we don't have to pass them from method to method Object.assign(state, options) @@ -1022,7 +1037,7 @@ function generateMethods(json = {}, examples = {}, templates = {}) { } // TODO: this is called too many places... let's reduce that to just generateMethods -function insertMethodMacros(template, methodObj, json, templates, examples = {}) { +function insertMethodMacros(template, methodObj, json, templates, examples={}) { const moduleName = getModuleName(json) const info = { @@ -1131,6 +1146,7 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) template = insertExampleMacros(template, examples[methodObj.name] || [], methodObj, json, templates) template = template.replace(/\$\{method\.name\}/g, method.name) + .replace(/\$\{method\.rpc\.name\}/g, methodObj.title || methodObj.name) .replace(/\$\{method\.summary\}/g, methodObj.summary) .replace(/\$\{method\.description\}/g, methodObj.description || methodObj.summary) diff --git a/src/macrofier/index.mjs b/src/macrofier/index.mjs index dc5f30e8..86483fad 100644 --- a/src/macrofier/index.mjs +++ b/src/macrofier/index.mjs @@ -44,6 +44,7 @@ const macrofy = async ( templatesPerModule, templatesPerSchema, persistPermission, + createPolymorphicMethods, createModuleDirectories, copySchemasIntoModules, extractSubSchemas, @@ -159,7 +160,7 @@ const macrofy = async ( // Pick the index and defaults templates for each module. templatesPerModule.forEach(t => { - const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, destination: t}) + const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: t}) let content = getTemplateForModule(module.info.title, t, templates) // NOTE: whichever insert is called first also needs to be called again last, so each phase can insert recursive macros from the other @@ -174,7 +175,7 @@ const macrofy = async ( }) if (primaryOutput) { - const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, destination: primaryOutput}) + const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: primaryOutput}) macros.append = append outputFiles[primaryOutput] = engine.insertMacros(outputFiles[primaryOutput], macros) } @@ -256,7 +257,7 @@ const macrofy = async ( Object.values(externalSchemas).forEach( document => { if (templatesPerSchema) { templatesPerSchema.forEach( t => { - const macros = engine.generateMacros(document, templates, exampleTemplates, {hideExcluded: hideExcluded, destination: t}) + const macros = engine.generateMacros(document, templates, exampleTemplates, {hideExcluded: hideExcluded, createPolymorphicMethods: createPolymorphicMethods, destination: t}) let content = getTemplate('/schemas', t, templates) // NOTE: whichever insert is called first also needs to be called again last, so each phase can insert recursive macros from the other diff --git a/src/sdk/index.mjs b/src/sdk/index.mjs index 7e85fab9..2e72ba9b 100755 --- a/src/sdk/index.mjs +++ b/src/sdk/index.mjs @@ -58,6 +58,7 @@ const run = async ({ templatesPerModule: config.templatesPerModule, templatesPerSchema: config.templatesPerSchema, persistPermission: config.persistPermission, + createPolymorphicMethods: config.createPolymorphicMethods, operators: config.operators, createModuleDirectories: config.createModuleDirectories, copySchemasIntoModules: config.copySchemasIntoModules, diff --git a/src/shared/modules.mjs b/src/shared/modules.mjs index a91c076b..927092c2 100644 --- a/src/shared/modules.mjs +++ b/src/shared/modules.mjs @@ -29,6 +29,7 @@ const { and, not } = logic import isString from 'crocks/core/isString.js' import predicates from 'crocks/predicates/index.js' import { getExternalSchemaPaths, isDefinitionReferencedBySchema, isNull, localizeDependencies, isSchema, getLocalSchemaPaths, replaceRef } from './json-schema.mjs' +import { getPath as getRefDefinition } from './json-schema.mjs' const { isObject, isArray, propEq, pathSatisfies, hasProp, propSatisfies } = predicates // util for visually debugging crocks ADTs @@ -861,6 +862,137 @@ const generateEventListenResponse = json => { return json } +const getAnyOfSchema = (inType, json) => { + let anyOfTypes = [] + let outType = localizeDependencies(inType, json) + if (outType.schema.anyOf) { + let definition = '' + if (inType.schema['$ref'] && (inType.schema['$ref'][0] === '#')) { + definition = getRefDefinition(inType.schema['$ref'], json, json['x-schemas']) + } + else { + definition = outType.schema + } + definition.anyOf.forEach(anyOf => { + anyOfTypes.push(anyOf) + }) + outType.schema.anyOf = anyOfTypes + } + return outType +} + +const generateAnyOfSchema = (anyOf, name, summary) => { + let anyOfType = {} + anyOfType["name"] = name[0].toLowerCase() + name.substr(1) + anyOfType["summary"] = summary + anyOfType["schema"] = anyOf + return anyOfType +} + +const generateParamsAnyOfSchema = (methodParams, anyOf, anyOfTypes, title, summary) => { + let params = [] + methodParams.forEach(p => { + if (p.schema.anyOf === anyOfTypes) { + let anyOfType = generateAnyOfSchema(anyOf, title, summary) + anyOfType.required = p.required + params.push(anyOfType) + } + else { + params.push(p) + } + }) + return params +} + +const generateResultAnyOfSchema = (method, methodResult, anyOf, anyOfTypes, title, summary) => { + let methodResultSchema = {} + if (methodResult.schema.anyOf === anyOfTypes) { + let anyOfType = generateAnyOfSchema(anyOf, title, summary) + let index = 0 + if (isEventMethod(method)) { + index = (method.result.schema.anyOf || method.result.schema.oneOf).indexOf(getPayloadFromEvent(method)) + } + else { + index = (method.result.schema.anyOf || method.result.schema.oneOf).indexOf(anyOfType) + } + if (method.result.schema.anyOf) { + methodResultSchema["anyOf"] = Object.assign([], method.result.schema.anyOf) + methodResultSchema.anyOf[index] = anyOfType.schema + } + else if (method.result.schema.oneOf) { + methodResultSchema["oneOf"] = Object.assign([], method.result.schema.oneOf) + methodResultSchema.oneOf[index] = anyOfType.schema + } + else { + methodResultSchema = anyOfType.schema + } + } + return methodResultSchema +} + +const createPolymorphicMethods = (method, json) => { + let anyOfTypes + let methodParams = [] + let methodResult = Object.assign({}, method.result) + method.params.forEach(p => { + if (p.schema) { + let param = getAnyOfSchema(p, json) + if (param.schema.anyOf && anyOfTypes) { + //anyOf is allowed with only one param in the params list + throw `WARNING anyOf is repeated with param:${p}` + } + else if (param.schema.anyOf) { + anyOfTypes = param.schema.anyOf + } + methodParams.push(param) + } + }) + let foundAnyOfParams = anyOfTypes ? true : false + + if (isEventMethod(method)) { + methodResult.schema = getPayloadFromEvent(method) + } + methodResult = getAnyOfSchema(methodResult, json) + let foundAnyOfResult = methodResult.schema.anyOf ? true : false + if (foundAnyOfParams === true && foundAnyOfResult === true) { + throw `WARNING anyOf is already with param schema, it is repeated with ${method.name} result too` + } + else if (foundAnyOfResult === true) { + anyOfTypes = methodResult.schema.anyOf + } + let polymorphicMethodSchemas = [] + //anyOfTypes will be allowed either in any one of the params or in result + if (anyOfTypes) { + let polymorphicMethodSchema = { + name: {}, + tags: {}, + summary: `${method.summary}`, + params: {}, + result: {}, + examples: {} + } + anyOfTypes.forEach(anyOf => { + + let localized = localizeDependencies(anyOf, json) + let title = localized.title || localized.name || '' + let summary = localized.summary || localized.description || '' + polymorphicMethodSchema.title = method.name + polymorphicMethodSchema.name = foundAnyOfParams ? `${method.name}With${title}` : `${method.name}${title}` + polymorphicMethodSchema.tags = method.tags + polymorphicMethodSchema.params = foundAnyOfParams ? generateParamsAnyOfSchema(methodParams, anyOf, anyOfTypes, title, summary) : methodParams + polymorphicMethodSchema.result = Object.assign({}, method.result) + polymorphicMethodSchema.result.schema = foundAnyOfResult ? generateResultAnyOfSchema(method, methodResult, anyOf, anyOfTypes, title, summary) : methodResult + polymorphicMethodSchema.examples = method.examples + polymorphicMethodSchemas.push(Object.assign({}, polymorphicMethodSchema)) + }) + } + else { + polymorphicMethodSchemas = method + } + + return polymorphicMethodSchemas +} + const getPathFromModule = (module, path) => { console.error("DEPRECATED: getPathFromModule") @@ -1183,5 +1315,6 @@ export { getSemanticVersion, addExternalMarkdown, addExternalSchemas, - getExternalMarkdownPaths -} \ No newline at end of file + getExternalMarkdownPaths, + createPolymorphicMethods +}