Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate polymorphci methods for anyOf param/result case #110

Merged
merged 2 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions languages/c/Types.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ function getSchemaTypeInfo(module = {}, json = {}, name = '', schemas = {}, pref
structure.namespace = getModuleName(module)
}
}

return structure
}

Expand Down
3 changes: 2 additions & 1 deletion languages/c/language.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
"/src/module_common.cpp",
"/src/jsondata_module.h"
],
"persistPermission": true
"persistPermission": true,
"createPolymorphicMethods": true
}
4 changes: 2 additions & 2 deletions languages/c/templates/codeblocks/setter.c
Original file line number Diff line number Diff line change
@@ -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);
}
6 changes: 3 additions & 3 deletions languages/c/templates/methods/default.c
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<FireboltSDK::Accessor>(), "${info.Title}.${method.name} is successfully invoked");
FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module<FireboltSDK::Accessor>(), "${info.Title}.${method.rpc.name} is successfully invoked");
${method.result.instantiation}
}

Expand Down
6 changes: 3 additions & 3 deletions languages/c/templates/methods/event.c
Original file line number Diff line number Diff line change
@@ -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}
Expand All @@ -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) {
Expand All @@ -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<void*>(userCB));
return FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title.lowercase}.${method.rpc.name}"), reinterpret_cast<void*>(userCB));
}
10 changes: 5 additions & 5 deletions languages/c/templates/methods/polymorphic-pull-event.c
Original file line number Diff line number Diff line change
@@ -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}
Expand Down Expand Up @@ -27,9 +27,9 @@ static void ${info.Title}${method.Name}InnerCallback( void* userCB, const void*
FireboltSDK::Transport<WPEFramework::Core::JSON::IElement>* 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<FireboltSDK::Accessor>(), "${info.Title}.${method.name} is successfully pushed with status as %d", jsonResult.Value());
FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module<FireboltSDK::Accessor>(), "${info.Title}.${method.rpc.name} is successfully pushed with status as %d", jsonResult.Value());
status = (jsonResult.Value() == true) ? FireboltSDKErrorNone : FireboltSDKErrorNotSupported;
}
} else {
Expand All @@ -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) {
Expand All @@ -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<void*>(userCB));
return FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title.lowercase}.${method.rpc.name}"), reinterpret_cast<void*>(userCB));
}
11 changes: 6 additions & 5 deletions languages/c/templates/methods/polymorphic-pull.c
Original file line number Diff line number Diff line change
@@ -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<WPEFramework::Core::JSON::IElement>* 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<FireboltSDK::Accessor>(), "${info.Title}.${method.name} is successfully pushed with status as %d", jsonResult.Value());
FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module<FireboltSDK::Accessor>(), "${info.Title}.${method.rpc.name} is successfully pushed with status as %d", jsonResult.Value());
status = (jsonResult.Value() == true) ? FireboltSDKErrorNone : FireboltSDKErrorNotSupported;
}
} else {
Expand Down
4 changes: 2 additions & 2 deletions languages/c/templates/methods/property.c
Original file line number Diff line number Diff line change
@@ -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}
Expand Down
20 changes: 18 additions & 2 deletions src/macrofier/engine.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 4 additions & 3 deletions src/macrofier/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ const macrofy = async (
templatesPerModule,
templatesPerSchema,
persistPermission,
createPolymorphicMethods,
createModuleDirectories,
copySchemasIntoModules,
extractSubSchemas,
Expand Down Expand Up @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/sdk/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
137 changes: 135 additions & 2 deletions src/shared/modules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -861,6 +862,137 @@ const generateEventListenResponse = json => {
return json
}

const getAnyOfSchema = (inType, json) => {
jlacivita marked this conversation as resolved.
Show resolved Hide resolved
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")

Expand Down Expand Up @@ -1183,5 +1315,6 @@ export {
getSemanticVersion,
addExternalMarkdown,
addExternalSchemas,
getExternalMarkdownPaths
}
getExternalMarkdownPaths,
createPolymorphicMethods
}