From b37481827ea6af4e50d756b7435a8827c48fb63c Mon Sep 17 00:00:00 2001 From: Kevin <72702438+kevinshahfws@users.noreply.github.com> Date: Fri, 14 Jun 2024 09:14:45 -0400 Subject: [PATCH] 3.0.0 Release (#194) * Integration of CPPSDK support (#176) * BREAKING CHANGE: Generalized templating engine to support both JavaScript and CPP (and other languages). * feat(languages): Add support for a distinct JSON-type for each schema * feat(accessors): New macro section for schema property accessors * Add support to generate file inclusion for common schema also (#97) * Detach setter declaration from property template and add setter template, remove unredundant warnings Cleanup changes, add separator between prefix and subSchema name generation Introduce excludeDeclarations flag to handle declaration exclusion specific to language Alignement changes + cleanup + OUT params support added to differentiate method signature parameters Update in static code * CPPSDK : Add Async communication support and CPP code generation for x-uses + x-allow-focus=true (#170) * chore(release): 3.0.0-next.1 [skip ci] # [3.0.0-next.1](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.3.0-next.1...v3.0.0-next.1) (2024-03-26) * Integration of CPPSDK support (#176) ([89294cc](https://github.com/rdkcentral/firebolt-openrpc/commit/89294cc37f23a94012621130858e01c946a3a9a6)), closes [#176](https://github.com/rdkcentral/firebolt-openrpc/issues/176) [#90](https://github.com/rdkcentral/firebolt-openrpc/issues/90) [#93](https://github.com/rdkcentral/firebolt-openrpc/issues/93) [#94](https://github.com/rdkcentral/firebolt-openrpc/issues/94) [#96](https://github.com/rdkcentral/firebolt-openrpc/issues/96) [#97](https://github.com/rdkcentral/firebolt-openrpc/issues/97) [#100](https://github.com/rdkcentral/firebolt-openrpc/issues/100) [#99](https://github.com/rdkcentral/firebolt-openrpc/issues/99) [#101](https://github.com/rdkcentral/firebolt-openrpc/issues/101) [#102](https://github.com/rdkcentral/firebolt-openrpc/issues/102) [#107](https://github.com/rdkcentral/firebolt-openrpc/issues/107) [#106](https://github.com/rdkcentral/firebolt-openrpc/issues/106) [#105](https://github.com/rdkcentral/firebolt-openrpc/issues/105) [#103](https://github.com/rdkcentral/firebolt-openrpc/issues/103) [#109](https://github.com/rdkcentral/firebolt-openrpc/issues/109) [#112](https://github.com/rdkcentral/firebolt-openrpc/issues/112) [#111](https://github.com/rdkcentral/firebolt-openrpc/issues/111) [#113](https://github.com/rdkcentral/firebolt-openrpc/issues/113) [#114](https://github.com/rdkcentral/firebolt-openrpc/issues/114) [#115](https://github.com/rdkcentral/firebolt-openrpc/issues/115) [#110](https://github.com/rdkcentral/firebolt-openrpc/issues/110) [#90](https://github.com/rdkcentral/firebolt-openrpc/issues/90) [#93](https://github.com/rdkcentral/firebolt-openrpc/issues/93) [#94](https://github.com/rdkcentral/firebolt-openrpc/issues/94) [#96](https://github.com/rdkcentral/firebolt-openrpc/issues/96) [#97](https://github.com/rdkcentral/firebolt-openrpc/issues/97) [#100](https://github.com/rdkcentral/firebolt-openrpc/issues/100) [#99](https://github.com/rdkcentral/firebolt-openrpc/issues/99) [#101](https://github.com/rdkcentral/firebolt-openrpc/issues/101) [#102](https://github.com/rdkcentral/firebolt-openrpc/issues/102) [#107](https://github.com/rdkcentral/firebolt-openrpc/issues/107) [#106](https://github.com/rdkcentral/firebolt-openrpc/issues/106) [#105](https://github.com/rdkcentral/firebolt-openrpc/issues/105) [#103](https://github.com/rdkcentral/firebolt-openrpc/issues/103) [#109](https://github.com/rdkcentral/firebolt-openrpc/issues/109) [#112](https://github.com/rdkcentral/firebolt-openrpc/issues/112) [#111](https://github.com/rdkcentral/firebolt-openrpc/issues/111) [#113](https://github.com/rdkcentral/firebolt-openrpc/issues/113) [#114](https://github.com/rdkcentral/firebolt-openrpc/issues/114) [#115](https://github.com/rdkcentral/firebolt-openrpc/issues/115) [#110](https://github.com/rdkcentral/firebolt-openrpc/issues/110) [#117](https://github.com/rdkcentral/firebolt-openrpc/issues/117) [#116](https://github.com/rdkcentral/firebolt-openrpc/issues/116) [#118](https://github.com/rdkcentral/firebolt-openrpc/issues/118) [#121](https://github.com/rdkcentral/firebolt-openrpc/issues/121) [#119](https://github.com/rdkcentral/firebolt-openrpc/issues/119) [#90](https://github.com/rdkcentral/firebolt-openrpc/issues/90) [#93](https://github.com/rdkcentral/firebolt-openrpc/issues/93) [#94](https://github.com/rdkcentral/firebolt-openrpc/issues/94) [#96](https://github.com/rdkcentral/firebolt-openrpc/issues/96) [#97](https://github.com/rdkcentral/firebolt-openrpc/issues/97) [#100](https://github.com/rdkcentral/firebolt-openrpc/issues/100) [#99](https://github.com/rdkcentral/firebolt-openrpc/issues/99) [#101](https://github.com/rdkcentral/firebolt-openrpc/issues/101) [#102](https://github.com/rdkcentral/firebolt-openrpc/issues/102) [#107](https://github.com/rdkcentral/firebolt-openrpc/issues/107) [#106](https://github.com/rdkcentral/firebolt-openrpc/issues/106) [#105](https://github.com/rdkcentral/firebolt-openrpc/issues/105) [#103](https://github.com/rdkcentral/firebolt-openrpc/issues/103) [#109](https://github.com/rdkcentral/firebolt-openrpc/issues/109) [#112](https://github.com/rdkcentral/firebolt-openrpc/issues/112) [#111](https://github.com/rdkcentral/firebolt-openrpc/issues/111) [#113](https://github.com/rdkcentral/firebolt-openrpc/issues/113) [#114](https://github.com/rdkcentral/firebolt-openrpc/issues/114) [#115](https://github.com/rdkcentral/firebolt-openrpc/issues/115) [#110](https://github.com/rdkcentral/firebolt-openrpc/issues/110) [#117](https://github.com/rdkcentral/firebolt-openrpc/issues/117) [#116](https://github.com/rdkcentral/firebolt-openrpc/issues/116) [#118](https://github.com/rdkcentral/firebolt-openrpc/issues/118) [#121](https://github.com/rdkcentral/firebolt-openrpc/issues/121) [#119](https://github.com/rdkcentral/firebolt-openrpc/issues/119) [#127](https://github.com/rdkcentral/firebolt-openrpc/issues/127) [#137](https://github.com/rdkcentral/firebolt-openrpc/issues/137) [#135](https://github.com/rdkcentral/firebolt-openrpc/issues/135) [#134](https://github.com/rdkcentral/firebolt-openrpc/issues/134) [#136](https://github.com/rdkcentral/firebolt-openrpc/issues/136) [#139](https://github.com/rdkcentral/firebolt-openrpc/issues/139) [#138](https://github.com/rdkcentral/firebolt-openrpc/issues/138) [#140](https://github.com/rdkcentral/firebolt-openrpc/issues/140) [#142](https://github.com/rdkcentral/firebolt-openrpc/issues/142) [#144](https://github.com/rdkcentral/firebolt-openrpc/issues/144) [#145](https://github.com/rdkcentral/firebolt-openrpc/issues/145) [#141](https://github.com/rdkcentral/firebolt-openrpc/issues/141) [#149](https://github.com/rdkcentral/firebolt-openrpc/issues/149) [#150](https://github.com/rdkcentral/firebolt-openrpc/issues/150) [#152](https://github.com/rdkcentral/firebolt-openrpc/issues/152) [#151](https://github.com/rdkcentral/firebolt-openrpc/issues/151) [#153](https://github.com/rdkcentral/firebolt-openrpc/issues/153) [#154](https://github.com/rdkcentral/firebolt-openrpc/issues/154) [#156](https://github.com/rdkcentral/firebolt-openrpc/issues/156) [#157](https://github.com/rdkcentral/firebolt-openrpc/issues/157) [#158](https://github.com/rdkcentral/firebolt-openrpc/issues/158) [#160](https://github.com/rdkcentral/firebolt-openrpc/issues/160) [#159](https://github.com/rdkcentral/firebolt-openrpc/issues/159) [#161](https://github.com/rdkcentral/firebolt-openrpc/issues/161) [#163](https://github.com/rdkcentral/firebolt-openrpc/issues/163) [#164](https://github.com/rdkcentral/firebolt-openrpc/issues/164) [#165](https://github.com/rdkcentral/firebolt-openrpc/issues/165) [#167](https://github.com/rdkcentral/firebolt-openrpc/issues/167) [#169](https://github.com/rdkcentral/firebolt-openrpc/issues/169) [#171](https://github.com/rdkcentral/firebolt-openrpc/issues/171) [#173](https://github.com/rdkcentral/firebolt-openrpc/issues/173) [#172](https://github.com/rdkcentral/firebolt-openrpc/issues/172) [#170](https://github.com/rdkcentral/firebolt-openrpc/issues/170) * chore: Copy JS templates to C and create language.config.json * feat(accessors): New macro section for schema property accessors * feat: CI/CD Merging from next (#183) # [3.0.0-next.2](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.1...v3.0.0-next.2) (2024-04-08) ### Features * CI/CD Merging from next ([#183](https://github.com/rdkcentral/firebolt-openrpc/issues/183)) ([14476a9](https://github.com/rdkcentral/firebolt-openrpc/commit/14476a908f2c10d907417187c41ce255ac2ea6a6)) * Detach setter declaration from property template and add setter template, remove unredundant warnings Cleanup changes, add separator between prefix and subSchema name generation Introduce excludeDeclarations flag to handle declaration exclusion specific to language Alignement changes + cleanup + OUT params support added to differentiate method signature parameters Update in static code * Add support to generate file inclusion for common schema also (#97) * C-Language: Templates based property methods (#100) * feat: Add method templates for properties * feat: Property setters using templates * fix: Macro name correction * Add support to keep original file permissions of template/sdk files (#99) * Event template implementation added (#101) * Template for Polymorphic pull method (#102) * chore(release): 3.0.0-next.3 [skip ci] # [3.0.0-next.3](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.2...v3.0.0-next.3) (2024-05-09) ### Bug Fixes * Account for new OpenRPC spec URL contents ([#186](https://github.com/rdkcentral/firebolt-openrpc/issues/186)) ([66bd6ac](https://github.com/rdkcentral/firebolt-openrpc/commit/66bd6acc1ffdaefadcb46d78864d615a116b5a85)) * feat: App Pass-through (#188) 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. * chore(release): 3.0.0-next.4 [skip ci] # [3.0.0-next.4](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.3...v3.0.0-next.4) (2024-06-06) ### Features * App Pass-through ([#188](https://github.com/rdkcentral/firebolt-openrpc/issues/188)) ([83fa0fc](https://github.com/rdkcentral/firebolt-openrpc/commit/83fa0fcb62cade7ed3b527de6b3fe1ebb42b54e0)) * chore(release): 3.0.0-next.5 [skip ci] # [3.0.0-next.5](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.4...v3.0.0-next.5) (2024-06-13) --- .github/workflows/merge-to-main.yml | 43 + .github/workflows/merge-to-next-major.yml | 72 ++ .gitignore | 1 + CHANGELOG.md | 41 + languages/c-structs/language.config.json | 27 + languages/c-structs/src/index.mjs | 19 + .../c-structs/templates/codeblocks/export.c | 1 + .../templates/codeblocks/mock-import.c | 1 + .../templates/codeblocks/mock-parameter.c | 1 + .../c-structs/templates/codeblocks/module.c | 0 .../c-structs/templates/codeblocks/setter.c | 9 + .../c-structs/templates/declarations/clear.c | 0 .../templates/declarations/default.c | 5 + .../c-structs/templates/declarations/event.c | 4 + .../c-structs/templates/declarations/listen.c | 0 .../c-structs/templates/declarations/once.c | 0 .../declarations/polymorphic-reducer.c | 5 + .../templates/declarations/property.c | 5 + .../templates/declarations/provide.c | 0 .../templates/declarations/rpc-only.c | 0 .../c-structs/templates/defaults/default.c | 0 .../c-structs/templates/defaults/property.c | 0 .../c-structs/templates/imports/default.cpp | 1 + .../c-structs/templates/imports/default.h | 1 + .../templates/imports/default.jsondata | 1 + .../json-types/additionalProperties.c | 1 + .../c-structs/templates/json-types/anyOf.c | 1 + .../c-structs/templates/json-types/array.c | 4 + .../c-structs/templates/json-types/boolean.c | 1 + .../c-structs/templates/json-types/const.c | 0 .../templates/json-types/default.cpp | 1 + .../c-structs/templates/json-types/enum.cpp | 4 + .../c-structs/templates/json-types/enum.h | 4 + .../c-structs/templates/json-types/float.c | 1 + .../c-structs/templates/json-types/integer.c | 1 + .../templates/json-types/namespace.c | 1 + .../c-structs/templates/json-types/object.cpp | 25 + .../templates/json-types/primitive.c | 0 .../templates/json-types/property-assign.cpp | 2 + .../json-types/property-register.cpp | 1 + .../templates/json-types/property.cpp | 1 + .../c-structs/templates/json-types/ref.c | 1 + .../c-structs/templates/json-types/string.c | 1 + .../c-structs/templates/json-types/title.c | 1 + .../c-structs/templates/json-types/tuple.c | 25 + .../templates/json-types/types/object.c | 0 .../c-structs/templates/json-types/x-method.c | 1 + .../c-structs/templates/language/enum-item.c | 1 + languages/c-structs/templates/language/enum.c | 3 + .../c-structs/templates/language/parameter.c | 1 + .../templates/language/schema-item.c | 1 + .../c-structs/templates/language/schema.c | 3 + languages/c-structs/templates/methods/clear.c | 0 .../c-structs/templates/methods/default.c | 24 + languages/c-structs/templates/methods/event.c | 26 + .../c-structs/templates/methods/listen.c | 0 languages/c-structs/templates/methods/once.c | 0 .../methods/polymorphic-pull-event.c | 0 .../templates/methods/polymorphic-pull.c | 21 + .../templates/methods/polymorphic-reducer.c | 0 .../c-structs/templates/methods/property.c | 15 + .../c-structs/templates/methods/provide.c | 0 .../c-structs/templates/methods/rpc-only.c | 0 .../c-structs/templates/methods/setter.c | 0 .../templates/modules/include/Module.h | 41 + .../templates/modules/src/Module.cpp | 41 + .../parameter-serialization/boolean.cpp | 2 + .../parameter-serialization/default.cpp | 2 + .../parameter-serialization/object.cpp | 2 + .../parameter-serialization/primitive.cpp | 2 + .../parameter-serialization/string.cpp | 2 + .../c-structs/templates/parameters/default.c | 1 + .../c-structs/templates/parameters/json.c | 3 + .../c-structs/templates/parameters/optional.c | 1 + .../c-structs/templates/parameters/result.c | 1 + .../result-instantiation/boolean.cpp | 2 + .../result-instantiation/default.cpp | 1 + .../templates/result-instantiation/object.cpp | 4 + .../result-instantiation/primitive.cpp | 1 + .../templates/result-instantiation/string.cpp | 2 + .../c-structs/templates/schemas/default.c | 1 + .../templates/schemas/include/Common/Module.h | 39 + .../templates/schemas/src/JsonData_Module.h | 30 + .../templates/schemas/src/Module_Common.cpp | 35 + .../c-structs/templates/sdk/CMakeLists.txt | 61 ++ .../templates/sdk/cmake/HelperFunctions.cmake | 144 +++ .../templates/sdk/cmake/project.cmake.in | 35 + .../c-structs/templates/sdk/include/Error.h | 41 + .../templates/sdk/include/Firebolt.h | 65 ++ .../c-structs/templates/sdk/include/Types.h | 37 + .../c-structs/templates/sdk/scripts/build.sh | 11 + .../templates/sdk/scripts/install.js | 32 + .../templates/sdk/src/Accessor/Accessor.cpp | 117 +++ .../templates/sdk/src/Accessor/Accessor.h | 122 +++ .../templates/sdk/src/Accessor/WorkerPool.h | 102 ++ .../templates/sdk/src/CMakeLists.txt | 84 ++ .../templates/sdk/src/Event/Event.cpp | 147 +++ .../c-structs/templates/sdk/src/Event/Event.h | 165 +++ .../c-structs/templates/sdk/src/Firebolt.cpp | 40 + .../templates/sdk/src/FireboltSDK.conf.in | 3 + .../c-structs/templates/sdk/src/FireboltSDK.h | 26 + .../templates/sdk/src/Logger/Logger.cpp | 83 ++ .../templates/sdk/src/Logger/Logger.h | 85 ++ .../c-structs/templates/sdk/src/Module.cpp | 21 + .../c-structs/templates/sdk/src/Module.h | 29 + .../templates/sdk/src/Properties/Properties.h | 148 +++ .../templates/sdk/src/Transport/Transport.cpp | 24 + .../templates/sdk/src/Transport/Transport.h | 897 ++++++++++++++++ .../c-structs/templates/sdk/src/Types.cpp | 40 + .../c-structs/templates/sdk/src/TypesPriv.h | 56 + .../templates/sdk/test/CMakeLists.txt | 86 ++ languages/c-structs/templates/sdk/test/Main.c | 44 + .../c-structs/templates/sdk/test/Module.cpp | 21 + .../c-structs/templates/sdk/test/Module.h | 29 + .../templates/sdk/test/OpenRPCCTests.h | 42 + .../templates/sdk/test/OpenRPCTests.cpp | 385 +++++++ .../templates/sdk/test/OpenRPCTests.h | 99 ++ .../c-structs/templates/sdk/test/TestUtils.h | 38 + .../c-structs/templates/sections/accessors.c | 2 + .../templates/sections/declarations.c | 1 + .../c-structs/templates/sections/enums.c | 1 + .../c-structs/templates/sections/events.c | 1 + .../c-structs/templates/sections/methods.c | 4 + .../templates/sections/methods_accessors.c | 1 + .../templates/sections/methods_types.c | 1 + .../templates/sections/provider-interfaces.c | 11 + .../c-structs/templates/sections/schemas.c | 1 + .../c-structs/templates/sections/types.c | 1 + languages/c-structs/templates/types/anyOf.c | 1 + languages/c-structs/templates/types/array.c | 1 + languages/c-structs/templates/types/const.c | 0 languages/c-structs/templates/types/default.c | 4 + languages/c-structs/templates/types/enum.cpp | 4 + languages/c-structs/templates/types/enum.h | 4 + languages/c-structs/templates/types/object.c | 3 + .../c-structs/templates/types/primitive.c | 1 + .../c-structs/templates/types/property.c | 1 + languages/c-structs/templates/types/ref.c | 1 + languages/c-structs/templates/types/title.c | 1 + languages/c-structs/templates/types/tuple.c | 3 + .../json-types/additionalProperties.c | 1 + languages/c/templates/json-types/anyOf.c | 1 + languages/c/templates/json-types/array.c | 4 + languages/c/templates/json-types/boolean.c | 1 + languages/c/templates/json-types/const.c | 0 languages/c/templates/json-types/default.cpp | 1 + languages/c/templates/json-types/enum.cpp | 4 + languages/c/templates/json-types/enum.h | 4 + languages/c/templates/json-types/float.c | 1 + languages/c/templates/json-types/integer.c | 1 + languages/c/templates/json-types/namespace.c | 1 + languages/c/templates/json-types/object.cpp | 25 + languages/c/templates/json-types/primitive.c | 0 .../templates/json-types/property-assign.cpp | 2 + .../json-types/property-register.cpp | 2 + languages/c/templates/json-types/property.cpp | 1 + languages/c/templates/json-types/ref.c | 1 + languages/c/templates/json-types/string.c | 1 + languages/c/templates/json-types/title.c | 1 + languages/c/templates/json-types/tuple.c | 25 + .../c/templates/json-types/types/object.c | 0 languages/c/templates/json-types/x-method.c | 1 + languages/c/templates/language/enum-item.c | 1 + languages/c/templates/language/enum.c | 3 + languages/c/templates/language/parameter.c | 1 + languages/c/templates/language/schema-item.c | 1 + languages/c/templates/language/schema.c | 3 + .../parameter-serialization/boolean.cpp | 2 + .../parameter-serialization/default.cpp | 2 + .../parameter-serialization/object.cpp | 2 + .../parameter-serialization/primitive.cpp | 2 + .../parameter-serialization/string.cpp | 2 + languages/c/templates/parameters/optional.c | 1 + languages/c/templates/parameters/result.c | 1 + .../result-instantiation/boolean.cpp | 2 + .../result-instantiation/default.cpp | 1 + .../templates/result-instantiation/object.cpp | 4 + .../result-instantiation/primitive.cpp | 1 + .../templates/result-instantiation/string.cpp | 2 + languages/c/templates/sections/enums.c | 1 + .../c/templates/types/additionalProperties.c | 4 + languages/c/templates/types/anyOf.c | 1 + languages/c/templates/types/array.c | 4 + languages/c/templates/types/const.c | 0 languages/c/templates/types/default.c | 4 + languages/c/templates/types/object.c | 10 + languages/c/templates/types/primitive.c | 0 languages/c/templates/types/property.c | 6 + languages/c/templates/types/ref.c | 1 + languages/c/templates/types/title.c | 1 + languages/c/templates/types/tuple.c | 8 + languages/c/templates/types/x-method.c | 1 + languages/cpp/language.config.json | 34 + languages/cpp/src/shared/CMakeLists.txt | 56 + .../src/shared/cmake/HelperFunctions.cmake | 72 ++ .../cpp/src/shared/cmake/project.cmake.in | 35 + languages/cpp/src/shared/include/error.h | 39 + languages/cpp/src/shared/include/types.h | 28 + .../cpp/src/shared/src/Accessor/Accessor.cpp | 95 ++ .../cpp/src/shared/src/Accessor/Accessor.h | 144 +++ .../cpp/src/shared/src/Accessor/WorkerPool.h | 102 ++ languages/cpp/src/shared/src/Async/Async.cpp | 78 ++ languages/cpp/src/shared/src/Async/Async.h | 203 ++++ languages/cpp/src/shared/src/CMakeLists.txt | 79 ++ languages/cpp/src/shared/src/Event/Event.cpp | 163 +++ languages/cpp/src/shared/src/Event/Event.h | 163 +++ .../cpp/src/shared/src/FireboltSDK.conf.in | 3 + languages/cpp/src/shared/src/FireboltSDK.h | 27 + languages/cpp/src/shared/src/IModule.h | 25 + .../cpp/src/shared/src/Logger/Logger.cpp | 86 ++ languages/cpp/src/shared/src/Logger/Logger.h | 85 ++ languages/cpp/src/shared/src/Module.cpp | 21 + languages/cpp/src/shared/src/Module.h | 29 + .../src/shared/src/Properties/Properties.h | 148 +++ .../src/shared/src/Transport/Transport.cpp | 24 + .../cpp/src/shared/src/Transport/Transport.h | 983 ++++++++++++++++++ languages/cpp/src/shared/src/TypesPriv.h | 62 ++ languages/cpp/src/shared/test/CMakeLists.txt | 86 ++ languages/cpp/src/shared/test/Main.cpp | 44 + languages/cpp/src/shared/test/Module.cpp | 21 + languages/cpp/src/shared/test/Module.h | 28 + .../cpp/src/shared/test/OpenRPCTests.cpp | 319 ++++++ languages/cpp/src/shared/test/OpenRPCTests.h | 118 +++ languages/cpp/src/shared/test/TestUtils.h | 38 + .../templates/additional-types/boolean.cpp | 1 + .../templates/additional-types/integer.cpp | 1 + .../cpp/templates/additional-types/number.cpp | 1 + .../cpp/templates/additional-types/string.cpp | 1 + .../generic.cpp | 3 + .../primitive.cpp | 1 + .../callback-context-instantiation/ref.h | 1 + .../generic.cpp | 1 + .../primitive.cpp | 1 + .../additionalProperties.cpp | 1 + .../callback-initialization/anyOf.cpp | 1 + .../callback-initialization/array.cpp | 1 + .../callback-initialization/enum.cpp | 1 + .../callback-initialization/generic.cpp | 1 + .../object-empty-property.cpp | 1 + .../callback-initialization/object.cpp | 1 + .../callback-initialization/primitive.cpp | 1 + .../callback-initialization/title.cpp | 1 + .../callback-instantiation/generic.cpp | 22 + .../generic.cpp | 1 + .../primitive.cpp | 1 + .../callback-parameter-serialization/ref.h | 1 + .../additionalProperties.cpp | 1 + .../generic.cpp | 1 + .../primitive.cpp | 1 + .../callback-response-instantiation/ref.h | 1 + .../additionalProperties.cpp | 7 + .../callback-result-instantiation/anyOf.cpp | 1 + .../callback-result-instantiation/array.cpp | 4 + .../callback-result-instantiation/enum.cpp | 1 + .../callback-result-instantiation/generic.cpp | 1 + .../namespace.cpp | 1 + .../callback-result-instantiation/object.cpp | 1 + .../primitive.cpp | 1 + .../property.cpp | 3 + .../callback-result-instantiation/ref.h | 1 + .../sub-property/anyOf.cpp | 1 + .../sub-property/anyOfSchemaShape.cpp | 7 + .../sub-property/array.cpp | 10 + .../sub-property/const.cpp | 3 + .../sub-property/namespace.cpp | 1 + .../sub-property/object-array.cpp | 4 + .../sub-property/object-separator.cpp | 1 + .../sub-property/object.cpp | 6 + .../sub-property/property.cpp | 3 + .../sub-property/ref.h | 1 + .../sub-property/title.cpp | 1 + .../callback-result-instantiation/title.cpp | 1 + .../callback-result-instantiation/tuple.cpp | 2 + .../additionalProperties.cpp | 1 + .../callback-result-serialization/generic.cpp | 1 + .../primitive.cpp | 1 + .../callback-result-serialization/ref.h | 1 + .../callback-serialization/generic.cpp | 1 + .../callback-value-initialization/generic.cpp | 3 + .../additionalProperties.cpp | 4 + .../callback-value-instantiation/anyOf.cpp | 1 + .../callback-value-instantiation/array.cpp | 4 + .../callback-value-instantiation/enum.cpp | 1 + .../callback-value-instantiation/generic.cpp | 1 + .../namespace.cpp | 1 + .../callback-value-instantiation/object.cpp | 6 + .../primitive.cpp | 1 + .../callback-value-instantiation/property.cpp | 1 + .../callback-value-instantiation/ref.h | 1 + .../callback-value-instantiation/string.cpp | 1 + .../sub-property/anyOf.cpp | 1 + .../sub-property/anyOfSchemaShape.cpp | 3 + .../sub-property/array.cpp | 5 + .../sub-property/const.cpp | 1 + .../sub-property/generic.cpp | 1 + .../sub-property/namespace.cpp | 1 + .../sub-property/object-array.cpp | 4 + .../sub-property/object-separator.cpp | 1 + .../sub-property/object.cpp | 4 + .../sub-property/property.cpp | 1 + .../sub-property/ref.h | 1 + .../sub-property/title.cpp | 1 + .../callback-value-instantiation/title.cpp | 1 + .../callback-value-instantiation/tuple.cpp | 2 + .../cpp/templates/codeblocks/interface.cpp | 47 + .../cpp/templates/codeblocks/interface.h | 12 + .../codeblocks/module-include-private.cpp | 2 + .../cpp/templates/codeblocks/module-include.h | 2 + .../cpp/templates/codeblocks/module-init.cpp | 15 + .../cpp/templates/codeblocks/module-init.h | 3 + .../codeblocks/provider-subscribe.cpp | 1 + .../cpp/templates/codeblocks/provider.cpp | 1 + languages/cpp/templates/codeblocks/provider.h | 1 + languages/cpp/templates/codeblocks/setter.cpp | 16 + .../cpp/templates/codeblocks/subscribe.cpp | 5 + .../declarations-override/allowsFocus.h | 6 + .../declarations-override/calls-metrics.h | 5 + .../templates/declarations-override/clear.h | 0 .../templates/declarations-override/default.h | 5 + .../templates/declarations-override/event.h | 4 + .../templates/declarations-override/listen.h | 0 .../templates/declarations-override/once.h | 0 .../polymorphic-pull-event.h | 2 + .../declarations-override/polymorphic-pull.h | 5 + .../polymorphic-reducer.h | 0 .../declarations-override/property.h | 5 + .../templates/declarations-override/provide.h | 1 + .../declarations-override/rpc-only.h | 0 .../templates/declarations-override/setter.h | 5 + .../cpp/templates/declarations/allowsFocus.h | 6 + .../templates/declarations/calls-metrics.h | 6 + languages/cpp/templates/declarations/clear.h | 0 .../cpp/templates/declarations/default.h | 6 + languages/cpp/templates/declarations/event.h | 8 + languages/cpp/templates/declarations/listen.h | 0 languages/cpp/templates/declarations/once.h | 0 .../declarations/polymorphic-pull-event.h | 6 + .../templates/declarations/polymorphic-pull.h | 6 + .../declarations/polymorphic-reducer.h | 0 .../cpp/templates/declarations/property.h | 5 + .../cpp/templates/declarations/provide.h | 1 + .../cpp/templates/declarations/rpc-only.h | 0 languages/cpp/templates/declarations/setter.h | 5 + .../cpp/templates/imports/calls-metrics.cpp | 1 + .../cpp/templates/imports/calls-metrics.impl | 1 + languages/cpp/templates/imports/default.cpp | 1 + languages/cpp/templates/imports/default.h | 1 + languages/cpp/templates/imports/default.impl | 1 + .../cpp/templates/imports/default.jsondata | 1 + .../cpp/templates/interfaces/default.cpp | 17 + languages/cpp/templates/interfaces/default.h | 1 + .../cpp/templates/interfaces/focusable.cpp | 44 + .../cpp/templates/interfaces/focusable.h | 1 + .../json-types/additionalProperties.cpp | 1 + languages/cpp/templates/json-types/anyOf.h | 1 + .../templates/json-types/anyOfSchemaShape.h | 1 + languages/cpp/templates/json-types/array.h | 1 + languages/cpp/templates/json-types/boolean.h | 1 + languages/cpp/templates/json-types/const.h | 1 + languages/cpp/templates/json-types/default.h | 1 + languages/cpp/templates/json-types/enum.cpp | 1 + languages/cpp/templates/json-types/integer.h | 1 + .../cpp/templates/json-types/namespace.h | 1 + languages/cpp/templates/json-types/null.h | 1 + languages/cpp/templates/json-types/number.h | 1 + .../json-types/object-empty-property.h | 1 + languages/cpp/templates/json-types/object.cpp | 25 + .../cpp/templates/json-types/primitive.h | 1 + .../templates/json-types/property-assign.cpp | 2 + .../json-types/property-register.cpp | 1 + .../cpp/templates/json-types/property.cpp | 1 + languages/cpp/templates/json-types/ref.h | 1 + languages/cpp/templates/json-types/string.h | 1 + languages/cpp/templates/json-types/title.h | 1 + languages/cpp/templates/json-types/tuple.cpp | 1 + languages/cpp/templates/json-types/void.cpp | 1 + languages/cpp/templates/json-types/x-method.h | 1 + languages/cpp/templates/language/enum-item.h | 1 + languages/cpp/templates/language/enum.h | 3 + languages/cpp/templates/language/parameter.h | 1 + .../cpp/templates/language/schema-item.h | 1 + languages/cpp/templates/language/schema.h | 3 + .../cpp/templates/methods/allowsFocus.cpp | 45 + .../cpp/templates/methods/calls-metrics.cpp | 29 + languages/cpp/templates/methods/clear.cpp | 0 languages/cpp/templates/methods/default.cpp | 26 + languages/cpp/templates/methods/event.cpp | 37 + languages/cpp/templates/methods/listen.cpp | 0 languages/cpp/templates/methods/once.cpp | 0 .../methods/polymorphic-pull-event.cpp | 61 ++ .../templates/methods/polymorphic-pull.cpp | 26 + .../templates/methods/polymorphic-reducer.cpp | 0 languages/cpp/templates/methods/property.cpp | 19 + languages/cpp/templates/methods/provide.cpp | 8 + languages/cpp/templates/methods/rpc-only.cpp | 0 languages/cpp/templates/methods/setter.cpp | 0 .../cpp/templates/modules/include/module.h | 43 + .../cpp/templates/modules/src/module_impl.cpp | 39 + .../cpp/templates/modules/src/module_impl.h | 49 + .../additionalProperties.cpp | 12 + .../parameter-serialization/array.cpp | 11 + .../parameter-serialization/enum.cpp | 7 + .../parameter-serialization/generic.cpp | 5 + .../parameter-serialization/namespace.cpp | 1 + .../parameter-serialization/object-array.cpp | 9 + .../object-empty-property.cpp | 5 + .../parameter-serialization/object.cpp | 19 + .../patternProperties.cpp | 9 + .../parameter-serialization/primitive.cpp | 1 + .../parameter-serialization/property.cpp | 3 + .../templates/parameter-serialization/ref.h | 1 + .../sub-property/anyOf.cpp | 1 + .../sub-property/anyOfSchemaShape.cpp | 3 + .../sub-property/array.cpp | 13 + .../sub-property/const.cpp | 3 + .../sub-property/namespace.cpp | 1 + .../sub-property/object-array.cpp | 5 + .../sub-property/object-separator.cpp | 1 + .../sub-property/object.cpp | 3 + .../sub-property/property.cpp | 3 + .../sub-property/ref.h | 1 + .../sub-property/title.cpp | 1 + .../parameter-serialization/title.cpp | 1 + languages/cpp/templates/parameters/default.h | 1 + languages/cpp/templates/parameters/json.cpp | 3 + .../cpp/templates/parameters/nonprimitive.h | 1 + languages/cpp/templates/parameters/optional.h | 1 + languages/cpp/templates/parameters/string.h | 1 + .../cpp/templates/result-callback/default.h | 1 + .../templates/result-callback/nonprimitive.h | 1 + .../cpp/templates/result-callback/string.h | 1 + .../additionalProperties.cpp | 1 + .../templates/result-initialization/anyOf.cpp | 1 + .../templates/result-initialization/array.cpp | 1 + .../result-initialization/boolean.cpp | 1 + .../result-initialization/generic.cpp | 1 + .../result-initialization/namespace.cpp | 1 + .../result-initialization/number.cpp | 1 + .../object-empty-property.cpp | 1 + .../result-initialization/primitive.cpp | 1 + .../cpp/templates/result-initialization/ref.h | 1 + .../result-initialization/string.cpp | 1 + .../templates/result-initialization/title.cpp | 1 + .../additionalProperties.cpp | 7 + .../templates/result-instantiation/anyOf.cpp | 3 + .../templates/result-instantiation/array.cpp | 4 + .../result-instantiation/generic.cpp | 1 + .../result-instantiation/namespace.cpp | 1 + .../result-instantiation/object-array.cpp | 4 + .../object-empty-property.cpp | 3 + .../templates/result-instantiation/object.cpp | 3 + .../result-instantiation/primitive.cpp | 1 + .../result-instantiation/property.cpp | 3 + .../cpp/templates/result-instantiation/ref.h | 1 + .../templates/result-instantiation/string.cpp | 1 + .../sub-property/anyOf.cpp | 1 + .../sub-property/anyOfSchemaShape.cpp | 7 + .../sub-property/array.cpp | 10 + .../sub-property/namespace.cpp | 1 + .../sub-property/object-array.cpp | 6 + .../sub-property/object-separator.cpp | 1 + .../sub-property/object.cpp | 6 + .../sub-property/property.cpp | 3 + .../result-instantiation/sub-property/ref.h | 1 + .../sub-property/title.cpp | 1 + .../templates/result-instantiation/title.cpp | 1 + .../templates/result-instantiation/tuple.cpp | 5 + languages/cpp/templates/result/default.h | 1 + languages/cpp/templates/schemas/default.cpp | 1 + .../templates/schemas/include/common/module.h | 35 + .../templates/schemas/src/jsondata_module.h | 30 + .../templates/schemas/src/module_common.cpp | 27 + .../cpp/templates/sdk/include/firebolt.h | 133 +++ languages/cpp/templates/sdk/scripts/build.sh | 44 + .../cpp/templates/sdk/scripts/install.sh | 65 ++ languages/cpp/templates/sdk/src/firebolt.cpp | 116 +++ .../cpp/templates/sections/declarations.h | 1 + languages/cpp/templates/sections/enums.h | 1 + languages/cpp/templates/sections/events.h | 1 + languages/cpp/templates/sections/methods.h | 1 + .../cpp/templates/sections/methods_types.h | 1 + .../sections/provider-interfaces.cpp | 1 + .../templates/sections/provider-interfaces.h | 14 + .../templates/sections/provider-subscribe.cpp | 1 + languages/cpp/templates/sections/schemas.h | 1 + languages/cpp/templates/sections/types.h | 1 + .../cpp/templates/sections/xuses-interfaces.h | 7 + .../templates/types/additionalProperties.h | 1 + .../templates/types/additionalPropertiesKey.h | 1 + languages/cpp/templates/types/anyOf.h | 1 + .../cpp/templates/types/anyOfSchemaShape.h | 1 + languages/cpp/templates/types/array.h | 1 + languages/cpp/templates/types/boolean.h | 1 + languages/cpp/templates/types/const.h | 1 + languages/cpp/templates/types/default.h | 1 + languages/cpp/templates/types/enum.cpp | 4 + languages/cpp/templates/types/enum.h | 4 + languages/cpp/templates/types/integer.h | 1 + languages/cpp/templates/types/items.h | 1 + languages/cpp/templates/types/namespace.h | 1 + languages/cpp/templates/types/null.h | 1 + languages/cpp/templates/types/number.h | 1 + .../templates/types/object-empty-property.h | 2 + languages/cpp/templates/types/object.h | 3 + languages/cpp/templates/types/primitive.h | 1 + languages/cpp/templates/types/property.h | 1 + languages/cpp/templates/types/ref.h | 1 + languages/cpp/templates/types/string.h | 1 + languages/cpp/templates/types/title.h | 1 + languages/cpp/templates/types/tuple.h | 2 + languages/cpp/templates/types/x-method.h | 1 + languages/javascript/language.config.json | 16 +- .../templates/codeblocks/module.mjs | 4 +- .../templates/declarations/default.js | 2 +- .../templates/declarations/interface.js | 1 + .../declarations/polymorphic-reducer.js | 4 +- .../templates/declarations/synchronous.js | 6 + .../templates/interfaces/default.mjs | 1 + .../templates/interfaces/focusable.mjs | 1 + .../templates/parameters/default.js | 2 +- .../templates/parameters/optional.js | 1 + .../javascript/templates/schemas/default.js | 1 - .../javascript/templates/sections/enums.js | 1 + .../templates/sections/provider-interfaces.js | 20 +- .../javascript/templates/types/anyOf.mjs | 1 + .../javascript/templates/types/array.mjs | 1 + .../javascript/templates/types/const.mjs | 1 + .../javascript/templates/types/default.mjs | 1 + .../javascript/templates/types/default.ts | 1 + .../templates/types/enum-empty-property.mjs | 3 + languages/javascript/templates/types/enum.mjs | 2 +- languages/javascript/templates/types/enum.ts | 3 + .../javascript/templates/types/generic.mjs | 1 + .../javascript/templates/types/items.mjs | 1 + languages/javascript/templates/types/null.mjs | 1 + .../javascript/templates/types/object.mjs | 3 + .../javascript/templates/types/primitive.mjs | 1 + .../javascript/templates/types/property.mjs | 1 + languages/javascript/templates/types/ref.mjs | 1 + .../javascript/templates/types/title.mjs | 1 + .../templates/types/tuple-delimiter.mjs | 1 + .../javascript/templates/types/tuple.mjs | 3 + .../javascript/templates/types/x-method.mjs | 1 + .../templates/codeblocks/interface.md | 3 + .../templates/declarations/default.md | 1 + .../markdown/templates/interfaces/default.mjs | 1 + .../templates/interfaces/focusable.mjs | 1 + languages/markdown/templates/types/default.md | 3 - languages/markdown/templates/types/enum.md | 5 - languages/markdown/templates/types/object.md | 3 - package-lock.json | 74 +- package.json | 6 +- src/cli.mjs | 3 +- src/docs/index.mjs | 1 + src/firebolt-openrpc.json | 37 + src/macrofier/engine.mjs | 796 ++++++++++---- src/macrofier/index.mjs | 71 +- src/macrofier/types.mjs | 933 +++++++++++++++++ src/openrpc/index.mjs | 21 +- src/sdk/index.mjs | 10 +- src/shared/json-schema.mjs | 149 ++- src/shared/modules.mjs | 450 ++++++-- src/shared/typescript.mjs | 2 + src/validate/index.mjs | 23 +- src/validate/validator/index.mjs | 106 ++ 565 files changed, 11595 insertions(+), 410 deletions(-) create mode 100644 .github/workflows/merge-to-main.yml create mode 100644 .github/workflows/merge-to-next-major.yml create mode 100644 languages/c-structs/language.config.json create mode 100644 languages/c-structs/src/index.mjs create mode 100644 languages/c-structs/templates/codeblocks/export.c create mode 100644 languages/c-structs/templates/codeblocks/mock-import.c create mode 100644 languages/c-structs/templates/codeblocks/mock-parameter.c create mode 100644 languages/c-structs/templates/codeblocks/module.c create mode 100644 languages/c-structs/templates/codeblocks/setter.c create mode 100644 languages/c-structs/templates/declarations/clear.c create mode 100644 languages/c-structs/templates/declarations/default.c create mode 100644 languages/c-structs/templates/declarations/event.c create mode 100644 languages/c-structs/templates/declarations/listen.c create mode 100644 languages/c-structs/templates/declarations/once.c create mode 100644 languages/c-structs/templates/declarations/polymorphic-reducer.c create mode 100644 languages/c-structs/templates/declarations/property.c create mode 100644 languages/c-structs/templates/declarations/provide.c create mode 100644 languages/c-structs/templates/declarations/rpc-only.c create mode 100644 languages/c-structs/templates/defaults/default.c create mode 100644 languages/c-structs/templates/defaults/property.c create mode 100644 languages/c-structs/templates/imports/default.cpp create mode 100644 languages/c-structs/templates/imports/default.h create mode 100644 languages/c-structs/templates/imports/default.jsondata create mode 100644 languages/c-structs/templates/json-types/additionalProperties.c create mode 100644 languages/c-structs/templates/json-types/anyOf.c create mode 100644 languages/c-structs/templates/json-types/array.c create mode 100644 languages/c-structs/templates/json-types/boolean.c create mode 100644 languages/c-structs/templates/json-types/const.c create mode 100644 languages/c-structs/templates/json-types/default.cpp create mode 100644 languages/c-structs/templates/json-types/enum.cpp create mode 100644 languages/c-structs/templates/json-types/enum.h create mode 100644 languages/c-structs/templates/json-types/float.c create mode 100644 languages/c-structs/templates/json-types/integer.c create mode 100644 languages/c-structs/templates/json-types/namespace.c create mode 100644 languages/c-structs/templates/json-types/object.cpp create mode 100644 languages/c-structs/templates/json-types/primitive.c create mode 100644 languages/c-structs/templates/json-types/property-assign.cpp create mode 100644 languages/c-structs/templates/json-types/property-register.cpp create mode 100644 languages/c-structs/templates/json-types/property.cpp create mode 100644 languages/c-structs/templates/json-types/ref.c create mode 100644 languages/c-structs/templates/json-types/string.c create mode 100644 languages/c-structs/templates/json-types/title.c create mode 100644 languages/c-structs/templates/json-types/tuple.c create mode 100644 languages/c-structs/templates/json-types/types/object.c create mode 100644 languages/c-structs/templates/json-types/x-method.c create mode 100644 languages/c-structs/templates/language/enum-item.c create mode 100644 languages/c-structs/templates/language/enum.c create mode 100644 languages/c-structs/templates/language/parameter.c create mode 100644 languages/c-structs/templates/language/schema-item.c create mode 100644 languages/c-structs/templates/language/schema.c create mode 100644 languages/c-structs/templates/methods/clear.c create mode 100644 languages/c-structs/templates/methods/default.c create mode 100644 languages/c-structs/templates/methods/event.c create mode 100644 languages/c-structs/templates/methods/listen.c create mode 100644 languages/c-structs/templates/methods/once.c create mode 100644 languages/c-structs/templates/methods/polymorphic-pull-event.c create mode 100644 languages/c-structs/templates/methods/polymorphic-pull.c create mode 100644 languages/c-structs/templates/methods/polymorphic-reducer.c create mode 100644 languages/c-structs/templates/methods/property.c create mode 100644 languages/c-structs/templates/methods/provide.c create mode 100644 languages/c-structs/templates/methods/rpc-only.c create mode 100644 languages/c-structs/templates/methods/setter.c create mode 100644 languages/c-structs/templates/modules/include/Module.h create mode 100644 languages/c-structs/templates/modules/src/Module.cpp create mode 100644 languages/c-structs/templates/parameter-serialization/boolean.cpp create mode 100644 languages/c-structs/templates/parameter-serialization/default.cpp create mode 100644 languages/c-structs/templates/parameter-serialization/object.cpp create mode 100644 languages/c-structs/templates/parameter-serialization/primitive.cpp create mode 100644 languages/c-structs/templates/parameter-serialization/string.cpp create mode 100644 languages/c-structs/templates/parameters/default.c create mode 100644 languages/c-structs/templates/parameters/json.c create mode 100644 languages/c-structs/templates/parameters/optional.c create mode 100644 languages/c-structs/templates/parameters/result.c create mode 100644 languages/c-structs/templates/result-instantiation/boolean.cpp create mode 100644 languages/c-structs/templates/result-instantiation/default.cpp create mode 100644 languages/c-structs/templates/result-instantiation/object.cpp create mode 100644 languages/c-structs/templates/result-instantiation/primitive.cpp create mode 100644 languages/c-structs/templates/result-instantiation/string.cpp create mode 100644 languages/c-structs/templates/schemas/default.c create mode 100644 languages/c-structs/templates/schemas/include/Common/Module.h create mode 100644 languages/c-structs/templates/schemas/src/JsonData_Module.h create mode 100644 languages/c-structs/templates/schemas/src/Module_Common.cpp create mode 100644 languages/c-structs/templates/sdk/CMakeLists.txt create mode 100644 languages/c-structs/templates/sdk/cmake/HelperFunctions.cmake create mode 100644 languages/c-structs/templates/sdk/cmake/project.cmake.in create mode 100644 languages/c-structs/templates/sdk/include/Error.h create mode 100644 languages/c-structs/templates/sdk/include/Firebolt.h create mode 100644 languages/c-structs/templates/sdk/include/Types.h create mode 100755 languages/c-structs/templates/sdk/scripts/build.sh create mode 100644 languages/c-structs/templates/sdk/scripts/install.js create mode 100644 languages/c-structs/templates/sdk/src/Accessor/Accessor.cpp create mode 100644 languages/c-structs/templates/sdk/src/Accessor/Accessor.h create mode 100644 languages/c-structs/templates/sdk/src/Accessor/WorkerPool.h create mode 100644 languages/c-structs/templates/sdk/src/CMakeLists.txt create mode 100644 languages/c-structs/templates/sdk/src/Event/Event.cpp create mode 100644 languages/c-structs/templates/sdk/src/Event/Event.h create mode 100644 languages/c-structs/templates/sdk/src/Firebolt.cpp create mode 100644 languages/c-structs/templates/sdk/src/FireboltSDK.conf.in create mode 100644 languages/c-structs/templates/sdk/src/FireboltSDK.h create mode 100644 languages/c-structs/templates/sdk/src/Logger/Logger.cpp create mode 100644 languages/c-structs/templates/sdk/src/Logger/Logger.h create mode 100644 languages/c-structs/templates/sdk/src/Module.cpp create mode 100644 languages/c-structs/templates/sdk/src/Module.h create mode 100644 languages/c-structs/templates/sdk/src/Properties/Properties.h create mode 100644 languages/c-structs/templates/sdk/src/Transport/Transport.cpp create mode 100644 languages/c-structs/templates/sdk/src/Transport/Transport.h create mode 100644 languages/c-structs/templates/sdk/src/Types.cpp create mode 100644 languages/c-structs/templates/sdk/src/TypesPriv.h create mode 100644 languages/c-structs/templates/sdk/test/CMakeLists.txt create mode 100644 languages/c-structs/templates/sdk/test/Main.c create mode 100644 languages/c-structs/templates/sdk/test/Module.cpp create mode 100644 languages/c-structs/templates/sdk/test/Module.h create mode 100644 languages/c-structs/templates/sdk/test/OpenRPCCTests.h create mode 100644 languages/c-structs/templates/sdk/test/OpenRPCTests.cpp create mode 100644 languages/c-structs/templates/sdk/test/OpenRPCTests.h create mode 100644 languages/c-structs/templates/sdk/test/TestUtils.h create mode 100644 languages/c-structs/templates/sections/accessors.c create mode 100644 languages/c-structs/templates/sections/declarations.c create mode 100644 languages/c-structs/templates/sections/enums.c create mode 100644 languages/c-structs/templates/sections/events.c create mode 100644 languages/c-structs/templates/sections/methods.c create mode 100644 languages/c-structs/templates/sections/methods_accessors.c create mode 100644 languages/c-structs/templates/sections/methods_types.c create mode 100644 languages/c-structs/templates/sections/provider-interfaces.c create mode 100644 languages/c-structs/templates/sections/schemas.c create mode 100644 languages/c-structs/templates/sections/types.c create mode 100644 languages/c-structs/templates/types/anyOf.c create mode 100644 languages/c-structs/templates/types/array.c create mode 100644 languages/c-structs/templates/types/const.c create mode 100644 languages/c-structs/templates/types/default.c create mode 100644 languages/c-structs/templates/types/enum.cpp create mode 100644 languages/c-structs/templates/types/enum.h create mode 100644 languages/c-structs/templates/types/object.c create mode 100644 languages/c-structs/templates/types/primitive.c create mode 100644 languages/c-structs/templates/types/property.c create mode 100644 languages/c-structs/templates/types/ref.c create mode 100644 languages/c-structs/templates/types/title.c create mode 100644 languages/c-structs/templates/types/tuple.c create mode 100644 languages/c/templates/json-types/additionalProperties.c create mode 100644 languages/c/templates/json-types/anyOf.c create mode 100644 languages/c/templates/json-types/array.c create mode 100644 languages/c/templates/json-types/boolean.c create mode 100644 languages/c/templates/json-types/const.c create mode 100644 languages/c/templates/json-types/default.cpp create mode 100644 languages/c/templates/json-types/enum.cpp create mode 100644 languages/c/templates/json-types/enum.h create mode 100644 languages/c/templates/json-types/float.c create mode 100644 languages/c/templates/json-types/integer.c create mode 100644 languages/c/templates/json-types/namespace.c create mode 100644 languages/c/templates/json-types/object.cpp create mode 100644 languages/c/templates/json-types/primitive.c create mode 100644 languages/c/templates/json-types/property-assign.cpp create mode 100644 languages/c/templates/json-types/property-register.cpp create mode 100644 languages/c/templates/json-types/property.cpp create mode 100644 languages/c/templates/json-types/ref.c create mode 100644 languages/c/templates/json-types/string.c create mode 100644 languages/c/templates/json-types/title.c create mode 100644 languages/c/templates/json-types/tuple.c create mode 100644 languages/c/templates/json-types/types/object.c create mode 100644 languages/c/templates/json-types/x-method.c create mode 100644 languages/c/templates/language/enum-item.c create mode 100644 languages/c/templates/language/enum.c create mode 100644 languages/c/templates/language/parameter.c create mode 100644 languages/c/templates/language/schema-item.c create mode 100644 languages/c/templates/language/schema.c create mode 100644 languages/c/templates/parameter-serialization/boolean.cpp create mode 100644 languages/c/templates/parameter-serialization/default.cpp create mode 100644 languages/c/templates/parameter-serialization/object.cpp create mode 100644 languages/c/templates/parameter-serialization/primitive.cpp create mode 100644 languages/c/templates/parameter-serialization/string.cpp create mode 100644 languages/c/templates/parameters/optional.c create mode 100644 languages/c/templates/parameters/result.c create mode 100644 languages/c/templates/result-instantiation/boolean.cpp create mode 100644 languages/c/templates/result-instantiation/default.cpp create mode 100644 languages/c/templates/result-instantiation/object.cpp create mode 100644 languages/c/templates/result-instantiation/primitive.cpp create mode 100644 languages/c/templates/result-instantiation/string.cpp create mode 100644 languages/c/templates/sections/enums.c create mode 100644 languages/c/templates/types/additionalProperties.c create mode 100644 languages/c/templates/types/anyOf.c create mode 100644 languages/c/templates/types/array.c create mode 100644 languages/c/templates/types/const.c create mode 100644 languages/c/templates/types/default.c create mode 100644 languages/c/templates/types/object.c create mode 100644 languages/c/templates/types/primitive.c create mode 100644 languages/c/templates/types/property.c create mode 100644 languages/c/templates/types/ref.c create mode 100644 languages/c/templates/types/title.c create mode 100644 languages/c/templates/types/tuple.c create mode 100644 languages/c/templates/types/x-method.c create mode 100644 languages/cpp/language.config.json create mode 100644 languages/cpp/src/shared/CMakeLists.txt create mode 100644 languages/cpp/src/shared/cmake/HelperFunctions.cmake create mode 100644 languages/cpp/src/shared/cmake/project.cmake.in create mode 100644 languages/cpp/src/shared/include/error.h create mode 100644 languages/cpp/src/shared/include/types.h create mode 100644 languages/cpp/src/shared/src/Accessor/Accessor.cpp create mode 100644 languages/cpp/src/shared/src/Accessor/Accessor.h create mode 100644 languages/cpp/src/shared/src/Accessor/WorkerPool.h create mode 100644 languages/cpp/src/shared/src/Async/Async.cpp create mode 100644 languages/cpp/src/shared/src/Async/Async.h create mode 100644 languages/cpp/src/shared/src/CMakeLists.txt create mode 100644 languages/cpp/src/shared/src/Event/Event.cpp create mode 100644 languages/cpp/src/shared/src/Event/Event.h create mode 100644 languages/cpp/src/shared/src/FireboltSDK.conf.in create mode 100644 languages/cpp/src/shared/src/FireboltSDK.h create mode 100644 languages/cpp/src/shared/src/IModule.h create mode 100644 languages/cpp/src/shared/src/Logger/Logger.cpp create mode 100644 languages/cpp/src/shared/src/Logger/Logger.h create mode 100644 languages/cpp/src/shared/src/Module.cpp create mode 100644 languages/cpp/src/shared/src/Module.h create mode 100644 languages/cpp/src/shared/src/Properties/Properties.h create mode 100644 languages/cpp/src/shared/src/Transport/Transport.cpp create mode 100644 languages/cpp/src/shared/src/Transport/Transport.h create mode 100644 languages/cpp/src/shared/src/TypesPriv.h create mode 100644 languages/cpp/src/shared/test/CMakeLists.txt create mode 100644 languages/cpp/src/shared/test/Main.cpp create mode 100644 languages/cpp/src/shared/test/Module.cpp create mode 100644 languages/cpp/src/shared/test/Module.h create mode 100644 languages/cpp/src/shared/test/OpenRPCTests.cpp create mode 100644 languages/cpp/src/shared/test/OpenRPCTests.h create mode 100644 languages/cpp/src/shared/test/TestUtils.h create mode 100644 languages/cpp/templates/additional-types/boolean.cpp create mode 100644 languages/cpp/templates/additional-types/integer.cpp create mode 100644 languages/cpp/templates/additional-types/number.cpp create mode 100644 languages/cpp/templates/additional-types/string.cpp create mode 100644 languages/cpp/templates/callback-context-instantiation/generic.cpp create mode 100644 languages/cpp/templates/callback-context-instantiation/primitive.cpp create mode 100644 languages/cpp/templates/callback-context-instantiation/ref.h create mode 100644 languages/cpp/templates/callback-initialization-optional/generic.cpp create mode 100644 languages/cpp/templates/callback-initialization-optional/primitive.cpp create mode 100644 languages/cpp/templates/callback-initialization/additionalProperties.cpp create mode 100644 languages/cpp/templates/callback-initialization/anyOf.cpp create mode 100644 languages/cpp/templates/callback-initialization/array.cpp create mode 100644 languages/cpp/templates/callback-initialization/enum.cpp create mode 100644 languages/cpp/templates/callback-initialization/generic.cpp create mode 100644 languages/cpp/templates/callback-initialization/object-empty-property.cpp create mode 100644 languages/cpp/templates/callback-initialization/object.cpp create mode 100644 languages/cpp/templates/callback-initialization/primitive.cpp create mode 100644 languages/cpp/templates/callback-initialization/title.cpp create mode 100644 languages/cpp/templates/callback-instantiation/generic.cpp create mode 100644 languages/cpp/templates/callback-parameter-serialization/generic.cpp create mode 100644 languages/cpp/templates/callback-parameter-serialization/primitive.cpp create mode 100644 languages/cpp/templates/callback-parameter-serialization/ref.h create mode 100644 languages/cpp/templates/callback-response-instantiation/additionalProperties.cpp create mode 100644 languages/cpp/templates/callback-response-instantiation/generic.cpp create mode 100644 languages/cpp/templates/callback-response-instantiation/primitive.cpp create mode 100644 languages/cpp/templates/callback-response-instantiation/ref.h create mode 100644 languages/cpp/templates/callback-result-instantiation/additionalProperties.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/anyOf.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/array.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/enum.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/generic.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/namespace.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/object.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/primitive.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/property.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/ref.h create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/anyOf.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/anyOfSchemaShape.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/array.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/const.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/namespace.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/object-array.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/object-separator.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/object.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/property.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/ref.h create mode 100644 languages/cpp/templates/callback-result-instantiation/sub-property/title.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/title.cpp create mode 100644 languages/cpp/templates/callback-result-instantiation/tuple.cpp create mode 100644 languages/cpp/templates/callback-result-serialization/additionalProperties.cpp create mode 100644 languages/cpp/templates/callback-result-serialization/generic.cpp create mode 100644 languages/cpp/templates/callback-result-serialization/primitive.cpp create mode 100644 languages/cpp/templates/callback-result-serialization/ref.h create mode 100644 languages/cpp/templates/callback-serialization/generic.cpp create mode 100644 languages/cpp/templates/callback-value-initialization/generic.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/additionalProperties.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/anyOf.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/array.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/enum.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/generic.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/namespace.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/object.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/primitive.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/property.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/ref.h create mode 100644 languages/cpp/templates/callback-value-instantiation/string.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/anyOf.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/anyOfSchemaShape.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/array.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/const.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/generic.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/namespace.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/object-array.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/object-separator.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/object.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/property.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/ref.h create mode 100644 languages/cpp/templates/callback-value-instantiation/sub-property/title.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/title.cpp create mode 100644 languages/cpp/templates/callback-value-instantiation/tuple.cpp create mode 100644 languages/cpp/templates/codeblocks/interface.cpp create mode 100644 languages/cpp/templates/codeblocks/interface.h create mode 100644 languages/cpp/templates/codeblocks/module-include-private.cpp create mode 100644 languages/cpp/templates/codeblocks/module-include.h create mode 100644 languages/cpp/templates/codeblocks/module-init.cpp create mode 100644 languages/cpp/templates/codeblocks/module-init.h create mode 100644 languages/cpp/templates/codeblocks/provider-subscribe.cpp create mode 100644 languages/cpp/templates/codeblocks/provider.cpp create mode 100644 languages/cpp/templates/codeblocks/provider.h create mode 100644 languages/cpp/templates/codeblocks/setter.cpp create mode 100644 languages/cpp/templates/codeblocks/subscribe.cpp create mode 100644 languages/cpp/templates/declarations-override/allowsFocus.h create mode 100644 languages/cpp/templates/declarations-override/calls-metrics.h create mode 100644 languages/cpp/templates/declarations-override/clear.h create mode 100644 languages/cpp/templates/declarations-override/default.h create mode 100644 languages/cpp/templates/declarations-override/event.h create mode 100644 languages/cpp/templates/declarations-override/listen.h create mode 100644 languages/cpp/templates/declarations-override/once.h create mode 100644 languages/cpp/templates/declarations-override/polymorphic-pull-event.h create mode 100644 languages/cpp/templates/declarations-override/polymorphic-pull.h create mode 100644 languages/cpp/templates/declarations-override/polymorphic-reducer.h create mode 100644 languages/cpp/templates/declarations-override/property.h create mode 100644 languages/cpp/templates/declarations-override/provide.h create mode 100644 languages/cpp/templates/declarations-override/rpc-only.h create mode 100644 languages/cpp/templates/declarations-override/setter.h create mode 100644 languages/cpp/templates/declarations/allowsFocus.h create mode 100644 languages/cpp/templates/declarations/calls-metrics.h create mode 100644 languages/cpp/templates/declarations/clear.h create mode 100644 languages/cpp/templates/declarations/default.h create mode 100644 languages/cpp/templates/declarations/event.h create mode 100644 languages/cpp/templates/declarations/listen.h create mode 100644 languages/cpp/templates/declarations/once.h create mode 100644 languages/cpp/templates/declarations/polymorphic-pull-event.h create mode 100644 languages/cpp/templates/declarations/polymorphic-pull.h create mode 100644 languages/cpp/templates/declarations/polymorphic-reducer.h create mode 100644 languages/cpp/templates/declarations/property.h create mode 100644 languages/cpp/templates/declarations/provide.h create mode 100644 languages/cpp/templates/declarations/rpc-only.h create mode 100644 languages/cpp/templates/declarations/setter.h create mode 100644 languages/cpp/templates/imports/calls-metrics.cpp create mode 100644 languages/cpp/templates/imports/calls-metrics.impl create mode 100644 languages/cpp/templates/imports/default.cpp create mode 100644 languages/cpp/templates/imports/default.h create mode 100644 languages/cpp/templates/imports/default.impl create mode 100644 languages/cpp/templates/imports/default.jsondata create mode 100644 languages/cpp/templates/interfaces/default.cpp create mode 100644 languages/cpp/templates/interfaces/default.h create mode 100644 languages/cpp/templates/interfaces/focusable.cpp create mode 100644 languages/cpp/templates/interfaces/focusable.h create mode 100644 languages/cpp/templates/json-types/additionalProperties.cpp create mode 100644 languages/cpp/templates/json-types/anyOf.h create mode 100644 languages/cpp/templates/json-types/anyOfSchemaShape.h create mode 100644 languages/cpp/templates/json-types/array.h create mode 100644 languages/cpp/templates/json-types/boolean.h create mode 100644 languages/cpp/templates/json-types/const.h create mode 100644 languages/cpp/templates/json-types/default.h create mode 100644 languages/cpp/templates/json-types/enum.cpp create mode 100644 languages/cpp/templates/json-types/integer.h create mode 100644 languages/cpp/templates/json-types/namespace.h create mode 100644 languages/cpp/templates/json-types/null.h create mode 100644 languages/cpp/templates/json-types/number.h create mode 100644 languages/cpp/templates/json-types/object-empty-property.h create mode 100644 languages/cpp/templates/json-types/object.cpp create mode 100644 languages/cpp/templates/json-types/primitive.h create mode 100644 languages/cpp/templates/json-types/property-assign.cpp create mode 100644 languages/cpp/templates/json-types/property-register.cpp create mode 100644 languages/cpp/templates/json-types/property.cpp create mode 100644 languages/cpp/templates/json-types/ref.h create mode 100644 languages/cpp/templates/json-types/string.h create mode 100644 languages/cpp/templates/json-types/title.h create mode 100644 languages/cpp/templates/json-types/tuple.cpp create mode 100644 languages/cpp/templates/json-types/void.cpp create mode 100644 languages/cpp/templates/json-types/x-method.h create mode 100644 languages/cpp/templates/language/enum-item.h create mode 100644 languages/cpp/templates/language/enum.h create mode 100644 languages/cpp/templates/language/parameter.h create mode 100644 languages/cpp/templates/language/schema-item.h create mode 100644 languages/cpp/templates/language/schema.h create mode 100644 languages/cpp/templates/methods/allowsFocus.cpp create mode 100644 languages/cpp/templates/methods/calls-metrics.cpp create mode 100644 languages/cpp/templates/methods/clear.cpp create mode 100644 languages/cpp/templates/methods/default.cpp create mode 100644 languages/cpp/templates/methods/event.cpp create mode 100644 languages/cpp/templates/methods/listen.cpp create mode 100644 languages/cpp/templates/methods/once.cpp create mode 100644 languages/cpp/templates/methods/polymorphic-pull-event.cpp create mode 100644 languages/cpp/templates/methods/polymorphic-pull.cpp create mode 100644 languages/cpp/templates/methods/polymorphic-reducer.cpp create mode 100644 languages/cpp/templates/methods/property.cpp create mode 100644 languages/cpp/templates/methods/provide.cpp create mode 100644 languages/cpp/templates/methods/rpc-only.cpp create mode 100644 languages/cpp/templates/methods/setter.cpp create mode 100644 languages/cpp/templates/modules/include/module.h create mode 100644 languages/cpp/templates/modules/src/module_impl.cpp create mode 100644 languages/cpp/templates/modules/src/module_impl.h create mode 100644 languages/cpp/templates/parameter-serialization/additionalProperties.cpp create mode 100644 languages/cpp/templates/parameter-serialization/array.cpp create mode 100644 languages/cpp/templates/parameter-serialization/enum.cpp create mode 100644 languages/cpp/templates/parameter-serialization/generic.cpp create mode 100644 languages/cpp/templates/parameter-serialization/namespace.cpp create mode 100644 languages/cpp/templates/parameter-serialization/object-array.cpp create mode 100644 languages/cpp/templates/parameter-serialization/object-empty-property.cpp create mode 100644 languages/cpp/templates/parameter-serialization/object.cpp create mode 100644 languages/cpp/templates/parameter-serialization/patternProperties.cpp create mode 100644 languages/cpp/templates/parameter-serialization/primitive.cpp create mode 100644 languages/cpp/templates/parameter-serialization/property.cpp create mode 100644 languages/cpp/templates/parameter-serialization/ref.h create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/anyOf.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/anyOfSchemaShape.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/array.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/const.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/namespace.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/object-array.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/object-separator.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/object.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/property.cpp create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/ref.h create mode 100644 languages/cpp/templates/parameter-serialization/sub-property/title.cpp create mode 100644 languages/cpp/templates/parameter-serialization/title.cpp create mode 100644 languages/cpp/templates/parameters/default.h create mode 100644 languages/cpp/templates/parameters/json.cpp create mode 100644 languages/cpp/templates/parameters/nonprimitive.h create mode 100644 languages/cpp/templates/parameters/optional.h create mode 100644 languages/cpp/templates/parameters/string.h create mode 100644 languages/cpp/templates/result-callback/default.h create mode 100644 languages/cpp/templates/result-callback/nonprimitive.h create mode 100644 languages/cpp/templates/result-callback/string.h create mode 100644 languages/cpp/templates/result-initialization/additionalProperties.cpp create mode 100644 languages/cpp/templates/result-initialization/anyOf.cpp create mode 100644 languages/cpp/templates/result-initialization/array.cpp create mode 100644 languages/cpp/templates/result-initialization/boolean.cpp create mode 100644 languages/cpp/templates/result-initialization/generic.cpp create mode 100644 languages/cpp/templates/result-initialization/namespace.cpp create mode 100644 languages/cpp/templates/result-initialization/number.cpp create mode 100644 languages/cpp/templates/result-initialization/object-empty-property.cpp create mode 100644 languages/cpp/templates/result-initialization/primitive.cpp create mode 100644 languages/cpp/templates/result-initialization/ref.h create mode 100644 languages/cpp/templates/result-initialization/string.cpp create mode 100644 languages/cpp/templates/result-initialization/title.cpp create mode 100644 languages/cpp/templates/result-instantiation/additionalProperties.cpp create mode 100644 languages/cpp/templates/result-instantiation/anyOf.cpp create mode 100644 languages/cpp/templates/result-instantiation/array.cpp create mode 100644 languages/cpp/templates/result-instantiation/generic.cpp create mode 100644 languages/cpp/templates/result-instantiation/namespace.cpp create mode 100644 languages/cpp/templates/result-instantiation/object-array.cpp create mode 100644 languages/cpp/templates/result-instantiation/object-empty-property.cpp create mode 100644 languages/cpp/templates/result-instantiation/object.cpp create mode 100644 languages/cpp/templates/result-instantiation/primitive.cpp create mode 100644 languages/cpp/templates/result-instantiation/property.cpp create mode 100644 languages/cpp/templates/result-instantiation/ref.h create mode 100644 languages/cpp/templates/result-instantiation/string.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/anyOf.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/anyOfSchemaShape.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/array.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/namespace.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/object-array.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/object-separator.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/object.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/property.cpp create mode 100644 languages/cpp/templates/result-instantiation/sub-property/ref.h create mode 100644 languages/cpp/templates/result-instantiation/sub-property/title.cpp create mode 100644 languages/cpp/templates/result-instantiation/title.cpp create mode 100644 languages/cpp/templates/result-instantiation/tuple.cpp create mode 100644 languages/cpp/templates/result/default.h create mode 100644 languages/cpp/templates/schemas/default.cpp create mode 100644 languages/cpp/templates/schemas/include/common/module.h create mode 100644 languages/cpp/templates/schemas/src/jsondata_module.h create mode 100644 languages/cpp/templates/schemas/src/module_common.cpp create mode 100644 languages/cpp/templates/sdk/include/firebolt.h create mode 100755 languages/cpp/templates/sdk/scripts/build.sh create mode 100755 languages/cpp/templates/sdk/scripts/install.sh create mode 100644 languages/cpp/templates/sdk/src/firebolt.cpp create mode 100644 languages/cpp/templates/sections/declarations.h create mode 100644 languages/cpp/templates/sections/enums.h create mode 100644 languages/cpp/templates/sections/events.h create mode 100644 languages/cpp/templates/sections/methods.h create mode 100644 languages/cpp/templates/sections/methods_types.h create mode 100644 languages/cpp/templates/sections/provider-interfaces.cpp create mode 100644 languages/cpp/templates/sections/provider-interfaces.h create mode 100644 languages/cpp/templates/sections/provider-subscribe.cpp create mode 100644 languages/cpp/templates/sections/schemas.h create mode 100644 languages/cpp/templates/sections/types.h create mode 100644 languages/cpp/templates/sections/xuses-interfaces.h create mode 100644 languages/cpp/templates/types/additionalProperties.h create mode 100644 languages/cpp/templates/types/additionalPropertiesKey.h create mode 100644 languages/cpp/templates/types/anyOf.h create mode 100644 languages/cpp/templates/types/anyOfSchemaShape.h create mode 100644 languages/cpp/templates/types/array.h create mode 100644 languages/cpp/templates/types/boolean.h create mode 100644 languages/cpp/templates/types/const.h create mode 100644 languages/cpp/templates/types/default.h create mode 100644 languages/cpp/templates/types/enum.cpp create mode 100644 languages/cpp/templates/types/enum.h create mode 100644 languages/cpp/templates/types/integer.h create mode 100644 languages/cpp/templates/types/items.h create mode 100644 languages/cpp/templates/types/namespace.h create mode 100644 languages/cpp/templates/types/null.h create mode 100644 languages/cpp/templates/types/number.h create mode 100644 languages/cpp/templates/types/object-empty-property.h create mode 100644 languages/cpp/templates/types/object.h create mode 100644 languages/cpp/templates/types/primitive.h create mode 100644 languages/cpp/templates/types/property.h create mode 100644 languages/cpp/templates/types/ref.h create mode 100644 languages/cpp/templates/types/string.h create mode 100644 languages/cpp/templates/types/title.h create mode 100644 languages/cpp/templates/types/tuple.h create mode 100644 languages/cpp/templates/types/x-method.h create mode 100644 languages/javascript/templates/declarations/interface.js create mode 100644 languages/javascript/templates/declarations/synchronous.js create mode 100644 languages/javascript/templates/interfaces/default.mjs create mode 100644 languages/javascript/templates/interfaces/focusable.mjs create mode 100644 languages/javascript/templates/parameters/optional.js create mode 100644 languages/javascript/templates/sections/enums.js create mode 100644 languages/javascript/templates/types/anyOf.mjs create mode 100644 languages/javascript/templates/types/array.mjs create mode 100644 languages/javascript/templates/types/const.mjs create mode 100644 languages/javascript/templates/types/default.mjs create mode 100644 languages/javascript/templates/types/default.ts create mode 100644 languages/javascript/templates/types/enum-empty-property.mjs create mode 100644 languages/javascript/templates/types/enum.ts create mode 100644 languages/javascript/templates/types/generic.mjs create mode 100644 languages/javascript/templates/types/items.mjs create mode 100644 languages/javascript/templates/types/null.mjs create mode 100644 languages/javascript/templates/types/object.mjs create mode 100644 languages/javascript/templates/types/primitive.mjs create mode 100644 languages/javascript/templates/types/property.mjs create mode 100644 languages/javascript/templates/types/ref.mjs create mode 100644 languages/javascript/templates/types/title.mjs create mode 100644 languages/javascript/templates/types/tuple-delimiter.mjs create mode 100644 languages/javascript/templates/types/tuple.mjs create mode 100644 languages/javascript/templates/types/x-method.mjs create mode 100644 languages/markdown/templates/codeblocks/interface.md create mode 100644 languages/markdown/templates/declarations/default.md create mode 100644 languages/markdown/templates/interfaces/default.mjs create mode 100644 languages/markdown/templates/interfaces/focusable.mjs delete mode 100644 languages/markdown/templates/types/default.md delete mode 100644 languages/markdown/templates/types/enum.md delete mode 100644 languages/markdown/templates/types/object.md create mode 100644 src/macrofier/types.mjs diff --git a/.github/workflows/merge-to-main.yml b/.github/workflows/merge-to-main.yml new file mode 100644 index 00000000..6f2e3e73 --- /dev/null +++ b/.github/workflows/merge-to-main.yml @@ -0,0 +1,43 @@ +name: Attempt to merge next to main +on: + workflow_dispatch: + +jobs: + # Check if next can merge into main + perform_merge: + name: Perform merge if "next" can merge into "main" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + repo-token: ${{ secrets.SEMANTIC_RELEASE_BOT_PAT }} + persist-credentials: true + + # Set user identity + - name: Set-Identity + run: | + git config --global user.email "${{ secrets.GLOBAL_GITHUB_EMAIL }}" + git config --global user.name "${{ secrets.GLOBAL_GITHUB_USER }}" + + # Checkout "main" + - name: Checkout main + run: git checkout "main" + + - name: Perform the merge from next to main + run: | + git merge next + git push origin "main" + echo "Push to main succeeded" + + # If the merge cannot be performed, let stakeholders know + message_on_failure: + name: Merge failure + needs: perform_merge + runs-on: ubuntu-latest + if: ${{ failure() }} + + steps: + - name: Post error message (To-Do) + run: echo "Next cannot be merged into main cleanly" \ No newline at end of file diff --git a/.github/workflows/merge-to-next-major.yml b/.github/workflows/merge-to-next-major.yml new file mode 100644 index 00000000..3f03b6b2 --- /dev/null +++ b/.github/workflows/merge-to-next-major.yml @@ -0,0 +1,72 @@ +name: Attempt to merge next to next-major +on: + workflow_dispatch: + push: + branches: + - "next" + +jobs: + # Check if next can merge into next-major + perform_merge: + name: Perform merge if "next" can merge into "next-major" + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + with: + fetch-depth: 0 + repo-token: ${{ secrets.SEMANTIC_RELEASE_BOT_PAT }} + persist-credentials: true + + # Set user identity + - name: Set-Identity + run: | + git config --global user.email "${{ secrets.GLOBAL_GITHUB_EMAIL }}" + git config --global user.name "${{ secrets.GLOBAL_GITHUB_USER }}" + + # Checkout "next-major" + - name: Checkout next-major + run: git checkout "next-major" + + # Get the "next-major" version number + - name: Extract next-major version + id: extract_version + run: echo "::set-output name=version::$(node -e 'console.log(require("./package.json").version)')" + + # Checkout "next" + - name: Checkout next + run: git checkout "next" + + # Update "next" version to match "next-major" + - name: Update "next" version to match "next-major" + run: | + jq '.version = "${{ steps.extract_version.outputs.version }}"' package.json > temp.json + + if diff -q "package.json" "temp.json" >/dev/null; then + echo "Versions are identical. No change required." + rm temp.json + else + mv temp.json package.json + git add package.json && git commit -m "Sync version to ${{ steps.extract_version.outputs.version }}" + fi + + # Checkout "next-major" + - name: Checkout next-major + run: git checkout "next-major" + + - name: Perform the merge from next to next-major + run: | + git merge next + git push origin "next-major" + echo "Push to next-major succeeded" + + # If the merge cannot be performed, let stakeholders know + message_on_failure: + name: Merge failure + needs: perform_merge + runs-on: ubuntu-latest + if: ${{ failure() }} + + steps: + - name: Post error message (To-Do) + run: echo "Next cannot be merged into next-major cleanly" \ No newline at end of file diff --git a/.gitignore b/.gitignore index 5053bbc0..1b1bb730 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules/* .DS_Store .DS_Store coverage +.vscode/settings.json diff --git a/CHANGELOG.md b/CHANGELOG.md index b59dbe54..8f8f8dae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,55 @@ +# [3.0.0-next.5](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.4...v3.0.0-next.5) (2024-06-13) + + +### Bug Fixes + +* Resolve conflicts ([adf6762](https://github.com/rdkcentral/firebolt-openrpc/commit/adf6762c02e1ccd6e7cd708d5249630252bd7c44)) + +# [3.0.0-next.4](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.3...v3.0.0-next.4) (2024-06-06) + + +### Features + +* App Pass-through ([#188](https://github.com/rdkcentral/firebolt-openrpc/issues/188)) ([83fa0fc](https://github.com/rdkcentral/firebolt-openrpc/commit/83fa0fcb62cade7ed3b527de6b3fe1ebb42b54e0)) + +# [3.0.0-next.3](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.2...v3.0.0-next.3) (2024-05-09) # [2.3.0](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.2.0...v2.3.0) (2024-02-08) ### Bug Fixes +* Account for new OpenRPC spec URL contents ([#186](https://github.com/rdkcentral/firebolt-openrpc/issues/186)) ([66bd6ac](https://github.com/rdkcentral/firebolt-openrpc/commit/66bd6acc1ffdaefadcb46d78864d615a116b5a85)) + +# [3.0.0-next.2](https://github.com/rdkcentral/firebolt-openrpc/compare/v3.0.0-next.1...v3.0.0-next.2) (2024-04-08) * Insert 'v' in front of version enum names ([d8b9ada](https://github.com/rdkcentral/firebolt-openrpc/commit/d8b9ada1b624df29821b74679291d36167208470)) * Proper param handling for x-subscriber-type: global ([02204e5](https://github.com/rdkcentral/firebolt-openrpc/commit/02204e5f93d27a21086b4a9bbe5f586f969354f8)) ### Features +* CI/CD Merging from next ([#183](https://github.com/rdkcentral/firebolt-openrpc/issues/183)) ([14476a9](https://github.com/rdkcentral/firebolt-openrpc/commit/14476a908f2c10d907417187c41ce255ac2ea6a6)) + +# [3.0.0-next.1](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.3.0-next.1...v3.0.0-next.1) (2024-03-26) + + +* Integration of CPPSDK support (#176) ([89294cc](https://github.com/rdkcentral/firebolt-openrpc/commit/89294cc37f23a94012621130858e01c946a3a9a6)), closes [#176](https://github.com/rdkcentral/firebolt-openrpc/issues/176) [#90](https://github.com/rdkcentral/firebolt-openrpc/issues/90) [#93](https://github.com/rdkcentral/firebolt-openrpc/issues/93) [#94](https://github.com/rdkcentral/firebolt-openrpc/issues/94) [#96](https://github.com/rdkcentral/firebolt-openrpc/issues/96) [#97](https://github.com/rdkcentral/firebolt-openrpc/issues/97) [#100](https://github.com/rdkcentral/firebolt-openrpc/issues/100) [#99](https://github.com/rdkcentral/firebolt-openrpc/issues/99) [#101](https://github.com/rdkcentral/firebolt-openrpc/issues/101) [#102](https://github.com/rdkcentral/firebolt-openrpc/issues/102) [#107](https://github.com/rdkcentral/firebolt-openrpc/issues/107) [#106](https://github.com/rdkcentral/firebolt-openrpc/issues/106) [#105](https://github.com/rdkcentral/firebolt-openrpc/issues/105) [#103](https://github.com/rdkcentral/firebolt-openrpc/issues/103) [#109](https://github.com/rdkcentral/firebolt-openrpc/issues/109) [#112](https://github.com/rdkcentral/firebolt-openrpc/issues/112) [#111](https://github.com/rdkcentral/firebolt-openrpc/issues/111) [#113](https://github.com/rdkcentral/firebolt-openrpc/issues/113) [#114](https://github.com/rdkcentral/firebolt-openrpc/issues/114) [#115](https://github.com/rdkcentral/firebolt-openrpc/issues/115) [#110](https://github.com/rdkcentral/firebolt-openrpc/issues/110) [#90](https://github.com/rdkcentral/firebolt-openrpc/issues/90) [#93](https://github.com/rdkcentral/firebolt-openrpc/issues/93) [#94](https://github.com/rdkcentral/firebolt-openrpc/issues/94) [#96](https://github.com/rdkcentral/firebolt-openrpc/issues/96) [#97](https://github.com/rdkcentral/firebolt-openrpc/issues/97) [#100](https://github.com/rdkcentral/firebolt-openrpc/issues/100) [#99](https://github.com/rdkcentral/firebolt-openrpc/issues/99) [#101](https://github.com/rdkcentral/firebolt-openrpc/issues/101) [#102](https://github.com/rdkcentral/firebolt-openrpc/issues/102) [#107](https://github.com/rdkcentral/firebolt-openrpc/issues/107) [#106](https://github.com/rdkcentral/firebolt-openrpc/issues/106) [#105](https://github.com/rdkcentral/firebolt-openrpc/issues/105) [#103](https://github.com/rdkcentral/firebolt-openrpc/issues/103) [#109](https://github.com/rdkcentral/firebolt-openrpc/issues/109) [#112](https://github.com/rdkcentral/firebolt-openrpc/issues/112) [#111](https://github.com/rdkcentral/firebolt-openrpc/issues/111) [#113](https://github.com/rdkcentral/firebolt-openrpc/issues/113) [#114](https://github.com/rdkcentral/firebolt-openrpc/issues/114) [#115](https://github.com/rdkcentral/firebolt-openrpc/issues/115) [#110](https://github.com/rdkcentral/firebolt-openrpc/issues/110) [#117](https://github.com/rdkcentral/firebolt-openrpc/issues/117) [#116](https://github.com/rdkcentral/firebolt-openrpc/issues/116) [#118](https://github.com/rdkcentral/firebolt-openrpc/issues/118) [#121](https://github.com/rdkcentral/firebolt-openrpc/issues/121) [#119](https://github.com/rdkcentral/firebolt-openrpc/issues/119) [#90](https://github.com/rdkcentral/firebolt-openrpc/issues/90) [#93](https://github.com/rdkcentral/firebolt-openrpc/issues/93) [#94](https://github.com/rdkcentral/firebolt-openrpc/issues/94) [#96](https://github.com/rdkcentral/firebolt-openrpc/issues/96) [#97](https://github.com/rdkcentral/firebolt-openrpc/issues/97) [#100](https://github.com/rdkcentral/firebolt-openrpc/issues/100) [#99](https://github.com/rdkcentral/firebolt-openrpc/issues/99) [#101](https://github.com/rdkcentral/firebolt-openrpc/issues/101) [#102](https://github.com/rdkcentral/firebolt-openrpc/issues/102) [#107](https://github.com/rdkcentral/firebolt-openrpc/issues/107) [#106](https://github.com/rdkcentral/firebolt-openrpc/issues/106) [#105](https://github.com/rdkcentral/firebolt-openrpc/issues/105) [#103](https://github.com/rdkcentral/firebolt-openrpc/issues/103) [#109](https://github.com/rdkcentral/firebolt-openrpc/issues/109) [#112](https://github.com/rdkcentral/firebolt-openrpc/issues/112) [#111](https://github.com/rdkcentral/firebolt-openrpc/issues/111) [#113](https://github.com/rdkcentral/firebolt-openrpc/issues/113) [#114](https://github.com/rdkcentral/firebolt-openrpc/issues/114) [#115](https://github.com/rdkcentral/firebolt-openrpc/issues/115) [#110](https://github.com/rdkcentral/firebolt-openrpc/issues/110) [#117](https://github.com/rdkcentral/firebolt-openrpc/issues/117) [#116](https://github.com/rdkcentral/firebolt-openrpc/issues/116) [#118](https://github.com/rdkcentral/firebolt-openrpc/issues/118) [#121](https://github.com/rdkcentral/firebolt-openrpc/issues/121) [#119](https://github.com/rdkcentral/firebolt-openrpc/issues/119) [#127](https://github.com/rdkcentral/firebolt-openrpc/issues/127) [#137](https://github.com/rdkcentral/firebolt-openrpc/issues/137) [#135](https://github.com/rdkcentral/firebolt-openrpc/issues/135) [#134](https://github.com/rdkcentral/firebolt-openrpc/issues/134) [#136](https://github.com/rdkcentral/firebolt-openrpc/issues/136) [#139](https://github.com/rdkcentral/firebolt-openrpc/issues/139) [#138](https://github.com/rdkcentral/firebolt-openrpc/issues/138) [#140](https://github.com/rdkcentral/firebolt-openrpc/issues/140) [#142](https://github.com/rdkcentral/firebolt-openrpc/issues/142) [#144](https://github.com/rdkcentral/firebolt-openrpc/issues/144) [#145](https://github.com/rdkcentral/firebolt-openrpc/issues/145) [#141](https://github.com/rdkcentral/firebolt-openrpc/issues/141) [#149](https://github.com/rdkcentral/firebolt-openrpc/issues/149) [#150](https://github.com/rdkcentral/firebolt-openrpc/issues/150) [#152](https://github.com/rdkcentral/firebolt-openrpc/issues/152) [#151](https://github.com/rdkcentral/firebolt-openrpc/issues/151) [#153](https://github.com/rdkcentral/firebolt-openrpc/issues/153) [#154](https://github.com/rdkcentral/firebolt-openrpc/issues/154) [#156](https://github.com/rdkcentral/firebolt-openrpc/issues/156) [#157](https://github.com/rdkcentral/firebolt-openrpc/issues/157) [#158](https://github.com/rdkcentral/firebolt-openrpc/issues/158) [#160](https://github.com/rdkcentral/firebolt-openrpc/issues/160) [#159](https://github.com/rdkcentral/firebolt-openrpc/issues/159) [#161](https://github.com/rdkcentral/firebolt-openrpc/issues/161) [#163](https://github.com/rdkcentral/firebolt-openrpc/issues/163) [#164](https://github.com/rdkcentral/firebolt-openrpc/issues/164) [#165](https://github.com/rdkcentral/firebolt-openrpc/issues/165) [#167](https://github.com/rdkcentral/firebolt-openrpc/issues/167) [#169](https://github.com/rdkcentral/firebolt-openrpc/issues/169) [#171](https://github.com/rdkcentral/firebolt-openrpc/issues/171) [#173](https://github.com/rdkcentral/firebolt-openrpc/issues/173) [#172](https://github.com/rdkcentral/firebolt-openrpc/issues/172) [#170](https://github.com/rdkcentral/firebolt-openrpc/issues/170) + + +### BREAKING CHANGES + +* Generalized templating engine to support both JavaScript and CPP (and other languages). + +* chore: Copy JS templates to C and create language.config.json + +* feat(languages): Add support for a distinct JSON-type for each schema + +Also started tweaking C templates and adding a few useful macros. + +* chore: Dropped debug logs + +* fix: Stop passing non-schemas in to getSchemaType + +* feat(accessors): New macro section for schema property accessors * Support for context-free property subscribers ([9809273](https://github.com/rdkcentral/firebolt-openrpc/commit/980927309fa6efc7b03a490aa5fd7909f39ff4de)) # [2.3.0-next.1](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.2.0...v2.3.0-next.1) (2024-01-12) diff --git a/languages/c-structs/language.config.json b/languages/c-structs/language.config.json new file mode 100644 index 00000000..1179d74e --- /dev/null +++ b/languages/c-structs/language.config.json @@ -0,0 +1,27 @@ +{ + "name": "C", + "langcode": "c", + "createModuleDirectories": false, + "extractSubSchemas": true, + "unwrapResultObjects": true, + "convertTuplesToArraysOrObjects": true, + "templatesPerModule": [ + "/include/module.h", + "/src/module.cpp" + ], + "templatesPerSchema": [ + "/include/common/module.h", + "/src/module_common.cpp", + "/src/jsondata_module.h" + ], + "persistPermission": true, + "primitives": { + "boolean": "bool", + "integer": "int", + "number": "float", + "string": "char*" + }, + "additionalSchemaTemplates": [ + "json-types" + ] +} diff --git a/languages/c-structs/src/index.mjs b/languages/c-structs/src/index.mjs new file mode 100644 index 00000000..51c547a1 --- /dev/null +++ b/languages/c-structs/src/index.mjs @@ -0,0 +1,19 @@ +/* + * Copyright 2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +export { default as Transport } from './shared/Transport/index.mjs' \ No newline at end of file diff --git a/languages/c-structs/templates/codeblocks/export.c b/languages/c-structs/templates/codeblocks/export.c new file mode 100644 index 00000000..c98498e4 --- /dev/null +++ b/languages/c-structs/templates/codeblocks/export.c @@ -0,0 +1 @@ +export { default as ${info.title} } from './${info.title}/index.mjs' \ No newline at end of file diff --git a/languages/c-structs/templates/codeblocks/mock-import.c b/languages/c-structs/templates/codeblocks/mock-import.c new file mode 100644 index 00000000..5d22512a --- /dev/null +++ b/languages/c-structs/templates/codeblocks/mock-import.c @@ -0,0 +1 @@ +import { default as _${info.title} } from './${info.title}/defaults.mjs' \ No newline at end of file diff --git a/languages/c-structs/templates/codeblocks/mock-parameter.c b/languages/c-structs/templates/codeblocks/mock-parameter.c new file mode 100644 index 00000000..63e63902 --- /dev/null +++ b/languages/c-structs/templates/codeblocks/mock-parameter.c @@ -0,0 +1 @@ + ${info.title}: _${info.title}, \ No newline at end of file diff --git a/languages/c-structs/templates/codeblocks/module.c b/languages/c-structs/templates/codeblocks/module.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/codeblocks/setter.c b/languages/c-structs/templates/codeblocks/setter.c new file mode 100644 index 00000000..9f496543 --- /dev/null +++ b/languages/c-structs/templates/codeblocks/setter.c @@ -0,0 +1,9 @@ +/* ${method.name} - ${method.description} */ +uint32_t ${info.title}_${method.Name}( ${method.signature.params} ) +{ + const string method = _T("${info.title}.${method.name}"); + ${if.params} +${method.params.serialization} + ${end.if.params} + return FireboltSDK::Properties::Set(method, jsonParameters); +} diff --git a/languages/c-structs/templates/declarations/clear.c b/languages/c-structs/templates/declarations/clear.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/declarations/default.c b/languages/c-structs/templates/declarations/default.c new file mode 100644 index 00000000..5d972e4a --- /dev/null +++ b/languages/c-structs/templates/declarations/default.c @@ -0,0 +1,5 @@ +/* + * ${method.summary} + * ${method.params} + */ +int F${info.title}_${method.Name}(${method.signature.params}${if.result.properties}${if.params}, ${end.if.params}${end.if.result.properties}${method.result.properties}); diff --git a/languages/c-structs/templates/declarations/event.c b/languages/c-structs/templates/declarations/event.c new file mode 100644 index 00000000..c2f0e3d2 --- /dev/null +++ b/languages/c-structs/templates/declarations/event.c @@ -0,0 +1,4 @@ +/* ${method.name} - ${method.description} */ +typedef void (*F${info.Title}${method.Name}Callback)( const void* userData, ${event.signature.callback.params}${if.event.params}, ${end.if.event.params}${method.result.properties} ); +int F${info.title}_Register_${method.Name}( ${event.signature.params}${if.event.params}, ${end.if.event.params}F${info.Title}${method.Name}Callback userCB, const void* userData ); +int F${info.title}_Unregister_${method.Name}( F${info.Title}${method.Name}Callback userCB); diff --git a/languages/c-structs/templates/declarations/listen.c b/languages/c-structs/templates/declarations/listen.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/declarations/once.c b/languages/c-structs/templates/declarations/once.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/declarations/polymorphic-reducer.c b/languages/c-structs/templates/declarations/polymorphic-reducer.c new file mode 100644 index 00000000..5d972e4a --- /dev/null +++ b/languages/c-structs/templates/declarations/polymorphic-reducer.c @@ -0,0 +1,5 @@ +/* + * ${method.summary} + * ${method.params} + */ +int F${info.title}_${method.Name}(${method.signature.params}${if.result.properties}${if.params}, ${end.if.params}${end.if.result.properties}${method.result.properties}); diff --git a/languages/c-structs/templates/declarations/property.c b/languages/c-structs/templates/declarations/property.c new file mode 100644 index 00000000..77aac221 --- /dev/null +++ b/languages/c-structs/templates/declarations/property.c @@ -0,0 +1,5 @@ +/* + * ${method.summary} + * ${method.params} + */ +int F${info.title}_Get${method.Name}(${method.signature.params}${if.params}, ${end.if.params}${method.result.properties}); diff --git a/languages/c-structs/templates/declarations/provide.c b/languages/c-structs/templates/declarations/provide.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/declarations/rpc-only.c b/languages/c-structs/templates/declarations/rpc-only.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/defaults/default.c b/languages/c-structs/templates/defaults/default.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/defaults/property.c b/languages/c-structs/templates/defaults/property.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/imports/default.cpp b/languages/c-structs/templates/imports/default.cpp new file mode 100644 index 00000000..caf84bc9 --- /dev/null +++ b/languages/c-structs/templates/imports/default.cpp @@ -0,0 +1 @@ +#include "jsondata_${info.title.lowercase}.h" diff --git a/languages/c-structs/templates/imports/default.h b/languages/c-structs/templates/imports/default.h new file mode 100644 index 00000000..56a97a40 --- /dev/null +++ b/languages/c-structs/templates/imports/default.h @@ -0,0 +1 @@ +#include "common/${info.title.lowercase}.h" diff --git a/languages/c-structs/templates/imports/default.jsondata b/languages/c-structs/templates/imports/default.jsondata new file mode 100644 index 00000000..caf84bc9 --- /dev/null +++ b/languages/c-structs/templates/imports/default.jsondata @@ -0,0 +1 @@ +#include "jsondata_${info.title.lowercase}.h" diff --git a/languages/c-structs/templates/json-types/additionalProperties.c b/languages/c-structs/templates/json-types/additionalProperties.c new file mode 100644 index 00000000..b814e5fb --- /dev/null +++ b/languages/c-structs/templates/json-types/additionalProperties.c @@ -0,0 +1 @@ +// need cpp code to init, get, set, clear additional properties... \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/anyOf.c b/languages/c-structs/templates/json-types/anyOf.c new file mode 100644 index 00000000..a2682179 --- /dev/null +++ b/languages/c-structs/templates/json-types/anyOf.c @@ -0,0 +1 @@ +/* AnyOf is not supported in C: ${title} */ \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/array.c b/languages/c-structs/templates/json-types/array.c new file mode 100644 index 00000000..508a9eac --- /dev/null +++ b/languages/c-structs/templates/json-types/array.c @@ -0,0 +1,4 @@ +uint32_t ${info.Title}_${Title}Array_Size(${type} handle); +${type} ${title}Array_Get(${type} handle, uint32_t index); +void ${info.Title}_${Title}Array_Add(${propertyType} handle, ${valueType} value); +void ${info.Title}_${Title}Array_Clear(${propertyType} handle); diff --git a/languages/c-structs/templates/json-types/boolean.c b/languages/c-structs/templates/json-types/boolean.c new file mode 100644 index 00000000..18bbeec2 --- /dev/null +++ b/languages/c-structs/templates/json-types/boolean.c @@ -0,0 +1 @@ +WPEFramework::Core::JSON::Boolean \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/const.c b/languages/c-structs/templates/json-types/const.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/json-types/default.cpp b/languages/c-structs/templates/json-types/default.cpp new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/c-structs/templates/json-types/default.cpp @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/enum.cpp b/languages/c-structs/templates/json-types/enum.cpp new file mode 100644 index 00000000..6776b41a --- /dev/null +++ b/languages/c-structs/templates/json-types/enum.cpp @@ -0,0 +1,4 @@ + /* ${title} ${description} */ + ENUM_CONVERSION_BEGIN(${name}) + { ${NAME}_${key}, _T("${value}") }, + ENUM_CONVERSION_END(${name}) diff --git a/languages/c-structs/templates/json-types/enum.h b/languages/c-structs/templates/json-types/enum.h new file mode 100644 index 00000000..92108c75 --- /dev/null +++ b/languages/c-structs/templates/json-types/enum.h @@ -0,0 +1,4 @@ +/* ${title} ${description} */ +typedef enum { + ${NAME}_${key}, +} ${name}; diff --git a/languages/c-structs/templates/json-types/float.c b/languages/c-structs/templates/json-types/float.c new file mode 100644 index 00000000..c38bca91 --- /dev/null +++ b/languages/c-structs/templates/json-types/float.c @@ -0,0 +1 @@ +WPEFramework::Core::JSON::Float \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/integer.c b/languages/c-structs/templates/json-types/integer.c new file mode 100644 index 00000000..b57fe26e --- /dev/null +++ b/languages/c-structs/templates/json-types/integer.c @@ -0,0 +1 @@ +WPEFramework::Core::JSON::DecSInt32 \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/namespace.c b/languages/c-structs/templates/json-types/namespace.c new file mode 100644 index 00000000..8ea9d7de --- /dev/null +++ b/languages/c-structs/templates/json-types/namespace.c @@ -0,0 +1 @@ +FireboltSDK::${info.Title}:: \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/object.cpp b/languages/c-structs/templates/json-types/object.cpp new file mode 100644 index 00000000..db24d12c --- /dev/null +++ b/languages/c-structs/templates/json-types/object.cpp @@ -0,0 +1,25 @@ + class ${title}: public WPEFramework::Core::JSON::Container { + public: + ~${title}() override = default; + + public: + ${title}() + : WPEFramework::Core::JSON::Container() + { + ${properties.register} + } + + ${title}(const ${title}& other) + { + ${properties.assign} + } + + ${title}& operator=(const ${title}& other) + { + ${properties.assign} + return (*this); + } + + public: + ${properties} + }; diff --git a/languages/c-structs/templates/json-types/primitive.c b/languages/c-structs/templates/json-types/primitive.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/json-types/property-assign.cpp b/languages/c-structs/templates/json-types/property-assign.cpp new file mode 100644 index 00000000..cc1b2860 --- /dev/null +++ b/languages/c-structs/templates/json-types/property-assign.cpp @@ -0,0 +1,2 @@ +Add(_T("${property}"), &${Property}); +${Property} = other.${Property} \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/property-register.cpp b/languages/c-structs/templates/json-types/property-register.cpp new file mode 100644 index 00000000..f9b86372 --- /dev/null +++ b/languages/c-structs/templates/json-types/property-register.cpp @@ -0,0 +1 @@ +Add(_T("${property}"), &${Property}); \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/property.cpp b/languages/c-structs/templates/json-types/property.cpp new file mode 100644 index 00000000..46e3ef8e --- /dev/null +++ b/languages/c-structs/templates/json-types/property.cpp @@ -0,0 +1 @@ +${title} ${Property}; \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/ref.c b/languages/c-structs/templates/json-types/ref.c new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/c-structs/templates/json-types/ref.c @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/string.c b/languages/c-structs/templates/json-types/string.c new file mode 100644 index 00000000..2d60a3c8 --- /dev/null +++ b/languages/c-structs/templates/json-types/string.c @@ -0,0 +1 @@ +FireboltSDK::JSON::String \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/title.c b/languages/c-structs/templates/json-types/title.c new file mode 100644 index 00000000..ae1512e5 --- /dev/null +++ b/languages/c-structs/templates/json-types/title.c @@ -0,0 +1 @@ +JsonData_${Title} \ No newline at end of file diff --git a/languages/c-structs/templates/json-types/tuple.c b/languages/c-structs/templates/json-types/tuple.c new file mode 100644 index 00000000..db24d12c --- /dev/null +++ b/languages/c-structs/templates/json-types/tuple.c @@ -0,0 +1,25 @@ + class ${title}: public WPEFramework::Core::JSON::Container { + public: + ~${title}() override = default; + + public: + ${title}() + : WPEFramework::Core::JSON::Container() + { + ${properties.register} + } + + ${title}(const ${title}& other) + { + ${properties.assign} + } + + ${title}& operator=(const ${title}& other) + { + ${properties.assign} + return (*this); + } + + public: + ${properties} + }; diff --git a/languages/c-structs/templates/json-types/types/object.c b/languages/c-structs/templates/json-types/types/object.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/json-types/x-method.c b/languages/c-structs/templates/json-types/x-method.c new file mode 100644 index 00000000..573c9f8e --- /dev/null +++ b/languages/c-structs/templates/json-types/x-method.c @@ -0,0 +1 @@ +void* \ No newline at end of file diff --git a/languages/c-structs/templates/language/enum-item.c b/languages/c-structs/templates/language/enum-item.c new file mode 100644 index 00000000..79aabbeb --- /dev/null +++ b/languages/c-structs/templates/language/enum-item.c @@ -0,0 +1 @@ + ${key} diff --git a/languages/c-structs/templates/language/enum.c b/languages/c-structs/templates/language/enum.c new file mode 100644 index 00000000..0551b99c --- /dev/null +++ b/languages/c-structs/templates/language/enum.c @@ -0,0 +1,3 @@ +typedef enum { + ${items} +} F${info.title}_${title}; diff --git a/languages/c-structs/templates/language/parameter.c b/languages/c-structs/templates/language/parameter.c new file mode 100644 index 00000000..2ff7a678 --- /dev/null +++ b/languages/c-structs/templates/language/parameter.c @@ -0,0 +1 @@ +${type} ${name} \ No newline at end of file diff --git a/languages/c-structs/templates/language/schema-item.c b/languages/c-structs/templates/language/schema-item.c new file mode 100644 index 00000000..ecded7d5 --- /dev/null +++ b/languages/c-structs/templates/language/schema-item.c @@ -0,0 +1 @@ + ${type} ${property}; diff --git a/languages/c-structs/templates/language/schema.c b/languages/c-structs/templates/language/schema.c new file mode 100644 index 00000000..22760540 --- /dev/null +++ b/languages/c-structs/templates/language/schema.c @@ -0,0 +1,3 @@ +typedef struct { + ${properties} +} F${info.title}_${title}; diff --git a/languages/c-structs/templates/methods/clear.c b/languages/c-structs/templates/methods/clear.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/methods/default.c b/languages/c-structs/templates/methods/default.c new file mode 100644 index 00000000..3707e68b --- /dev/null +++ b/languages/c-structs/templates/methods/default.c @@ -0,0 +1,24 @@ +/* ${method.name} - ${method.description} */ +int F${info.title}_${method.Name}(${method.signature.params}${if.result.properties}${if.params}, ${end.if.params}${end.if.result.properties}${method.result.properties}) { + int status = FireboltSDKErrorUnavailable; + FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); + if (transport != nullptr) { + + JsonObject jsonParameters; + + ${if.params} +${method.params.json} + ${end.if.params} + + WPEFramework::Core::JSON::Boolean jsonResult; + status = transport->Invoke("${info.title}.${method.name}", jsonParameters, jsonResult); + if (status == FireboltSDKErrorNone) { + *success = jsonResult.Value(); + } + + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; +} diff --git a/languages/c-structs/templates/methods/event.c b/languages/c-structs/templates/methods/event.c new file mode 100644 index 00000000..6e0e966a --- /dev/null +++ b/languages/c-structs/templates/methods/event.c @@ -0,0 +1,26 @@ +/* ${method.name} - ${method.description} */ +static void F${info.Title}${method.Name}InnerCallback( void* userCB, const void* userData, void* response ) +{ +${event.callback.params.serialization} + ASSERT(jsonResponse->IsValid() == true); + if (jsonResponse->IsValid() == true) { +${event.callback.result.instantiation} + ${info.Title}${method.Name}Callback callback = reinterpret_cast<${info.Title}${method.Name}Callback>(userCB); + callback(userData, ${event.callback.response.instantiation}); + } +} +int F${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}"); + int status = FireboltSDKErrorNone; + + if (userCB != nullptr) { + ${event.params.serialization} + status = FireboltSDK::Event::Instance().Subscribe<${event.result.json.type}>(eventName, jsonParameters, ${info.Title}${method.Name}InnerCallback, reinterpret_cast(userCB), userData); + } + return status; +} +int F${info.title}_Unregister_${method.Name}( ${info.Title}${method.Name}Callback userCB) +{ + return FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title}.${method.name}"), reinterpret_cast(userCB)); +} diff --git a/languages/c-structs/templates/methods/listen.c b/languages/c-structs/templates/methods/listen.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/methods/once.c b/languages/c-structs/templates/methods/once.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/methods/polymorphic-pull-event.c b/languages/c-structs/templates/methods/polymorphic-pull-event.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/methods/polymorphic-pull.c b/languages/c-structs/templates/methods/polymorphic-pull.c new file mode 100644 index 00000000..553b2946 --- /dev/null +++ b/languages/c-structs/templates/methods/polymorphic-pull.c @@ -0,0 +1,21 @@ +/* ${method.name} - ${method.description} */ +uint32_t ${info.title}_Push${method.Name}(${method.signature.params}) +{ + uint32_t status = FireboltSDKErrorUnavailable; + ${if.params} +${method.params.serialization} + ${end.if.params} + FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); + if (transport != nullptr) { + WPEFramework::Core::JSON::Boolean jsonResult; + status = transport->Invoke(_T("${info.title}.${method.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()); + status = (jsonResult.Value() == true) ? FireboltSDKErrorNone : FireboltSDKErrorNotSupported; + } + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; +} diff --git a/languages/c-structs/templates/methods/polymorphic-reducer.c b/languages/c-structs/templates/methods/polymorphic-reducer.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/methods/property.c b/languages/c-structs/templates/methods/property.c new file mode 100644 index 00000000..7c46f7c1 --- /dev/null +++ b/languages/c-structs/templates/methods/property.c @@ -0,0 +1,15 @@ +/* ${method.name} - ${method.description} */ +int F${info.title}_Get${method.Name}(${method.signature.params}${if.result.properties}${if.params}, ${end.if.params}${end.if.result.properties}${method.result.properties}) { + const string method = _T("${info.title}.${method.name}"); +${if.params}${method.params.serialization}${end.if.params} + ${method.result.json} jsonResult; + ${if.params}int status = FireboltSDK::Properties::Get(method, jsonParameters, jsonResult);${end.if.params} + ${if.params.empty}int status = FireboltSDK::Properties::Get(method, jsonResult);${end.if.params.empty} + if (status == FireboltSDKErrorNone) { + if (${method.result.name} != nullptr) { +${method.result.instantiation} + } + } + return status; +} +${method.setter} diff --git a/languages/c-structs/templates/methods/provide.c b/languages/c-structs/templates/methods/provide.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/methods/rpc-only.c b/languages/c-structs/templates/methods/rpc-only.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/methods/setter.c b/languages/c-structs/templates/methods/setter.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/modules/include/Module.h b/languages/c-structs/templates/modules/include/Module.h new file mode 100644 index 00000000..7a192768 --- /dev/null +++ b/languages/c-structs/templates/modules/include/Module.h @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _${info.TITLE}_H +#define _${info.TITLE}_H + +#include "Firebolt.h" +/* ${IMPORTS} */ + +#ifdef __cplusplus +extern "C" { +#endif + +// Enums + +/* ${ENUMS} */ + +/* ${TYPES} */ + +/* ${DECLARATIONS} */ + +#ifdef __cplusplus +} +#endif + +#endif // Header Include Guard diff --git a/languages/c-structs/templates/modules/src/Module.cpp b/languages/c-structs/templates/modules/src/Module.cpp new file mode 100644 index 00000000..7d59c172 --- /dev/null +++ b/languages/c-structs/templates/modules/src/Module.cpp @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "FireboltSDK.h" +/* ${IMPORTS} */ +#include "${info.title}.h" + +namespace FireboltSDK { + namespace ${info.title} { + // Types + /* ${TYPES:json-types} */ + } +} + +/* ${ENUMS:json-types} */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* ${METHODS} */ +/* ${EVENTS} */ + +#ifdef __cplusplus +} +#endif diff --git a/languages/c-structs/templates/parameter-serialization/boolean.cpp b/languages/c-structs/templates/parameter-serialization/boolean.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c-structs/templates/parameter-serialization/boolean.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c-structs/templates/parameter-serialization/default.cpp b/languages/c-structs/templates/parameter-serialization/default.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c-structs/templates/parameter-serialization/default.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c-structs/templates/parameter-serialization/object.cpp b/languages/c-structs/templates/parameter-serialization/object.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c-structs/templates/parameter-serialization/object.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c-structs/templates/parameter-serialization/primitive.cpp b/languages/c-structs/templates/parameter-serialization/primitive.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c-structs/templates/parameter-serialization/primitive.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c-structs/templates/parameter-serialization/string.cpp b/languages/c-structs/templates/parameter-serialization/string.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c-structs/templates/parameter-serialization/string.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c-structs/templates/parameters/default.c b/languages/c-structs/templates/parameters/default.c new file mode 100644 index 00000000..e6e3b8ba --- /dev/null +++ b/languages/c-structs/templates/parameters/default.c @@ -0,0 +1 @@ +${method.param.type} ${method.param.name} \ No newline at end of file diff --git a/languages/c-structs/templates/parameters/json.c b/languages/c-structs/templates/parameters/json.c new file mode 100644 index 00000000..5ee36bec --- /dev/null +++ b/languages/c-structs/templates/parameters/json.c @@ -0,0 +1,3 @@ + ${json.param.type} ${method.param.Name} = ${method.param.name}; + jsonParameters.Add("_T(${method.param.name})", &${method.param.Name}); + diff --git a/languages/c-structs/templates/parameters/optional.c b/languages/c-structs/templates/parameters/optional.c new file mode 100644 index 00000000..e6e3b8ba --- /dev/null +++ b/languages/c-structs/templates/parameters/optional.c @@ -0,0 +1 @@ +${method.param.type} ${method.param.name} \ No newline at end of file diff --git a/languages/c-structs/templates/parameters/result.c b/languages/c-structs/templates/parameters/result.c new file mode 100644 index 00000000..699c8b5b --- /dev/null +++ b/languages/c-structs/templates/parameters/result.c @@ -0,0 +1 @@ +${method.param.type} *${method.param.name} \ No newline at end of file diff --git a/languages/c-structs/templates/result-instantiation/boolean.cpp b/languages/c-structs/templates/result-instantiation/boolean.cpp new file mode 100644 index 00000000..036b4a7d --- /dev/null +++ b/languages/c-structs/templates/result-instantiation/boolean.cpp @@ -0,0 +1,2 @@ + *${property} = jsonResult.Value(); + \ No newline at end of file diff --git a/languages/c-structs/templates/result-instantiation/default.cpp b/languages/c-structs/templates/result-instantiation/default.cpp new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/c-structs/templates/result-instantiation/default.cpp @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/c-structs/templates/result-instantiation/object.cpp b/languages/c-structs/templates/result-instantiation/object.cpp new file mode 100644 index 00000000..dcf0b333 --- /dev/null +++ b/languages/c-structs/templates/result-instantiation/object.cpp @@ -0,0 +1,4 @@ + WPEFramework::Core::ProxyType* resultPtr = new WPEFramework::Core::ProxyType(); + *resultPtr = WPEFramework::Core::ProxyType::Create(); + *(*resultPtr) = jsonResult; + *${property} = static_cast<${info.Title}_${title}>(resultPtr); diff --git a/languages/c-structs/templates/result-instantiation/primitive.cpp b/languages/c-structs/templates/result-instantiation/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/c-structs/templates/result-instantiation/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/c-structs/templates/result-instantiation/string.cpp b/languages/c-structs/templates/result-instantiation/string.cpp new file mode 100644 index 00000000..b6c9dcb5 --- /dev/null +++ b/languages/c-structs/templates/result-instantiation/string.cpp @@ -0,0 +1,2 @@ + FireboltSDK::JSON::String* strResult = new FireboltSDK::JSON::String(jsonResult); + *value = static_cast(strResult); diff --git a/languages/c-structs/templates/schemas/default.c b/languages/c-structs/templates/schemas/default.c new file mode 100644 index 00000000..9a52cff7 --- /dev/null +++ b/languages/c-structs/templates/schemas/default.c @@ -0,0 +1 @@ +${schema.shape} diff --git a/languages/c-structs/templates/schemas/include/Common/Module.h b/languages/c-structs/templates/schemas/include/Common/Module.h new file mode 100644 index 00000000..ff037fba --- /dev/null +++ b/languages/c-structs/templates/schemas/include/Common/Module.h @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _COMMON_${info.TITLE}_H +#define _COMMON_${info.TITLE}_H + +#include "Firebolt.h" +/* ${IMPORTS} */ + +#ifdef __cplusplus +extern "C" { +#endif + +// Enums + +/* ${ENUMS} */ + +/* ${TYPES} */ + +#ifdef __cplusplus +} +#endif + +#endif // Header Include Guard diff --git a/languages/c-structs/templates/schemas/src/JsonData_Module.h b/languages/c-structs/templates/schemas/src/JsonData_Module.h new file mode 100644 index 00000000..7c41a2ab --- /dev/null +++ b/languages/c-structs/templates/schemas/src/JsonData_Module.h @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/* ${IMPORTS} */ +#include "Common/${info.title}.h" + +namespace FireboltSDK { + namespace ${info.title} { + // Types + + /* ${SCHEMAS:json-types} */ + } +} diff --git a/languages/c-structs/templates/schemas/src/Module_Common.cpp b/languages/c-structs/templates/schemas/src/Module_Common.cpp new file mode 100644 index 00000000..0ccdfc28 --- /dev/null +++ b/languages/c-structs/templates/schemas/src/Module_Common.cpp @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "FireboltSDK.h" +/* ${IMPORTS} */ +#include "JsonData_${info.title}.h" + +/* ${ENUMS} */ + +#ifdef __cplusplus +extern "C" { +#endif + + /* ${ACCESSORS} */ + /* ${METHODS} */ + /* ${EVENTS} */ + +#ifdef __cplusplus +} +#endif diff --git a/languages/c-structs/templates/sdk/CMakeLists.txt b/languages/c-structs/templates/sdk/CMakeLists.txt new file mode 100644 index 00000000..5efcad1a --- /dev/null +++ b/languages/c-structs/templates/sdk/CMakeLists.txt @@ -0,0 +1,61 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.3) + +project(Firebolt) + +set(FIREBOLT_TRANSPORT_WAITTIME 1000 CACHE STRING "Maximum time to wait for Transport layer to get response") +set(FIREBOLT_LOGLEVEL "Info" CACHE STRING "Log level to be enabled") +option(FIREBOLT_ENABLE_STATIC_LIB "Create Firebolt library as Static library" OFF) +option(ENABLE_TESTS "Build openrpc native test" OFF) + +if (NOT SYSROOT_PATH) + # Set sysroot to support PC builds, sysroot_path not configured case + set(SYSROOT_PATH "${CMAKE_SOURCE_DIR}/../../firebolt") +endif() + +if (FIREBOLT_ENABLE_STATIC_LIB) + set(FIREBOLT_LIBRARY_TYPE STATIC) +else () + set(FIREBOLT_LIBRARY_TYPE SHARED) +endif () + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${SYSROOT_PATH}/usr" CACHE INTERNAL "" FORCE) + set(CMAKE_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/cmake" + "${SYSROOT_PATH}/usr/lib/cmake" + "${SYSROOT_PATH}/tools/cmake") +include(HelperFunctions) + +set(FIREBOLT_NAMESPACE ${PROJECT_NAME} CACHE STRING "Namespace of the project") + +find_package(WPEFramework CONFIG REQUIRED) + +add_subdirectory(src) + +if (ENABLE_TESTS) + add_subdirectory(test) +endif() + +# make sure others can make use cmake settings of Firebolt OpenRPC +configure_file( "${CMAKE_SOURCE_DIR}/cmake/project.cmake.in" + "${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}Config.cmake" + @ONLY) diff --git a/languages/c-structs/templates/sdk/cmake/HelperFunctions.cmake b/languages/c-structs/templates/sdk/cmake/HelperFunctions.cmake new file mode 100644 index 00000000..b3647c16 --- /dev/null +++ b/languages/c-structs/templates/sdk/cmake/HelperFunctions.cmake @@ -0,0 +1,144 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +macro(GetSubDirs subdirs currentdir) + file(GLOB subdirectories RELATIVE ${currentdir} ${currentdir}/*) + set(subdirs "") + foreach(subdir ${subdirectories}) + if (IS_DIRECTORY ${currentdir}/${subdir}) + list(APPEND subdirs ${subdir}) + endif() + endforeach() +endmacro() + +function(InstallHeaders) + set(optionsArgs EXCLUDE_ROOT_DIR) + set(oneValueArgs TARGET NAMESPACE SOURCE DESTINATION) + set(multiValueArgs HEADERS) + + cmake_parse_arguments(Argument "${optionsArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if (Argument_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to InstallHeaders(): \"${Argument_UNPARSED_ARGUMENTS}\"") + endif() + if (Argument_HEADERS) + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMENT "=================== Installing Headers ======================" + ) + foreach(directory ${Argument_HEADERS}) + if (Argument_EXCLUDE_ROOT_DIR) + set(destination ${Argument_DESTINATION}) + else() + set(destination ${Argument_DESTINATION}/${directory}) + endif() + + if (Argument_SOURCE) + set(source ${Argument_SOURCE}) + else() + set(source ${CMAKE_CURRENT_LIST_DIR}) + endif() + + GetSubDirs(subdirs ${source}/${directory}) + list(APPEND subdirs ${directory}) + + foreach(subdir ${subdirs}) + set(dest ${destination}/${subdir}) + file(GLOB headers "${source}/${directory}/${subdir}/*.h") + if (headers) + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${Argument_NAMESPACE}/usr/include/${dest} + COMMAND ${CMAKE_COMMAND} -E copy ${source}/${directory}/${subdir}/*.h ${CMAKE_BINARY_DIR}/${Argument_NAMESPACE}/usr/include/${dest} + ) + endif() + endforeach(subdir) + endforeach(directory) + endif() +endfunction(InstallHeaders) + +function(InstallLibraries) + set(optionsArgs SHARED STATIC) + set(oneValueArgs TARGET DESTINATION LIBDIR) + set(multiValueArgs LIBRARIES) + + cmake_parse_arguments(Argument "${optionsArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if (Argument_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to InstallLibraries(): \"${Argument_UNPARSED_ARGUMENTS}\"") + endif() + if (Argument_LIBRARIES) + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMENT "=================== Installing Libraries ======================" + ) + foreach(LIBRARY ${Argument_LIBRARIES}) + if (Argument_SHARED) + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${Argument_LIBDIR}/lib${LIBRARY}.so.${PROJECT_VERSION} ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib + COMMAND ${CMAKE_COMMAND} -E create_symlink lib${LIBRARY}.so.${PROJECT_VERSION} ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib/lib${LIBRARY}.so.${PROJECT_VERSION_MAJOR} + COMMAND ${CMAKE_COMMAND} -E create_symlink lib${LIBRARY}.so.${PROJECT_VERSION_MAJOR} ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib/lib${LIBRARY}.so + ) + elseif (Argument_STATIC) + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/lib${LIBRARY}.a ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib + ) + + endif() + endforeach(LIBRARY) + endif() +endfunction(InstallLibraries) + +function(InstallCMakeConfigs) + set(optionsArgs) + set(oneValueArgs TARGET DESTINATION) + set(multiValueArgs) + + cmake_parse_arguments(Argument "${optionsArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if (Argument_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to InstallCMakeConfigs(): \"${Argument_UNPARSED_ARGUMENTS}\"") + endif() + if (Argument_TARGET) + if (${CMAKE_VERSION} VERSION_LESS "3.25.0") + set(EXPORT_CONFIG_PATH "lib/cmake/${Argument_TARGET}") + else () + set(EXPORT_CONFIG_PATH "*") + endif () + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMENT "=================== Installing CMakeConfigs ======================" + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib/cmake/${Argument_TARGET} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${Argument_TARGET}Config*.cmake ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib/cmake/${Argument_TARGET} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/Export/${EXPORT_CONFIG_PATH}/${Argument_TARGET}Targets*.cmake ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib/cmake/${Argument_TARGET} + ) + if (EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${Argument_TARGET}.pc) + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib/pkgconfig + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${Argument_TARGET}.pc ${CMAKE_BINARY_DIR}/${Argument_DESTINATION}/usr/lib/pkgconfig + ) + endif() + endif() +endfunction(InstallCMakeConfigs) diff --git a/languages/c-structs/templates/sdk/cmake/project.cmake.in b/languages/c-structs/templates/sdk/cmake/project.cmake.in new file mode 100644 index 00000000..eca32f8c --- /dev/null +++ b/languages/c-structs/templates/sdk/cmake/project.cmake.in @@ -0,0 +1,35 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +set(FIREBOLT_NAMESPACE "@FIREBOLT_NAMESPACE@" CACHE INTERNAL "" FORCE) +set("${FIREBOLT_NAMESPACE}_FOUND" TRUE CACHE INTERNAL "" FORCE) + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/cmake" + "${SYSROOT_PATH}/usr/lib/cmake" + "${SYSROOT_PATH}/usr/lib/cmake/Firebolt" + "${SYSROOT_PATH}/tools/cmake") + +if (NOT DEFINED CMAKE_PREFIX_PATH) + set(CMAKE_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) +endif() + +if (FIREBOLT_ENABLE_STATIC_LIB) + set(FIREBOLT_LIBRARY_TYPE STATIC) +else () + set(FIREBOLT_LIBRARY_TYPE SHARED) +endif () + diff --git a/languages/c-structs/templates/sdk/include/Error.h b/languages/c-structs/templates/sdk/include/Error.h new file mode 100644 index 00000000..87cda9dd --- /dev/null +++ b/languages/c-structs/templates/sdk/include/Error.h @@ -0,0 +1,41 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _FIREBOLT_ERROR_H +#define _FIREBOLT_ERROR_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum FireboltSDKError { + FireboltSDKErrorNone = 0, + FireboltSDKErrorGeneral = 1, + FireboltSDKErrorUnavailable = 2, + FireboltSDKErrorTimedout = 3, + FireboltSDKErrorNotSubscribed = 4, + FireboltSDKErrorUnknown = 5, + FireboltSDKErrorInUse = 6, + FireboltSDKErrorNotSupported = 7 +} FireboltSDKError_t; + +#ifdef __cplusplus +} +#endif + +#endif // _FIREBOLT_ERROR_H diff --git a/languages/c-structs/templates/sdk/include/Firebolt.h b/languages/c-structs/templates/sdk/include/Firebolt.h new file mode 100644 index 00000000..2223bad3 --- /dev/null +++ b/languages/c-structs/templates/sdk/include/Firebolt.h @@ -0,0 +1,65 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _FIREBOLT_H +#define _FIREBOLT_H + +#include "Error.h" +#include "Types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Intitialize the Firebolt SDK + * + * @param configLine JSON String with configuration options + * + * CONFIG Format: + * { + * "waitTime": 1000, + * "logLevel": "Info", + * "workerPool":{ + * "queueSize": 8, + * "threadCount": 3 + * }, + * "wsUrl": "ws://127.0.0.1:9998" + * } + * + * + * @return FireboltSDKErrorNone if success, appropriate error otherwise. + * + */ +uint32_t FireboltSDK_Initialize(char* configLine); + + +/** + * @brief Deintitialize the Firebolt SDK + * + * @return FireboltSDKErrorNone if success, appropriate error otherwise. + * + */ +uint32_t FireboltSDK_Deinitialize(void); + +#ifdef __cplusplus +} +#endif + + +#endif // _FIREBOLT_H diff --git a/languages/c-structs/templates/sdk/include/Types.h b/languages/c-structs/templates/sdk/include/Types.h new file mode 100644 index 00000000..4fd16256 --- /dev/null +++ b/languages/c-structs/templates/sdk/include/Types.h @@ -0,0 +1,37 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _FIREBOLT_TYPES_H +#define _FIREBOLT_TYPES_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void* FireboltTypes_StringHandle; +const char* FireboltTypes_String(FireboltTypes_StringHandle handle); +void FireboltTypes_StringHandle_Release(FireboltTypes_StringHandle handle); + +#ifdef __cplusplus +} +#endif + +#endif // _FIREBOLT_TYPES_H diff --git a/languages/c-structs/templates/sdk/scripts/build.sh b/languages/c-structs/templates/sdk/scripts/build.sh new file mode 100755 index 00000000..e9b01f20 --- /dev/null +++ b/languages/c-structs/templates/sdk/scripts/build.sh @@ -0,0 +1,11 @@ +#!/bin/bash +SDK_PATH="." +if [ "$1" != "" ] +then + SDK_PATH=$1 + echo "inside ${1}" +fi +echo ${SDK_PATH} +rm -rf ${SDK_PATH}/build +cmake -B${SDK_PATH}/build -S${SDK_PATH} -DSYSROOT_PATH=${SYSROOT_PATH} +cmake --build ${SDK_PATH}/build diff --git a/languages/c-structs/templates/sdk/scripts/install.js b/languages/c-structs/templates/sdk/scripts/install.js new file mode 100644 index 00000000..5c332ceb --- /dev/null +++ b/languages/c-structs/templates/sdk/scripts/install.js @@ -0,0 +1,32 @@ +import { createRequire } from 'module'; +const require = createRequire(import.meta.url); +const fs = require('fs'); +const path = require('path'); + +var dest = process.env.NODE_INSTALL_PATH; +var src = 'src/native/build/' + process.env.TARGET_NAME; + +installFiles(src, dest); +function installFiles(src, dest) { + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest); + } + + var entries = fs.readdirSync(src); + entries.forEach((entry) => { + const srcPath = path.join(src, entry); + const destPath = path.join(dest, entry); + const stat = fs.lstatSync(srcPath); + + if (stat.isFile()) { + fs.copyFileSync(srcPath, destPath); + } else if (stat.isDirectory()) { + installFiles(srcPath, destPath); + } else if (stat.isSymbolicLink()) { + if (fs.existsSync(destPath)) { + fs.unlinkSync(destPath); + } + fs.symlinkSync(fs.readlinkSync(srcPath), destPath); + } + }); +} diff --git a/languages/c-structs/templates/sdk/src/Accessor/Accessor.cpp b/languages/c-structs/templates/sdk/src/Accessor/Accessor.cpp new file mode 100644 index 00000000..e4a5df32 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Accessor/Accessor.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Accessor.h" + +namespace FireboltSDK { + + Accessor* Accessor::_singleton = nullptr; + + Accessor::Accessor(const string& configLine) + : _workerPool() + , _transport(nullptr) + , _config() + { + _singleton = this; + _config.FromString(configLine); + + Logger::SetLogLevel(WPEFramework::Core::EnumerateType(_config.LogLevel.Value().c_str()).Value()); + + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "Url = %s", _config.WsUrl.Value().c_str()); + CreateTransport(_config.WsUrl.Value().c_str(), _config.WaitTime.Value()); + CreateEventHandler(); + + _workerPool = WPEFramework::Core::ProxyType::Create(_config.WorkerPool.ThreadCount.Value(), _config.WorkerPool.StackSize.Value(), _config.WorkerPool.QueueSize.Value()); + WPEFramework::Core::WorkerPool::Assign(&(*_workerPool)); + _workerPool->Run(); + } + + Accessor::~Accessor() + { + DestroyTransport(); + DestroyEventHandler(); + WPEFramework::Core::IWorkerPool::Assign(nullptr); + _workerPool->Stop(); + _singleton = nullptr; + } + + uint32_t Accessor::CreateEventHandler() + { + Event::Instance().Configure(_transport); + return FireboltSDKErrorNone; + } + + uint32_t Accessor::DestroyEventHandler() + { + Event::Dispose(); + return FireboltSDKErrorNone; + } + + Event& Accessor::GetEventManager() + { + return Event::Instance(); + } + + uint32_t Accessor::CreateTransport(const string& url, const uint32_t waitTime = DefaultWaitTime) + { + if (_transport != nullptr) { + delete _transport; + } + + _transport = new Transport(static_cast(url), waitTime); + if (WaitForLinkReady(_transport, waitTime) != FireboltSDKErrorNone) { + delete _transport; + _transport = nullptr; + } + + ASSERT(_transport != nullptr); + return ((_transport != nullptr) ? FireboltSDKErrorNone : FireboltSDKErrorUnavailable); + } + + uint32_t Accessor::DestroyTransport() + { + if (_transport != nullptr) { + delete _transport; + _transport = nullptr; + } + return FireboltSDKErrorNone; + } + + Transport* Accessor::GetTransport() + { + ASSERT(_transport != nullptr); + return _transport; + } + + uint32_t Accessor::WaitForLinkReady(Transport* transport, const uint32_t waitTime = DefaultWaitTime) { + uint32_t waiting = (waitTime == WPEFramework::Core::infinite ? WPEFramework::Core::infinite : waitTime); + static constexpr uint32_t SLEEPSLOT_TIME = 100; + + // Right, a wait till connection is closed is requested.. + while ((waiting > 0) && (transport->IsOpen() == false)) { + + uint32_t sleepSlot = (waiting > SLEEPSLOT_TIME ? SLEEPSLOT_TIME : waiting); + + // Right, lets sleep in slices of 100 ms + SleepMs(sleepSlot); + + waiting -= (waiting == WPEFramework::Core::infinite ? 0 : sleepSlot); + } + return (((waiting == 0) || (transport->IsOpen() == true)) ? FireboltSDKErrorNone : FireboltSDKErrorTimedout); + } +} diff --git a/languages/c-structs/templates/sdk/src/Accessor/Accessor.h b/languages/c-structs/templates/sdk/src/Accessor/Accessor.h new file mode 100644 index 00000000..f12dc51c --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Accessor/Accessor.h @@ -0,0 +1,122 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" +#include "WorkerPool.h" +#include "Transport/Transport.h" +#include "Event/Event.h" +#include "Logger/Logger.h" + +namespace FireboltSDK { + class Accessor { + private: + static constexpr uint8_t JSONVersion = 2; + + private: + //Singleton + Accessor(const string& configLine); + + public: + class EXTERNAL Config : public WPEFramework::Core::JSON::Container { + public: + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; + + class WorkerPoolConfig : public WPEFramework::Core::JSON::Container { + public: + WorkerPoolConfig& operator=(const WorkerPoolConfig&); + + WorkerPoolConfig() + : WPEFramework::Core::JSON::Container() + , QueueSize(8) + , ThreadCount(3) + , StackSize(WPEFramework::Core::Thread::DefaultStackSize()) + { + Add("queueSize", &QueueSize); + Add("threadCount", &ThreadCount); + Add("stackSize", &StackSize); + } + + virtual ~WorkerPoolConfig() = default; + + public: + WPEFramework::Core::JSON::DecUInt32 QueueSize; + WPEFramework::Core::JSON::DecUInt32 ThreadCount; + WPEFramework::Core::JSON::DecUInt32 StackSize; + }; + + + Config() + : WPEFramework::Core::JSON::Container() + , WaitTime(1000) + , LogLevel(_T("Info")) + , WorkerPool() + , WsUrl(_T("ws://127.0.0.1:9998")) + { + Add(_T("waitTime"), &WaitTime); + Add(_T("logLevel"), &LogLevel); + Add(_T("workerPool"), &WorkerPool); + Add(_T("wsUrl"), &WsUrl); + } + + public: + WPEFramework::Core::JSON::DecUInt32 WaitTime; + WPEFramework::Core::JSON::String LogLevel; + WorkerPoolConfig WorkerPool; + WPEFramework::Core::JSON::String WsUrl; + }; + + Accessor(const Accessor&) = delete; + Accessor& operator= (const Accessor&) = delete; + Accessor() = delete; + ~Accessor(); + + static Accessor& Instance(const string& configLine = "") + { + static Accessor *instance = new Accessor(configLine); + ASSERT(instance != nullptr); + return *instance; + } + + static void Dispose() + { + ASSERT(_singleton != nullptr); + + if (_singleton != nullptr) { + delete _singleton; + } + } + Event& GetEventManager(); + Transport* GetTransport(); + + private: + uint32_t CreateEventHandler(); + uint32_t DestroyEventHandler(); + uint32_t CreateTransport(const string& url, const uint32_t waitTime); + uint32_t DestroyTransport(); + uint32_t WaitForLinkReady(Transport* transport, const uint32_t waitTime); + + private: + WPEFramework::Core::ProxyType _workerPool; + Transport* _transport; + static Accessor* _singleton; + Config _config; + }; +} diff --git a/languages/c-structs/templates/sdk/src/Accessor/WorkerPool.h b/languages/c-structs/templates/sdk/src/Accessor/WorkerPool.h new file mode 100644 index 00000000..69005a5e --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Accessor/WorkerPool.h @@ -0,0 +1,102 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" + +namespace FireboltSDK { + + class WorkerPoolImplementation : public WPEFramework::Core::WorkerPool { + public: + WorkerPoolImplementation() = delete; + WorkerPoolImplementation(const WorkerPoolImplementation&) = delete; + WorkerPoolImplementation& operator=(const WorkerPoolImplementation&) = delete; + + WorkerPoolImplementation(const uint8_t threads, const uint32_t stackSize, const uint32_t queueSize) + : WorkerPool(threads, stackSize, queueSize, &_dispatcher) + { + } + + ~WorkerPoolImplementation() + { + // Diable the queue so the minions can stop, even if they are processing and waiting for work.. + Stop(); + } + + public: + void Stop() + { + WPEFramework::Core::WorkerPool::Stop(); + } + + void Run() + { + WPEFramework::Core::WorkerPool::Run(); + } + + private: + class Dispatcher : public WPEFramework::Core::ThreadPool::IDispatcher { + public: + Dispatcher(const Dispatcher&) = delete; + Dispatcher& operator=(const Dispatcher&) = delete; + + Dispatcher() = default; + ~Dispatcher() override = default; + + private: + void Initialize() override { } + void Deinitialize() override { } + void Dispatch(WPEFramework::Core::IDispatch* job) override + { job->Dispatch(); } + }; + + Dispatcher _dispatcher; + }; + + class Worker : public WPEFramework::Core::IDispatch { + public: + typedef std::function Dispatcher; + + protected: + Worker(const Dispatcher& dispatcher, const void* userData) + : _dispatcher(dispatcher) + , _userData(userData) + { + } + + public: + Worker() = delete; + Worker(const Worker&) = delete; + Worker& operator=(const Worker&) = delete; + + ~Worker() = default; + + public: + static WPEFramework::Core::ProxyType Create(const Dispatcher& dispatcher, const void* userData); + + void Dispatch() override + { + _dispatcher(_userData); + } + + private: + Dispatcher _dispatcher; + const void* _userData; + }; +} diff --git a/languages/c-structs/templates/sdk/src/CMakeLists.txt b/languages/c-structs/templates/sdk/src/CMakeLists.txt new file mode 100644 index 00000000..2f00bdf1 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/CMakeLists.txt @@ -0,0 +1,84 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.3) + +project(FireboltSDK) +project_version(1.0.0) +set(TARGET ${PROJECT_NAME}) +message("Setup ${TARGET} v${PROJECT_VERSION}") +file(GLOB GENERATED_SOURCES "${GENERATED_CODE_PATH}/src/*.cpp") + +file(GLOB SOURCES *.cpp) +add_library(${TARGET} ${FIREBOLT_LIBRARY_TYPE} + ${SOURCES} + Logger/Logger.cpp + Transport/Transport.cpp + Accessor/Accessor.cpp + Event/Event.cpp +) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) + +target_link_libraries(${TARGET} + PUBLIC + ${NAMESPACE}WebSocket::${NAMESPACE}WebSocket +) + +target_include_directories(${TARGET} + PRIVATE + $ + $ + $ + $ +) + +set_target_properties(${TARGET} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + FRAMEWORK FALSE + LINK_WHAT_YOU_USE TRUE + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) + +install( + TARGETS ${TARGET} EXPORT ${TARGET}Targets + LIBRARY DESTINATION lib COMPONENT libs + PUBLIC_HEADER DESTINATION include/${TARGET} COMPONENT devel # headers for mac (note the different component -> different package) + INCLUDES DESTINATION include/${TARGET} # headers +) + +InstallHeaders(TARGET ${TARGET} HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} DESTINATION ${FIREBOLT_NAMESPACE}SDK) +InstallHeaders(TARGET ${TARGET} EXCLUDE_ROOT_DIR HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} + SOURCE ${GENERATED_CODE_PATH}/include DESTINATION ${FIREBOLT_NAMESPACE}/generated) +InstallHeaders(TARGET ${TARGET} HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} + SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/../include DESTINATION ${FIREBOLT_NAMESPACE}) + +InstallLibraries(TARGET ${TARGET} ${FIREBOLT_LIBRARY_TYPE} LIBDIR ${LIBDIR} LIBRARIES ${TARGET} DESTINATION ${FIREBOLT_NAMESPACE}) +InstallCMakeConfig(TARGETS ${TARGET}) +InstallPackageConfig(TARGETS ${TARGET} DESCRIPTION "Firebolt SDK Library") +InstallCMakeConfigs(TARGET ${TARGET} DESTINATION ${FIREBOLT_NAMESPACE}) + +add_custom_command( + TARGET ${TARGET} + POST_BUILD + COMMENT "=================== Installing FireboltConfig & Helper CMakes ======================" + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/lib/cmake/${FIREBOLT_NAMESPACE} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}Config.cmake ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/lib/cmake/${FIREBOLT_NAMESPACE} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/cmake/*.cmake ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/lib/cmake/${FIREBOLT_NAMESPACE} +) diff --git a/languages/c-structs/templates/sdk/src/Event/Event.cpp b/languages/c-structs/templates/sdk/src/Event/Event.cpp new file mode 100644 index 00000000..5e98a36f --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Event/Event.cpp @@ -0,0 +1,147 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Transport/Transport.h" +#include "Event.h" + +namespace FireboltSDK { + Event* Event::_singleton = nullptr; + Event::Event() + : _eventMap() + , _adminLock() + , _transport(nullptr) + { + ASSERT(_singleton == nullptr); + _singleton = this; + } + + Event::~Event() /* override */ + { + _transport->SetEventHandler(nullptr); + _transport = nullptr; + + _singleton = nullptr; + } + + /* static */ Event& Event::Instance() + { + static Event *instance = new Event(); + ASSERT(instance != nullptr); + return *instance; + } + + /* static */ void Event::Dispose() + { + ASSERT(_singleton != nullptr); + + if (_singleton != nullptr) { + delete _singleton; + } + } + + void Event::Configure(Transport* transport) + { + _transport = transport; + _transport->SetEventHandler(this); + } + + uint32_t Event::Unsubscribe(const string& eventName, void* usercb) + { + uint32_t status = Revoke(eventName, usercb); + + if (status == FireboltSDKErrorNone) { + if (_transport != nullptr) { + + const string parameters("{\"listen\":false}"); + status = _transport->Unsubscribe(eventName, parameters); + } + } + return ((status == FireboltSDKErrorInUse) ? FireboltSDKErrorNone: status); + } + + uint32_t Event::ValidateResponse(const WPEFramework::Core::ProxyType& jsonResponse, bool& enabled) /* override */ + { + uint32_t result = FireboltSDKErrorGeneral; + Response response; + _transport->FromMessage((WPEFramework::Core::JSON::IElement*)&response, *jsonResponse); + if (response.Listening.IsSet() == true) { + result = FireboltSDKErrorNone; + enabled = response.Listening.Value(); + } + return result; + } + + uint32_t Event::Dispatch(const string& eventName, const WPEFramework::Core::ProxyType& jsonResponse) /* override */ + { + string response = jsonResponse->Result.Value(); + _adminLock.Lock(); + EventMap::iterator eventIndex = _eventMap.find(eventName); + if (eventIndex != _eventMap.end()) { + CallbackMap::iterator callbackIndex = eventIndex->second.begin(); + while(callbackIndex != eventIndex->second.end()) { + State state; + if (callbackIndex->second.state != State::REVOKED) { + callbackIndex->second.state = State::EXECUTING; + } + state = callbackIndex->second.state; + _adminLock.Unlock(); + if (state == State::EXECUTING) { + callbackIndex->second.lambda(callbackIndex->first, callbackIndex->second.userdata, (jsonResponse->Result.Value())); + } + _adminLock.Lock(); + if (callbackIndex->second.state == State::REVOKED) { + callbackIndex = eventIndex->second.erase(callbackIndex); + if (eventIndex->second.size() == 0) { + _eventMap.erase(eventIndex); + } + } else { + callbackIndex->second.state = State::IDLE; + callbackIndex++; + } + } + } + _adminLock.Unlock(); + + return FireboltSDKErrorNone;; + } + + uint32_t Event::Revoke(const string& eventName, void* usercb) + { + uint32_t status = FireboltSDKErrorNone; + _adminLock.Lock(); + EventMap::iterator eventIndex = _eventMap.find(eventName); + if (eventIndex != _eventMap.end()) { + CallbackMap::iterator callbackIndex = eventIndex->second.find(usercb); + if (callbackIndex->second.state != State::EXECUTING) { + if (callbackIndex != eventIndex->second.end()) { + eventIndex->second.erase(callbackIndex); + } + } else { + callbackIndex->second.state = State::REVOKED; + } + if (eventIndex->second.size() == 0) { + _eventMap.erase(eventIndex); + } else { + status = FireboltSDKErrorInUse; + } + } + _adminLock.Unlock(); + + return status; + } +} diff --git a/languages/c-structs/templates/sdk/src/Event/Event.h b/languages/c-structs/templates/sdk/src/Event/Event.h new file mode 100644 index 00000000..efa8457a --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Event/Event.h @@ -0,0 +1,165 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" + +namespace FireboltSDK { + + static constexpr uint32_t DefaultWaitTime = 1000; + + class Event : public IEventHandler { + public: + typedef std::function DispatchFunction; + private: + enum State : uint8_t { + IDLE, + EXECUTING, + REVOKED + }; + + struct CallbackData { + const DispatchFunction lambda; + const void* userdata; + State state; + }; + using CallbackMap = std::map; + using EventMap = std::map; + + class Response : public WPEFramework::Core::JSON::Container { + public: + Response& operator=(const Response&) = delete; + Response() + : WPEFramework::Core::JSON::Container() + , Listening(false) + { + Add(_T("listening"), &Listening); + } + Response(const Response& copy) + : WPEFramework::Core::JSON::Container() + , Listening(copy.Listening) + { + Add(_T("listening"), &Listening); + } + ~Response() override = default; + + public: + WPEFramework::Core::JSON::Boolean Listening; + }; + + private: + Event(); + public: + ~Event() override; + static Event& Instance(); + static void Dispose(); + void Configure(Transport* transport); + + public: + template + uint32_t Subscribe(const string& eventName, const CALLBACK& callback, void* usercb, const void* userdata) + { + JsonObject jsonParameters; + return Subscribe(eventName, jsonParameters, callback, usercb, userdata); + } + + template + uint32_t Subscribe(const string& eventName, JsonObject& jsonParameters, const CALLBACK& callback, void* usercb, const void* userdata) + { + uint32_t status = FireboltSDKErrorUnavailable; + if (_transport != nullptr) { + + status = Assign(eventName, callback, usercb, userdata); + if (status == FireboltSDKErrorNone) { + Response response; + + WPEFramework::Core::JSON::Variant Listen = true; + jsonParameters.Set(_T("listen"), Listen); + string parameters; + jsonParameters.ToString(parameters); + + status = _transport->Subscribe(eventName, parameters, response); + + if (status != FireboltSDKErrorNone) { + Revoke(eventName, usercb); + } else if ((response.Listening.IsSet() == true) && + (response.Listening.Value() == true)) { + status = FireboltSDKErrorNone; + } else { + status = FireboltSDKErrorNotSubscribed; + } + } + } + + return status; + } + + uint32_t Unsubscribe(const string& eventName, void* usercb); + + private: + template + uint32_t Assign(const string& eventName, const CALLBACK& callback, void* usercb, const void* userdata) + { + uint32_t status = FireboltSDKErrorNone; + std::function actualCallback = callback; + DispatchFunction implementation = [actualCallback](void* usercb, const void* userdata, const string& parameters) -> uint32_t { + + WPEFramework::Core::ProxyType* inbound = new WPEFramework::Core::ProxyType(); + *inbound = WPEFramework::Core::ProxyType::Create(); + (*inbound)->FromString(parameters); + actualCallback(usercb, userdata, static_cast(inbound)); + return (FireboltSDKErrorNone); + }; + CallbackData callbackData = {implementation, userdata, State::IDLE}; + + _adminLock.Lock(); + EventMap::iterator eventIndex = _eventMap.find(eventName); + if (eventIndex != _eventMap.end()) { + CallbackMap::iterator callbackIndex = eventIndex->second.find(usercb); + if (callbackIndex == eventIndex->second.end()) { + eventIndex->second.emplace(std::piecewise_construct, std::forward_as_tuple(usercb), std::forward_as_tuple(callbackData)); + } else { + // Already registered, no need to register again; + status = FireboltSDKErrorInUse; + } + } else { + + CallbackMap callbackMap; + callbackMap.emplace(std::piecewise_construct, std::forward_as_tuple(usercb), std::forward_as_tuple(callbackData)); + _eventMap.emplace(std::piecewise_construct, std::forward_as_tuple(eventName), std::forward_as_tuple(callbackMap)); + + } + + _adminLock.Unlock(); + return status; + } + uint32_t Revoke(const string& eventName, void* usercb); + + private: + uint32_t ValidateResponse(const WPEFramework::Core::ProxyType& jsonResponse, bool& enabled) override; + uint32_t Dispatch(const string& eventName, const WPEFramework::Core::ProxyType& jsonResponse) override; + + private: + EventMap _eventMap; + WPEFramework::Core::CriticalSection _adminLock; + Transport* _transport; + + static Event* _singleton; + }; +} diff --git a/languages/c-structs/templates/sdk/src/Firebolt.cpp b/languages/c-structs/templates/sdk/src/Firebolt.cpp new file mode 100644 index 00000000..ffc1369a --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Firebolt.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "FireboltSDK.h" + + +#ifdef __cplusplus +extern "C" { +#endif + + + +uint32_t FireboltSDK_Initialize(char* configLine) { + FireboltSDK::Accessor::Instance(configLine); + return FireboltSDKErrorNone; +} + +uint32_t FireboltSDK_Deinitialize(void) { + FireboltSDK::Accessor::Dispose(); + return FireboltSDKErrorNone; +} + +#ifdef __cplusplus +} +#endif diff --git a/languages/c-structs/templates/sdk/src/FireboltSDK.conf.in b/languages/c-structs/templates/sdk/src/FireboltSDK.conf.in new file mode 100644 index 00000000..6964a7bc --- /dev/null +++ b/languages/c-structs/templates/sdk/src/FireboltSDK.conf.in @@ -0,0 +1,3 @@ +url = "@FIREBOLT_SERVER_URL@" +waittime = "@FIREBOLT_TRANSPORT_WAITTIME@" +loglevel = "@FIREBOLT_LOGLEVEL@" diff --git a/languages/c-structs/templates/sdk/src/FireboltSDK.h b/languages/c-structs/templates/sdk/src/FireboltSDK.h new file mode 100644 index 00000000..19946126 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/FireboltSDK.h @@ -0,0 +1,26 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Transport/Transport.h" +#include "Properties/Properties.h" +#include "Accessor/Accessor.h" +#include "Logger/Logger.h" +#include "TypesPriv.h" +#include "Types.h" diff --git a/languages/c-structs/templates/sdk/src/Logger/Logger.cpp b/languages/c-structs/templates/sdk/src/Logger/Logger.cpp new file mode 100644 index 00000000..0e49dfce --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Logger/Logger.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" +#include "Error.h" +#include "Logger.h" + +namespace WPEFramework { + +ENUM_CONVERSION_BEGIN(FireboltSDK::Logger::LogLevel) + + { FireboltSDK::Logger::LogLevel::Error, _TXT("Error") }, + { FireboltSDK::Logger::LogLevel::Warning, _TXT("Warning") }, + { FireboltSDK::Logger::LogLevel::Info, _TXT("Info") }, + { FireboltSDK::Logger::LogLevel::Debug, _TXT("Debug") }, + +ENUM_CONVERSION_END(FireboltSDK::Logger::LogLevel) + +ENUM_CONVERSION_BEGIN(FireboltSDK::Logger::Category) + + { FireboltSDK::Logger::Category::OpenRPC, _TXT("FireboltSDK::OpenRPC") }, + { FireboltSDK::Logger::Category::Core, _TXT("FireboltSDK::Core") }, + { FireboltSDK::Logger::Category::Management, _TXT("FireboltSDK::Management") }, + { FireboltSDK::Logger::Category::Discovery, _TXT("FireboltSDK::Discovery") }, + +ENUM_CONVERSION_END(FireboltSDK::Logger::Category) + +} + +namespace FireboltSDK { + /* static */ Logger::LogLevel Logger::_logLevel = Logger::LogLevel::Error; + + uint32_t Logger::SetLogLevel(Logger::LogLevel logLevel) + { + ASSERT(logLevel < Logger::LogLevel::MaxLevel); + uint32_t status = FireboltSDKErrorNotSupported; + if (logLevel < Logger::LogLevel::MaxLevel) { + _logLevel = logLevel; + status = FireboltSDKErrorNone; + } + return status; + } + + void Logger::Log(LogLevel logLevel, Category category, const std::string& module, const std::string file, const std::string function, const uint16_t line, const std::string& format, ...) + { + if (logLevel <= _logLevel) { + va_list arg; + char msg[Logger::MaxBufSize]; + va_start(arg, format); + int length = vsnprintf(msg, Logger::MaxBufSize, format.c_str(), arg); + va_end(arg); + + uint32_t position = (length >= Logger::MaxBufSize) ? (Logger::MaxBufSize - 1) : length; + msg[position] = '\0'; + + char formattedMsg[Logger::MaxBufSize]; + const string time = WPEFramework::Core::Time::Now().ToTimeOnly(true); + const string categoryName = WPEFramework::Core::EnumerateType(category).Data(); + if (categoryName.empty() != true) { + sprintf(formattedMsg, "--->\033[1;32m[%s]:[%s]:[%s][%s:%d](%s) : %s\n", time.c_str(), categoryName.c_str(), module.c_str(), WPEFramework::Core::File::FileName(file).c_str(), line, function.c_str(), TRACE_PROCESS_ID, TRACE_THREAD_ID, msg); + } else { + sprintf(formattedMsg, "--->\033[1;32m[%s]:[%s][%s:%d](%s) : %s\n", time.c_str(), module.c_str(), WPEFramework::Core::File::FileName(file).c_str(), line, function.c_str(), TRACE_PROCESS_ID, TRACE_THREAD_ID, msg); + } + LOG_MESSAGE(formattedMsg); + } + } +} + diff --git a/languages/c-structs/templates/sdk/src/Logger/Logger.h b/languages/c-structs/templates/sdk/src/Logger/Logger.h new file mode 100644 index 00000000..cffeff54 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Logger/Logger.h @@ -0,0 +1,85 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Types.h" + +namespace FireboltSDK { + + class Logger { + private: + static constexpr uint16_t MaxBufSize = 512; + + public: + enum class LogLevel : uint8_t { + Error, + Warning, + Info, + Debug, + MaxLevel + }; + + enum class Category : uint8_t { + OpenRPC, + Core, + Management, + Discovery + }; + + public: + Logger() = default; + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + ~Logger() = default; + + public: + static uint32_t SetLogLevel(LogLevel logLevel); + static void Log(LogLevel logLevel, Category category, const std::string& module, const std::string file, const std::string function, const uint16_t line, const std::string& format, ...); + + public: + template + static const string Module() + { + return WPEFramework::Core::ClassNameOnly(typeid(CLASS).name()).Text(); + } + + private: + static LogLevel _logLevel; + }; +} + +#define FIREBOLT_LOG(level, category, module, ...) \ + FireboltSDK::Logger::Log(level, category, module, __FILE__, __func__, __LINE__, __VA_ARGS__) + +#define FIREBOLT_LOG_ERROR(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Error, category, module, __VA_ARGS__) +#define FIREBOLT_LOG_WARNING(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Warning, category, module, __VA_ARGS__) +#define FIREBOLT_LOG_INFO(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Info, category, module, __VA_ARGS__) +#define FIREBOLT_LOG_DEBUG(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Debug, category, module, __VA_ARGS__) + +#ifdef ENABLE_SYSLOG +#define LOG_MESSAGE(message) \ + syslog(sLOG_NOTIC, "%s", message); +#else +#define LOG_MESSAGE(message) \ + fprintf(stderr, "%s", message); fflush(stdout); +#endif diff --git a/languages/c-structs/templates/sdk/src/Module.cpp b/languages/c-structs/templates/sdk/src/Module.cpp new file mode 100644 index 00000000..d63badc4 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Module.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/languages/c-structs/templates/sdk/src/Module.h b/languages/c-structs/templates/sdk/src/Module.h new file mode 100644 index 00000000..00ea64bb --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Module.h @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifndef MODULE_NAME +#define MODULE_NAME OpenRPCNativeSDK +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/languages/c-structs/templates/sdk/src/Properties/Properties.h b/languages/c-structs/templates/sdk/src/Properties/Properties.h new file mode 100644 index 00000000..d75c3ba5 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Properties/Properties.h @@ -0,0 +1,148 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Accessor/Accessor.h" +#include "Event/Event.h" + +namespace FireboltSDK { + + class Properties { + public: + Properties(const Properties&) = delete; + Properties& operator= (const Properties&) = delete; + + Properties() = default; + ~Properties() = default; + + public: + template + static uint32_t Get(const string& propertyName, WPEFramework::Core::ProxyType& response) + { + uint32_t status = FireboltSDKErrorUnavailable; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + JsonObject parameters; + RESPONSETYPE responseType; + status = transport->Invoke(propertyName, parameters, responseType); + if (status == FireboltSDKErrorNone) { + ASSERT(response.IsValid() == false); + if (response.IsValid() == true) { + response.Release(); + } + response = WPEFramework::Core::ProxyType::Create(); + (*response) = responseType; + } + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static uint32_t Get(const string& propertyName, const PARAMETERS& parameters, WPEFramework::Core::ProxyType& response) + { + uint32_t status = FireboltSDKErrorUnavailable; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + RESPONSETYPE responseType; + status = transport->Invoke(propertyName, parameters, responseType); + if (status == FireboltSDKErrorNone) { + ASSERT(response.IsValid() == false); + if (response.IsValid() == true) { + response.Release(); + } + response = WPEFramework::Core::ProxyType::Create(); + (*response) = responseType; + } + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + + template + static uint32_t Get(const string& propertyName, RESPONSETYPE& response) + { + uint32_t status = FireboltSDKErrorUnavailable; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + JsonObject parameters; + status = transport->Invoke(propertyName, parameters, response); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static uint32_t Get(const string& propertyName, const PARAMETERS& parameters, RESPONSETYPE& response) + { + uint32_t status = FireboltSDKErrorUnavailable; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + status = transport->Invoke(propertyName, parameters, response); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static uint32_t Set(const string& propertyName, const PARAMETERS& parameters) + { + uint32_t status = FireboltSDKErrorUnavailable; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + JsonObject responseType; + status = transport->Invoke(propertyName, parameters, responseType); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static uint32_t Subscribe(const string& propertyName, JsonObject& paramsters, const CALLBACK& callback, void* usercb, const void* userdata) + { + return Event::Instance().Subscribe(EventName(propertyName), paramsters, callback, usercb, userdata); + } + + static uint32_t Unsubscribe(const string& propertyName, void* usercb) + { + return Event::Instance().Unsubscribe(EventName(propertyName), usercb); + } + private: + static inline string EventName(const string& propertyName) { + size_t pos = propertyName.find_first_of('.'); + string eventName = propertyName; + if (pos != std::string::npos) { + eventName[pos + 1] = std::toupper(eventName[pos + 1]); + eventName = string(eventName.substr(0, pos + 1) + "on" + eventName.substr(pos + 1) + "Changed"); + } + return eventName; + } + }; +} diff --git a/languages/c-structs/templates/sdk/src/Transport/Transport.cpp b/languages/c-structs/templates/sdk/src/Transport/Transport.cpp new file mode 100644 index 00000000..280944c6 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Transport/Transport.cpp @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Transport.h" + +namespace FireboltSDK { + +} + diff --git a/languages/c-structs/templates/sdk/src/Transport/Transport.h b/languages/c-structs/templates/sdk/src/Transport/Transport.h new file mode 100644 index 00000000..2aff350d --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Transport/Transport.h @@ -0,0 +1,897 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" +#include "Error.h" + +namespace FireboltSDK { + + using namespace WPEFramework::Core::TypeTraits; + + template + class CommunicationChannel { + public: + typedef std::function Callback; + class Entry { + private: + Entry(const Entry&) = delete; + Entry& operator=(const Entry& rhs) = delete; + struct Synchronous { + Synchronous() + : _signal(false, true) + , _response() + { + } + WPEFramework::Core::Event _signal; + std::list> _response; + }; + struct ASynchronous { + ASynchronous(const uint32_t waitTime, const Callback& completed) + : _waitTime(WPEFramework::Core::Time::Now().Add(waitTime).Ticks()) + , _completed(completed) + { + } + uint64_t _waitTime; + Callback _completed; + }; + + public: + Entry() + : _synchronous(true) + , _info() + { + } + Entry(const uint32_t waitTime, const Callback& completed) + : _synchronous(false) + , _info(waitTime, completed) + { + } + ~Entry() + { + if (_synchronous == true) { + _info.sync.~Synchronous(); + } + else { + _info.async.~ASynchronous(); + } + } + + public: + const WPEFramework::Core::ProxyType& Response() const + { + return (*(_info.sync._response.begin())); + } + bool Signal(const WPEFramework::Core::ProxyType& response) + { + if (_synchronous == true) { + _info.sync._response.push_back(response); + _info.sync._signal.SetEvent(); + } + else { + _info.async._completed(*response); + } + + return (_synchronous == false); + } + const uint64_t& Expiry() const + { + return (_info.async._waitTime); + } + void Abort(const uint32_t id) + { + if (_synchronous == true) { + _info.sync._signal.SetEvent(); + } + else { + MESSAGETYPE message; + ToMessage(id, message, WPEFramework::Core::ERROR_ASYNC_ABORTED); + _info.async._completed(message); + } + } + bool Expired(const uint32_t id, const uint64_t& currentTime, uint64_t& nextTime) + { + bool expired = false; + + if (_synchronous == false) { + if (_info.async._waitTime > currentTime) { + if (_info.async._waitTime < nextTime) { + nextTime = _info.async._waitTime; + } + } + else { + MESSAGETYPE message; + ToMessage(id, message, WPEFramework::Core::ERROR_TIMEDOUT); + _info.async._completed(message); + expired = true; + } + } + return (expired); + } + bool WaitForResponse(const uint32_t waitTime) + { + return (_info.sync._signal.Lock(waitTime) == WPEFramework::Core::ERROR_NONE); + } + + private: + void ToMessage(const uint32_t id, WPEFramework::Core::JSONRPC::Message& message, uint32_t error) + { + message.Id = id; + message.Error.Code = error; + switch (error) { + case WPEFramework::Core::ERROR_ASYNC_ABORTED: { + message.Error.Text = _T("Pending a-sync call has been aborted"); + break; + } + case WPEFramework::Core::ERROR_TIMEDOUT: { + message.Error.Text = _T("Pending a-sync call has timed out"); + break; + } + } + } + + bool _synchronous; + union Info { + public: + Info() + : sync() + { + } + Info(const uint32_t waitTime, const Callback& completed) + : async(waitTime, completed) + { + } + ~Info() + { + } + Synchronous sync; + ASynchronous async; + } _info; + }; + + + + private: + class FactoryImpl { + private: + FactoryImpl(const FactoryImpl&) = delete; + FactoryImpl& operator=(const FactoryImpl&) = delete; + + class WatchDog { + private: + WatchDog() = delete; + WatchDog& operator=(const WatchDog&) = delete; + + public: + WatchDog(CLIENT* client) + : _client(client) + { + } + WatchDog(const WatchDog& copy) + : _client(copy._client) + { + } + ~WatchDog() + { + } + + bool operator==(const WatchDog& rhs) const + { + return (rhs._client == _client); + } + bool operator!=(const WatchDog& rhs) const + { + return (!operator==(rhs)); + } + + public: + uint64_t Timed(const uint64_t scheduledTime) { + return (_client->Timed()); + } + + private: + CLIENT* _client; + }; + + friend WPEFramework::Core::SingletonType; + + FactoryImpl() + : _messageFactory(2) + , _watchDog(WPEFramework::Core::Thread::DefaultStackSize(), _T("TransportCleaner")) + { + } + + public: + static FactoryImpl& Instance() + { + return (WPEFramework::Core::SingletonType::Instance()); + } + + ~FactoryImpl() + { + } + + public: + WPEFramework::Core::ProxyType Element(const string&) + { + return (_messageFactory.Element()); + } + void Trigger(const uint64_t& time, CLIENT* client) + { + _watchDog.Trigger(time, client); + } + void Revoke(CLIENT* client) + { + _watchDog.Revoke(client); + } + private: + WPEFramework::Core::ProxyPoolType _messageFactory; + WPEFramework::Core::TimerType _watchDog; + }; + + class ChannelImpl : public WPEFramework::Core::StreamJSONType, FactoryImpl&, INTERFACE> { + private: + ChannelImpl(const ChannelImpl&) = delete; + ChannelImpl& operator=(const ChannelImpl&) = delete; + + typedef WPEFramework::Core::StreamJSONType, FactoryImpl&, INTERFACE> BaseClass; + + public: + ChannelImpl(CommunicationChannel* parent, const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask) + : BaseClass(5, FactoryImpl::Instance(), path, _T("JSON"), query, "", false, mask, false, remoteNode.AnyInterface(), remoteNode, 1024 * 2, 1024 * 2) //TODO Relook this size issue + , _parent(*parent) + { + } + ~ChannelImpl() override = default; + + public: + void Received(WPEFramework::Core::ProxyType& response) override + { + WPEFramework::Core::ProxyType inbound(response); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) { + _parent.Inbound(inbound); + } + } + void Send(WPEFramework::Core::ProxyType& msg) override + { +#ifdef __DEBUG__ + string message; + ToMessage(msg, message); + TRACE_L1("Message: %s send", message.c_str()); +#endif + } + void StateChange() override + { + _parent.StateChange(); + } + bool IsIdle() const override + { + return (true); + } + + private: + void ToMessage(const WPEFramework::Core::ProxyType& jsonObject, string& message) const + { + WPEFramework::Core::ProxyType inbound(jsonObject); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) { + inbound->ToString(message); + } + } + void ToMessage(const WPEFramework::Core::ProxyType& jsonObject, string& message) const + { + WPEFramework::Core::ProxyType inbound(jsonObject); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) { + std::vector values; + inbound->ToBuffer(values); + if (values.empty() != true) { + WPEFramework::Core::ToString(values.data(), static_cast(values.size()), false, message); + } + } + } + + private: + CommunicationChannel& _parent; + }; + + protected: + CommunicationChannel(const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask) + : _channel(this, remoteNode, path, query, mask) + , _sequence(0) + { + } + + public: + ~CommunicationChannel() = default; + static WPEFramework::Core::ProxyType Instance(const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask = true) + { + static WPEFramework::Core::ProxyMapType channelMap; + + string searchLine = remoteNode.HostAddress() + '@' + path; + + return (channelMap.template Instance(searchLine, remoteNode, path, query, mask)); + } + + public: + static void Trigger(const uint64_t& time, CLIENT* client) + { + FactoryImpl::Instance().Trigger(time, client); + } + static WPEFramework::Core::ProxyType Message() + { + return (FactoryImpl::Instance().Element(string())); + } + uint32_t Sequence() const + { + return (++_sequence); + } + void Register(CLIENT& client) + { + _adminLock.Lock(); + ASSERT(std::find(_observers.begin(), _observers.end(), &client) == _observers.end()); + _observers.push_back(&client); + if (_channel.IsOpen() == true) { + client.Opened(); + } + _adminLock.Unlock(); + } + void Unregister(CLIENT& client) + { + _adminLock.Lock(); + typename std::list::iterator index(std::find(_observers.begin(), _observers.end(), &client)); + if (index != _observers.end()) { + _observers.erase(index); + } + FactoryImpl::Instance().Revoke(&client); + _adminLock.Unlock(); + } + + void Submit(const WPEFramework::Core::ProxyType& message) + { + _channel.Submit(message); + } + bool IsSuspended() const + { + return (_channel.IsSuspended()); + } + uint32_t Initialize() + { + return (Open(0)); + } + void Deinitialize() + { + Close(); + } + bool IsOpen() + { + return (_channel.IsOpen() == true); + } + + protected: + void StateChange() + { + _adminLock.Lock(); + typename std::list::iterator index(_observers.begin()); + while (index != _observers.end()) { + if (_channel.IsOpen() == true) { + (*index)->Opened(); + } + else { + (*index)->Closed(); + } + index++; + } + _adminLock.Unlock(); + } + bool Open(const uint32_t waitTime) + { + bool result = true; + if (_channel.IsClosed() == true) { + result = (_channel.Open(waitTime) == WPEFramework::Core::ERROR_NONE); + } + return (result); + } + void Close() + { + _channel.Close(WPEFramework::Core::infinite); + } + + private: + uint32_t Inbound(const WPEFramework::Core::ProxyType& inbound) + { + uint32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + _adminLock.Lock(); + typename std::list::iterator index(_observers.begin()); + while ((result != WPEFramework::Core::ERROR_NONE) && (index != _observers.end())) { + result = (*index)->Submit(inbound); + index++; + } + _adminLock.Unlock(); + + return (result); + } + + private: + WPEFramework::Core::CriticalSection _adminLock; + ChannelImpl _channel; + mutable std::atomic _sequence; + std::list _observers; + }; + + class IEventHandler { + public: + virtual uint32_t ValidateResponse(const WPEFramework::Core::ProxyType& jsonResponse, bool& enabled) = 0; + virtual uint32_t Dispatch(const string& eventName, const WPEFramework::Core::ProxyType& jsonResponse) = 0; + virtual ~IEventHandler() = default; + }; + + template + class Transport { + private: + using Channel = CommunicationChannel; + using Entry = typename CommunicationChannel::Entry; + using PendingMap = std::unordered_map; + using EventMap = std::map; + typedef std::function& jsonResponse, bool& enabled)> EventResponseValidatioionFunction; + + class Job : public WPEFramework::Core::IDispatch { + protected: + Job(const WPEFramework::Core::ProxyType& inbound, class Transport* parent) + : _inbound(inbound) + , _parent(parent) + { + } + + public: + Job() = delete; + Job(const Job&) = delete; + Job& operator=(const Job&) = delete; + + ~Job() = default; + + public: + static WPEFramework::Core::ProxyType Create(const WPEFramework::Core::ProxyType& inbound, class Transport* parent); + + void Dispatch() override + { + _parent->Inbound(_inbound); + } + + private: + const WPEFramework::Core::ProxyType _inbound; + class Transport* _parent; + }; + + protected: + static constexpr uint32_t DefaultWaitTime = 10000; + + inline void Announce() { + _channel->Register(*this); + } + + private: + static constexpr const TCHAR* PathPrefix = _T("/"); + public: + Transport() = delete; + Transport(const Transport&) = delete; + Transport& operator=(Transport&) = delete; + Transport(const WPEFramework::Core::URL& url, const uint32_t waitTime) + : _adminLock() + , _connectId(WPEFramework::Core::NodeId(url.Host().Value().c_str(), url.Port().Value())) + , _channel(Channel::Instance(_connectId, ((url.Path().Value().rfind(PathPrefix, 0) == 0) ? url.Path().Value() : string(PathPrefix + url.Path().Value())), url.Query().Value(), true)) + , _eventHandler(nullptr) + , _pendingQueue() + , _scheduledTime(0) + , _waitTime(waitTime) + { + _channel->Register(*this); + } + + virtual ~Transport() + { + _channel->Unregister(*this); + + for (auto& element : _pendingQueue) { + element.second.Abort(element.first); + } + } + + public: + inline bool IsOpen() + { + return _channel->IsOpen(); + } + + void Revoke(const string& eventName) + { + _adminLock.Lock(); + _eventMap.erase(eventName); + _adminLock.Unlock(); + } + + void SetEventHandler(IEventHandler* eventHandler) + { + _eventHandler = eventHandler; + } + + template + uint32_t Invoke(const string& method, const PARAMETERS& parameters, RESPONSE& response) + { + Entry slot; + uint32_t id = _channel->Sequence(); + uint32_t result = Send(method, parameters, id); + if (result == WPEFramework::Core::ERROR_NONE) { + result = WaitForResponse(id, response, _waitTime); + } + + return (FireboltErrorValue(result)); + } + + template + uint32_t Subscribe(const string& eventName, const string& parameters, RESPONSE& response) + { + Entry slot; + uint32_t id = _channel->Sequence(); + uint32_t result = Send(eventName, parameters, id); + if (result == WPEFramework::Core::ERROR_NONE) { + _adminLock.Lock(); + _eventMap.emplace(std::piecewise_construct, + std::forward_as_tuple(eventName), + std::forward_as_tuple(~0)); + _adminLock.Unlock(); + + result = WaitForEventResponse(id, eventName, response, _waitTime); + } + + return (FireboltErrorValue(result)); + } + + uint32_t Unsubscribe(const string& eventName, const string& parameters) + { + Revoke(eventName); + Entry slot; + uint32_t id = _channel->Sequence(); + uint32_t result = Send(eventName, parameters, id); + + return (FireboltErrorValue(result)); + } + + private: + friend Channel; + inline bool IsEvent(const uint32_t id, string& eventName) + { + _adminLock.Lock(); + for (auto& event : _eventMap) { + if (event.second == id) { + eventName = event.first; + break; + } + } + _adminLock.Unlock(); + return (eventName.empty() != true); + } + uint64_t Timed() + { + uint64_t result = ~0; + uint64_t currentTime = WPEFramework::Core::Time::Now().Ticks(); + + // Lets see if some callback are expire. If so trigger and remove... + _adminLock.Lock(); + + typename PendingMap::iterator index = _pendingQueue.begin(); + + while (index != _pendingQueue.end()) { + + if (index->second.Expired(index->first, currentTime, result) == true) { + index = _pendingQueue.erase(index); + } + else { + index++; + } + } + _scheduledTime = (result != static_cast(~0) ? result : 0); + + _adminLock.Unlock(); + + return (_scheduledTime); + } + + virtual void Opened() + { + // Nice to know :-) + } + + void Closed() + { + // Abort any in progress RPC command: + _adminLock.Lock(); + + // See if we issued anything, if so abort it.. + while (_pendingQueue.size() != 0) { + + _pendingQueue.begin()->second.Abort(_pendingQueue.begin()->first); + _pendingQueue.erase(_pendingQueue.begin()); + } + + _adminLock.Unlock(); + } + + uint32_t Submit(const WPEFramework::Core::ProxyType& inbound) + { + uint32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(inbound, this)); + WPEFramework::Core::IWorkerPool::Instance().Submit(job); + return result; + } + + uint32_t Inbound(const WPEFramework::Core::ProxyType& inbound) + { + uint32_t result = WPEFramework::Core::ERROR_INVALID_SIGNATURE; + + ASSERT(inbound.IsValid() == true); + + if ((inbound->Id.IsSet() == true) && (inbound->Result.IsSet() || inbound->Error.IsSet())) { + // Looks like this is a response.. + ASSERT(inbound->Parameters.IsSet() == false); + ASSERT(inbound->Designator.IsSet() == false); + + _adminLock.Lock(); + + // See if we issued this.. + typename PendingMap::iterator index = _pendingQueue.find(inbound->Id.Value()); + + if (index != _pendingQueue.end()) { + + if (index->second.Signal(inbound) == true) { + _pendingQueue.erase(index); + } + + result = WPEFramework::Core::ERROR_NONE; + } else { + + string eventName; + if (IsEvent(inbound->Id.Value(), eventName)) { + _eventHandler->Dispatch(eventName, inbound); + } + + } + _adminLock.Unlock(); + } + + return (result); + } + + + template + uint32_t Send(const string& method, const PARAMETERS& parameters, const uint32_t& id) + { + uint32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + + if ((_channel.IsValid() == true) && (_channel->IsSuspended() == true)) { + result = WPEFramework::Core::ERROR_ASYNC_FAILED; + } + else if (_channel.IsValid() == true) { + + result = WPEFramework::Core::ERROR_ASYNC_FAILED; + + WPEFramework::Core::ProxyType message(Channel::Message()); + message->Id = id; + message->Designator = method; + ToMessage(parameters, message); + + _adminLock.Lock(); + + typename std::pair< typename PendingMap::iterator, bool> newElement = + _pendingQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(id), + std::forward_as_tuple()); + ASSERT(newElement.second == true); + + if (newElement.second == true) { + + _adminLock.Unlock(); + + _channel->Submit(WPEFramework::Core::ProxyType(message)); + + message.Release(); + result = WPEFramework::Core::ERROR_NONE; + } + } + return result; + } + + template + uint32_t WaitForResponse(const uint32_t& id, RESPONSE& response, const uint32_t waitTime) + { + uint32_t result = WPEFramework::Core::ERROR_TIMEDOUT; + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry& slot(index->second); + _adminLock.Unlock(); + + if (slot.WaitForResponse(waitTime) == true) { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + + // See if we have a jsonResponse, maybe it was just the connection + // that closed? + if (jsonResponse.IsValid() == true) { + if (jsonResponse->Error.IsSet() == true) { + result = jsonResponse->Error.Code.Value(); + } + else { + result = WPEFramework::Core::ERROR_NONE; + if ((jsonResponse->Result.IsSet() == true) + && (jsonResponse->Result.Value().empty() == false)) { + FromMessage((INTERFACE*)&response, *jsonResponse); + } + } + } + } else { + result = WPEFramework::Core::ERROR_TIMEDOUT; + } + _adminLock.Lock(); + _pendingQueue.erase(id); + _adminLock.Unlock(); + return result; + } + + static constexpr uint32_t WAITSLOT_TIME = 100; + template + uint32_t WaitForEventResponse(const uint32_t& id, const string& eventName, RESPONSE& response, const uint32_t waitTime) + { + uint32_t result = WPEFramework::Core::ERROR_TIMEDOUT; + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry& slot(index->second); + _adminLock.Unlock(); + + uint8_t waiting = waitTime; + do { + uint32_t waitSlot = (waiting > WAITSLOT_TIME ? WAITSLOT_TIME : waiting); + if (slot.WaitForResponse(waitSlot) == true) { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + + // See if we have a jsonResponse, maybe it was just the connection + // that closed? + if (jsonResponse.IsValid() == true) { + if (jsonResponse->Error.IsSet() == true) { + result = jsonResponse->Error.Code.Value(); + } else { + if ((jsonResponse->Result.IsSet() == true) + && (jsonResponse->Result.Value().empty() == false)) { + result = WPEFramework::Core::ERROR_NONE; + bool enabled; + result = _eventHandler->ValidateResponse(jsonResponse, enabled); + if (result == WPEFramework::Core::ERROR_NONE) { + FromMessage((INTERFACE*)&response, *jsonResponse); + if (enabled) { + _adminLock.Lock(); + typename EventMap::iterator index = _eventMap.find(eventName); + if (index != _eventMap.end()) { + index->second = id; + } + _adminLock.Unlock(); + } + } + } + } + } + } else { + result = WPEFramework::Core::ERROR_TIMEDOUT; + } + waiting -= (waiting == WPEFramework::Core::infinite ? 0 : waitSlot); + } while ((result != WPEFramework::Core::ERROR_NONE) && (waiting > 0 )); + _adminLock.Lock(); + _pendingQueue.erase(id); + _adminLock.Unlock(); + + return result; + } + + public: + void FromMessage(WPEFramework::Core::JSON::IElement* response, const WPEFramework::Core::JSONRPC::Message& message) const + { + response->FromString(message.Result.Value()); + } + + void FromMessage(WPEFramework::Core::JSON::IMessagePack* response, const WPEFramework::Core::JSONRPC::Message& message) const + { + string value = message.Result.Value(); + std::vector result(value.begin(), value.end()); + response->FromBuffer(result); + } + + + private: + + void ToMessage(const string& parameters, WPEFramework::Core::ProxyType& message) const + { + if (parameters.empty() != true) { + message->Parameters = parameters; + } + } + + template + void ToMessage(PARAMETERS& parameters, WPEFramework::Core::ProxyType& message) const + { + ToMessage((INTERFACE*)(¶meters), message); + return; + } + + void ToMessage(WPEFramework::Core::JSON::IMessagePack* parameters, WPEFramework::Core::ProxyType& message) const + { + std::vector values; + parameters->ToBuffer(values); + if (values.empty() != true) { + string strValues(values.begin(), values.end()); + message->Parameters = strValues; + } + return; + } + + void ToMessage(WPEFramework::Core::JSON::IElement* parameters, WPEFramework::Core::ProxyType& message) const + { + string values; + parameters->ToString(values); + if (values.empty() != true) { + message->Parameters = values; + } + return; + } + + uint32_t FireboltErrorValue(const uint32_t error) + { + + uint32_t fireboltError = FireboltSDKErrorUnknown; + switch (error) { + case WPEFramework::Core::ERROR_NONE: + fireboltError = FireboltSDKErrorNone; + break; + case WPEFramework::Core::ERROR_GENERAL: + fireboltError = FireboltSDKErrorGeneral; + break; + case WPEFramework::Core::ERROR_UNAVAILABLE: + fireboltError = FireboltSDKErrorUnavailable; + break; + case WPEFramework::Core::ERROR_TIMEDOUT: + fireboltError = FireboltSDKErrorTimedout; + break; + default: + break; + } + + return fireboltError; + } + + private: + WPEFramework::Core::CriticalSection _adminLock; + WPEFramework::Core::NodeId _connectId; + WPEFramework::Core::ProxyType _channel; + IEventHandler* _eventHandler; + PendingMap _pendingQueue; + EventMap _eventMap; + uint64_t _scheduledTime; + uint32_t _waitTime; + }; +} diff --git a/languages/c-structs/templates/sdk/src/Types.cpp b/languages/c-structs/templates/sdk/src/Types.cpp new file mode 100644 index 00000000..f19cfe63 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/Types.cpp @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" +#include "Types.h" +#include "TypesPriv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +// String Type Handler Interfaces +const char* FireboltTypes_String(FireboltTypes_StringHandle handle) +{ + return ((static_cast(handle))->Value().c_str()); +} + +void FireboltTypes_StringHandle_Release(FireboltTypes_StringHandle handle) +{ + delete static_cast(handle); +} + +#ifdef __cplusplus +} +#endif diff --git a/languages/c-structs/templates/sdk/src/TypesPriv.h b/languages/c-structs/templates/sdk/src/TypesPriv.h new file mode 100644 index 00000000..6e365ec7 --- /dev/null +++ b/languages/c-structs/templates/sdk/src/TypesPriv.h @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +namespace FireboltSDK { +namespace JSON { +class String : public WPEFramework::Core::JSON::String { + using Base = WPEFramework::Core::JSON::String; + public: + String() + : Base() + , _value() + { + } + String(const char value[]) + : Base(value) + , _value(value) + { + } + String& operator=(const char RHS[]) + { + Base::operator = (RHS); + _value = RHS; + return (*this); + } + + public: + const string& Value() const + { + _value = Base::Value(); + return _value; + } + + private: + mutable std::string _value; + }; +} +} diff --git a/languages/c-structs/templates/sdk/test/CMakeLists.txt b/languages/c-structs/templates/sdk/test/CMakeLists.txt new file mode 100644 index 00000000..a56ca20a --- /dev/null +++ b/languages/c-structs/templates/sdk/test/CMakeLists.txt @@ -0,0 +1,86 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.3) + +project(FireboltSDKTests) +project_version(1.0.0) + +set(TESTLIB ${PROJECT_NAME}) + +message("Setup ${TESTLIB} v${PROJECT_VERSION}") + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +find_package(${NAMESPACE}Core CONFIG REQUIRED) + +add_library(${TESTLIB} STATIC OpenRPCTests.cpp) + +target_link_libraries(${TESTLIB} + PUBLIC + ${NAMESPACE}Core::${NAMESPACE}Core + ${FIREBOLT_NAMESPACE}SDK +) + +target_include_directories(${TESTLIB} + PRIVATE + $ + $ +) + +set_target_properties(${TESTLIB} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + LINK_WHAT_YOU_USE TRUE + FRAMEWORK FALSE +) + +install( + TARGETS ${TESTLIB} EXPORT ${TESTLIB}Targets + LIBRARY DESTINATION lib COMPONENT libs + PUBLIC_HEADER DESTINATION include/${FIREBOLT_NAMESPACE}Test COMPONENT devel # headers for mac (note the different component -> different package) + INCLUDES DESTINATION include/${FIREBOLT_NAMESPACE}Test # headers +) + +InstallCMakeConfig(TARGETS ${TESTLIB}) +InstallCMakeConfigs(TARGET ${TESTLIB} DESTINATION ${FIREBOLT_NAMESPACE}) +InstallHeaders(TARGET ${TESTLIB} HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} DESTINATION FireboltTest) +InstallLibraries(TARGET ${TESTLIB} LIBRARIES ${TESTLIB} DESTINATION ${FIREBOLT_NAMESPACE}) + +set(TESTAPP "FireboltSDKTestApp") + +message("Setup ${TESTAPP}") + +add_executable(${TESTAPP} Main.c) + +target_link_libraries(${TESTAPP} + PRIVATE + ${TESTLIB} +) + +target_include_directories(${TESTAPP} + PRIVATE + $ + $ +) + +add_custom_command( + TARGET ${TESTAPP} + POST_BUILD + COMMENT "=================== Installing TestApp ======================" + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/bin + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${TESTAPP} ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/bin +) + diff --git a/languages/c-structs/templates/sdk/test/Main.c b/languages/c-structs/templates/sdk/test/Main.c new file mode 100644 index 00000000..7c4c94a5 --- /dev/null +++ b/languages/c-structs/templates/sdk/test/Main.c @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "OpenRPCCTests.h" + +int __cnt = 0; +int __pass = 0; + +int TotalTests = 0; +int TotalTestsPassed = 0; + +int main() +{ + test_firebolt_create_instance(); + test_firebolt_main(); + + // Calling C function sequences + printf("%s:%s:%d Calling C function tests\n", __FILE__, __func__, __LINE__); + EXECUTE("test_properties_get_device_id", test_properties_get_device_id); + EXECUTE("test_properties_set", test_properties_set); + EXECUTE("test_eventregister_by_providing_callback", test_eventregister_by_providing_callback); + EXECUTE("test_eventregister", test_eventregister); + EXECUTE("test_string_set_get_value", test_string_set_get_value); + + test_firebolt_dispose_instance(); + + printf("TOTAL: %i tests; %i PASSED, %i FAILED\n", TotalTests, TotalTestsPassed, (TotalTests - TotalTestsPassed)); +} + diff --git a/languages/c-structs/templates/sdk/test/Module.cpp b/languages/c-structs/templates/sdk/test/Module.cpp new file mode 100644 index 00000000..d63badc4 --- /dev/null +++ b/languages/c-structs/templates/sdk/test/Module.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/languages/c-structs/templates/sdk/test/Module.h b/languages/c-structs/templates/sdk/test/Module.h new file mode 100644 index 00000000..d5340b68 --- /dev/null +++ b/languages/c-structs/templates/sdk/test/Module.h @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifndef MODULE_NAME +#define MODULE_NAME OpenRPCTestApp +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/languages/c-structs/templates/sdk/test/OpenRPCCTests.h b/languages/c-structs/templates/sdk/test/OpenRPCCTests.h new file mode 100644 index 00000000..7f2fb59e --- /dev/null +++ b/languages/c-structs/templates/sdk/test/OpenRPCCTests.h @@ -0,0 +1,42 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef _OPENRPC_C_TESTS_H +#define _OPENRPC_C_TESTS_H + +#include "TestUtils.h" + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t test_firebolt_create_instance(); +uint32_t test_firebolt_dispose_instance(); + +uint32_t test_firebolt_main(); +uint32_t test_properties_get_device_id(); +uint32_t test_properties_set(); +uint32_t test_eventregister(); +uint32_t test_eventregister_by_providing_callback(); +uint32_t test_string_set_get_value(); + +#ifdef __cplusplus +} +#endif + +#endif // _OPENRPC_C_TESTS_H diff --git a/languages/c-structs/templates/sdk/test/OpenRPCTests.cpp b/languages/c-structs/templates/sdk/test/OpenRPCTests.cpp new file mode 100644 index 00000000..cf9ea5a0 --- /dev/null +++ b/languages/c-structs/templates/sdk/test/OpenRPCTests.cpp @@ -0,0 +1,385 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" +#include "OpenRPCTests.h" +#include "OpenRPCCTests.h" + +namespace WPEFramework { + +ENUM_CONVERSION_BEGIN(::JsonValue::type) + + { JsonValue::type::EMPTY, _TXT("empty") }, + { JsonValue::type::BOOLEAN, _TXT("boolean") }, + { JsonValue::type::NUMBER, _TXT("number") }, + { JsonValue::type::STRING, _TXT("string") }, + +ENUM_CONVERSION_END(::JsonValue::type) + +ENUM_CONVERSION_BEGIN(TestEnum) + { TestEnum::Test1, _TXT("Test1ValueCheck") }, + { TestEnum::Test2, _TXT("Test2ValueCheck") }, + { TestEnum::Test3, _TXT("Test3ValueCheck") }, + { TestEnum::Test4, _TXT("Test4ValueCheck") }, +ENUM_CONVERSION_END(TestEnum) +} +namespace FireboltSDK { + Tests::Tests() + { + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("SubscribeEventWithMultipleCallback"), + std::forward_as_tuple(&SubscribeEventWithMultipleCallback)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("SubscribeEvent"), + std::forward_as_tuple(&SubscribeEvent)); + + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Set UnKnown Method"), + std::forward_as_tuple(&SetUnKnownMethod)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Set LifeCycle Close"), + std::forward_as_tuple(&SetLifeCycleClose)); + + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Get UnKnown Method"), + std::forward_as_tuple(&GetUnKnownMethod)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Get Device Version"), + std::forward_as_tuple(&GetDeviceVersion)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Get Device Id"), + std::forward_as_tuple(&GetDeviceId)); + } + + /* static */ void Tests::PrintJsonObject(const JsonObject::Iterator& iterator) + { + JsonObject::Iterator index = iterator; + while (index.Next() == true) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "Element [%s]: <%s> = \"%s\"\n", + index.Label(), + WPEFramework::Core::EnumerateType(index.Current().Content()).Data(), + index.Current().Value().c_str()); + } + } + + /* static */ uint32_t Tests::GetDeviceId() + { + const string method = _T("device.id"); + WPEFramework::Core::ProxyType response; + uint32_t status = FireboltSDK::Properties::Get(method, response); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status == FireboltSDKErrorNone) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "DeviceId : %s", response->Value().c_str()); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Get %s status = %d\n", method.c_str(), status); + } + + return status; + } + + /*static */ uint32_t Tests::GetDeviceVersion() + { + const string method = _T("device.version"); + WPEFramework::Core::ProxyType response; + uint32_t status = FireboltSDK::Properties::Get(method, response); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status == FireboltSDKErrorNone) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "DeviceVersion"); + PrintJsonObject(response->Variants()); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Get %s status = %d", method.c_str(), status); + } + + return status; + } + + /* static */ uint32_t Tests::GetUnKnownMethod() + { + const string method = _T("get.unknownMethod"); + WPEFramework::Core::ProxyType response; + uint32_t status = FireboltSDK::Properties::Get(method, response); + + EXPECT_NE(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Get %s status = %d\n", method.c_str(), status); + } + + return status; + } + + /* static */ uint32_t Tests::SetLifeCycleClose() + { + const string method = _T("lifecycle.close"); + JsonObject parameters; + parameters["reason"] = "remoteButton"; + uint32_t status = FireboltSDK::Properties::Set(method, parameters); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Set %s status = %d\n", method.c_str(), status); + } + + return status; + } + + /* static */ uint32_t Tests::SetUnKnownMethod() + { + const string method = _T("set.unknownMethod"); + JsonObject parameters; + uint32_t status = FireboltSDK::Properties::Set(method, parameters); + + EXPECT_NE(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Set %s status = %d", method.c_str(), status); + } + + return status; + } + + static void deviceNameChangeCallback(const void* userData, void* response) + { + WPEFramework::Core::ProxyType& jsonResponse = *(static_cast*>(response)); + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "Received a new event: %s", jsonResponse->Value().c_str()); + jsonResponse.Release(); + FireboltSDK::Tests::EventControl* eventControl = reinterpret_cast(const_cast(userData)); + eventControl->NotifyEvent(); + } + + /* static */ uint32_t Tests::SubscribeEvent() + { + FireboltSDK::Tests::EventControl* eventControl = new FireboltSDK::Tests::EventControl(); + const string eventName = _T("device.Name"); + const void* userdata = static_cast(eventControl); + + uint32_t id = 0; + + eventControl->ResetEvent(); + uint32_t status = Properties::Subscribe(eventName, deviceNameChangeCallback, userdata, id); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), + "Set %s status = %d", eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes registered successfully", __func__); + eventControl->WaitForEvent(WPEFramework::Core::infinite); + } + + EXPECT_EQ(Properties::Unsubscribe(eventName, id), FireboltSDKErrorNone); + delete eventControl; + + return status; + } + + template + /* static */ uint32_t Tests::SubscribeEventForC(const string& eventName, CALLBACK& callbackFunc, const void* userdata, uint32_t& id) + { + return Properties::Subscribe(eventName, callbackFunc, userdata, id);; + } + + static void deviceNameChangeMultipleCallback(const void* userData, void* response) + { + WPEFramework::Core::ProxyType& jsonResponse = *(static_cast*>(response)); + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "Received a new event from deviceNameChangeMultipleCallback: %s", jsonResponse->Value().c_str()); + jsonResponse.Release(); + FireboltSDK::Tests::EventControl* eventControl = reinterpret_cast(const_cast(userData)); + eventControl->NotifyEvent(); + } + + /* static */ uint32_t Tests::SubscribeEventWithMultipleCallback() + { + FireboltSDK::Tests::EventControl* eventControl1 = new FireboltSDK::Tests::EventControl(); + const string eventName = _T("device.Name"); + const void* userdata = static_cast(eventControl1); + uint32_t id1 = 0, id2 = 0; + + eventControl1->ResetEvent(); + uint32_t status = Properties::Subscribe(eventName, deviceNameChangeCallback, userdata, id1); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), + "Set %s status = %d", eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes registered successfully", __func__); + } + + if (status == FireboltSDKErrorNone) { + FireboltSDK::Tests::EventControl* eventControl2 = new FireboltSDK::Tests::EventControl(); + userdata = static_cast(eventControl2); + + status = Properties::Subscribe(eventName, deviceNameChangeMultipleCallback, userdata, id2); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Set %s status = %d", eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes registered second callback also successfully", __func__); + eventControl1->WaitForEvent(WPEFramework::Core::infinite); + eventControl2->WaitForEvent(WPEFramework::Core::infinite); + } + EXPECT_EQ(Properties::Unsubscribe(eventName, id1), FireboltSDKErrorNone); + delete eventControl2; + } + EXPECT_EQ(Properties::Unsubscribe(eventName, id2), FireboltSDKErrorNone); + + delete eventControl1; + return status; + } + +} + +#ifdef __cplusplus +extern "C" { +#endif + +uint32_t test_firebolt_create_instance() +{ + FireboltSDK::Accessor::Instance(); +} + +uint32_t test_firebolt_dispose_instance() +{ + FireboltSDK::Accessor::Dispose(); +} + +uint32_t test_firebolt_main() +{ + return FireboltSDK::Tests::Main(); +} + +uint32_t test_properties_get_device_id() +{ + const string method = _T("device.id"); + WPEFramework::Core::ProxyType response; + uint32_t status = FireboltSDK::Properties::Get(method, response); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status == FireboltSDKErrorNone) { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "DeviceId : %s", response->Value().c_str()); + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "Get %s status = %d", method.c_str(), status); + } + + return status; +} + +uint32_t test_properties_set() +{ + const string method = _T("lifecycle.close"); + JsonObject parameters; + parameters["reason"] = "remoteButton"; + uint32_t status = FireboltSDK::Properties::Set(method, parameters); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "Set %s status = %d", method.c_str(), status); + } + + return status; +} + +static void deviceNameChangeCallbackForC(const void* userData, void* response) +{ + WPEFramework::Core::ProxyType& jsonResponse = *(static_cast*>(response)); + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "Received a new event--->: %s", jsonResponse->Value().c_str()); + jsonResponse.Release(); + + FireboltSDK::Tests::EventControl* eventControl = reinterpret_cast(const_cast(userData)); + eventControl->NotifyEvent(); +} + +uint32_t test_eventregister() +{ + FireboltSDK::Tests::EventControl* eventControl = new FireboltSDK::Tests::EventControl(); + JsonObject parameters; + + const string eventName = _T("device.Name"); + const void* userdata = static_cast(eventControl); + uint32_t id = 0; + + eventControl->ResetEvent(); + uint32_t status = FireboltSDK::Properties::Subscribe(eventName, deviceNameChangeCallbackForC, userdata, id); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "%s Set %s status = %d", __func__, eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "%s Yes registered successfully", __func__); + eventControl->WaitForEvent(WPEFramework::Core::infinite); + } + + delete eventControl; + EXPECT_EQ(FireboltSDK::Properties::Unsubscribe(eventName, id), FireboltSDKErrorNone); + + return status; +} + +uint32_t test_eventregister_by_providing_callback() +{ + FireboltSDK::Tests::EventControl* eventControl = new FireboltSDK::Tests::EventControl(); + + const string eventName = _T("device.Name"); + const void* userdata = static_cast(eventControl); + uint32_t id = 0; + + eventControl->ResetEvent(); + uint32_t status = FireboltSDK::Tests::SubscribeEventForC(eventName, deviceNameChangeCallbackForC, userdata, id); + + EXPECT_EQ(status, FireboltSDKErrorNone); + if (status != FireboltSDKErrorNone) { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "%s Set %s status = %d", __func__, eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, "ctest", + "%s Yes registered successfully", __func__); + eventControl->WaitForEvent(WPEFramework::Core::infinite); + } + + delete eventControl; + EXPECT_EQ(FireboltSDK::Properties::Unsubscribe(eventName, id), FireboltSDKErrorNone); +} + +#include "TypesPriv.h" +uint32_t test_string_set_get_value() +{ + uint32_t status = FireboltSDKErrorNone; + FireboltSDK::String* str = new FireboltSDK::String("testString"); + void* handle = static_cast(str); + + const char* value = FireboltTypes_String(handle); + EXPECT_EQ(strncmp(value, str->Value().c_str(), str->Value().length()), 0); + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, "ctest", + " ---> type name = %s %s", str->Value().c_str(), value); + + WPEFramework::Core::JSON::EnumType<::TestEnum> testEnum = Test4; + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, "ctest", + " EnumTest = %d %s", testEnum.Value(), testEnum.Data()); + FireboltTypes_StringHandle_Release(handle); + return status; +} + +#ifdef __cplusplus +} +#endif diff --git a/languages/c-structs/templates/sdk/test/OpenRPCTests.h b/languages/c-structs/templates/sdk/test/OpenRPCTests.h new file mode 100644 index 00000000..00abd364 --- /dev/null +++ b/languages/c-structs/templates/sdk/test/OpenRPCTests.h @@ -0,0 +1,99 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "TestUtils.h" +#include "Firebolt.h" + +namespace FireboltSDK { + typedef uint32_t (*Func)(); + + class Tests { + public: + class EventControl { + public: + EventControl() + : _event(false, true) + { + } + ~EventControl() = default; + + public: + void NotifyEvent() + { + _event.SetEvent(); + } + uint32_t WaitForEvent(uint32_t waitTime) + { + return _event.Lock(waitTime); + } + void ResetEvent() + { + _event.ResetEvent(); + } + private: + WPEFramework::Core::Event _event; + }; + + private: + typedef std::unordered_map TestFunctionMap; + + public: + Tests(); + virtual ~Tests() = default; + + inline TestFunctionMap& TestList() + { + return _functionMap; + } + + template + static uint32_t Main() + { + TESTS fireboltTest; + for (auto i = fireboltTest.TestList().begin(); i != fireboltTest.TestList().end(); i++) { + EXECUTE(i->first.c_str(), i->second); + } + + printf("TOTAL: %i tests; %i PASSED, %i FAILED\n", TotalTests, TotalTestsPassed, (TotalTests - TotalTestsPassed)); + + return 0; + } + + static uint32_t GetDeviceId(); + static uint32_t GetDeviceVersion(); + static uint32_t GetUnKnownMethod(); + + static uint32_t SetLifeCycleClose(); + static uint32_t SetUnKnownMethod(); + + static uint32_t SubscribeEvent(); + static uint32_t SubscribeEventWithMultipleCallback(); + + template + static uint32_t SubscribeEventForC(const string& eventName, CALLBACK& callbackFunc, const void* userdata, uint32_t& id); + + protected: + static void PrintJsonObject(const JsonObject::Iterator& iterator); + + protected: + std::list menu; + TestFunctionMap _functionMap; + }; +} diff --git a/languages/c-structs/templates/sdk/test/TestUtils.h b/languages/c-structs/templates/sdk/test/TestUtils.h new file mode 100644 index 00000000..8c3cb732 --- /dev/null +++ b/languages/c-structs/templates/sdk/test/TestUtils.h @@ -0,0 +1,38 @@ +#ifndef _TEST_UTILS_H +#define _TEST_UTILS_H + +#include +#include +#include + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define _RESULT(expr, exprorig, result) if (expr) { printf(" SUCCESS: %s\n", #exprorig); __pass++; } else printf(" FAILED: %s, actual: %u\n", #exprorig, result) +#define _EVAL(result, expected, op) do { __cnt++; uint32_t resval = ((uint32_t)(result)); uint32_t expval = ((uint32_t)(expected)); _RESULT(resval op expval, result op expected, resval); } while(0) +#define _HEAD(name) printf("\n======== %s\n", name); __cnt = 0; __pass = 0 +#define _FOOT(name) printf("\n======== %s - %i PASSED, %i FAILED\n", name, __pass, (__cnt - __pass)); TotalTests += __cnt; TotalTestsPassed += __pass; + +#define EXECUTE(name, test) do { _HEAD(name); test(); _FOOT(name); printf("\n"); } while(0) +#define EXPECT_EQ(result, expected) _EVAL(result, expected, ==) +#define EXPECT_NE(result, expected) _EVAL(result, expected, !=) +#define EXPECT_LT(result, expected) _EVAL(result, expected, <) +#define EXPECT_LE(result, expected) _EVAL(result, expected, <=) +#define EXPECT_GT(result, expected) _EVAL(result, expected, >) +#define EXPECT_GE(result, expected) _EVAL(result, expected, >=) + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cnt; +extern int __pass; + +extern int TotalTests ; +extern int TotalTestsPassed; + +#ifdef __cplusplus +} +#endif + +#endif // _TEST_UTILS_H diff --git a/languages/c-structs/templates/sections/accessors.c b/languages/c-structs/templates/sections/accessors.c new file mode 100644 index 00000000..1c790810 --- /dev/null +++ b/languages/c-structs/templates/sections/accessors.c @@ -0,0 +1,2 @@ +// Accessors +${schema.list} diff --git a/languages/c-structs/templates/sections/declarations.c b/languages/c-structs/templates/sections/declarations.c new file mode 100644 index 00000000..b3ef974d --- /dev/null +++ b/languages/c-structs/templates/sections/declarations.c @@ -0,0 +1 @@ +${declaration.list} diff --git a/languages/c-structs/templates/sections/enums.c b/languages/c-structs/templates/sections/enums.c new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/c-structs/templates/sections/enums.c @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/c-structs/templates/sections/events.c b/languages/c-structs/templates/sections/events.c new file mode 100644 index 00000000..5be10409 --- /dev/null +++ b/languages/c-structs/templates/sections/events.c @@ -0,0 +1 @@ +${event.list} diff --git a/languages/c-structs/templates/sections/methods.c b/languages/c-structs/templates/sections/methods.c new file mode 100644 index 00000000..3ab606c0 --- /dev/null +++ b/languages/c-structs/templates/sections/methods.c @@ -0,0 +1,4 @@ + +// Methods + +${method.list} diff --git a/languages/c-structs/templates/sections/methods_accessors.c b/languages/c-structs/templates/sections/methods_accessors.c new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/c-structs/templates/sections/methods_accessors.c @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/c-structs/templates/sections/methods_types.c b/languages/c-structs/templates/sections/methods_types.c new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/c-structs/templates/sections/methods_types.c @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/c-structs/templates/sections/provider-interfaces.c b/languages/c-structs/templates/sections/provider-interfaces.c new file mode 100644 index 00000000..418d7abf --- /dev/null +++ b/languages/c-structs/templates/sections/provider-interfaces.c @@ -0,0 +1,11 @@ + // Provider Interfaces + + interface ProviderSession { + correlationId(): string // Returns the correlation id of the current provider session + } + + interface FocusableProviderSession extends ProviderSession { + focus(): Promise // Requests that the provider app be moved into focus to prevent a user experience + } + + ${providers.list} \ No newline at end of file diff --git a/languages/c-structs/templates/sections/schemas.c b/languages/c-structs/templates/sections/schemas.c new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/c-structs/templates/sections/schemas.c @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/c-structs/templates/sections/types.c b/languages/c-structs/templates/sections/types.c new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/c-structs/templates/sections/types.c @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/c-structs/templates/types/anyOf.c b/languages/c-structs/templates/types/anyOf.c new file mode 100644 index 00000000..9cc8aaef --- /dev/null +++ b/languages/c-structs/templates/types/anyOf.c @@ -0,0 +1 @@ +/* AnyOf is not supported in C */ \ No newline at end of file diff --git a/languages/c-structs/templates/types/array.c b/languages/c-structs/templates/types/array.c new file mode 100644 index 00000000..6f532328 --- /dev/null +++ b/languages/c-structs/templates/types/array.c @@ -0,0 +1 @@ +${title} * \ No newline at end of file diff --git a/languages/c-structs/templates/types/const.c b/languages/c-structs/templates/types/const.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c-structs/templates/types/default.c b/languages/c-structs/templates/types/default.c new file mode 100644 index 00000000..38b05042 --- /dev/null +++ b/languages/c-structs/templates/types/default.c @@ -0,0 +1,4 @@ +/* + * ${title} - ${description} + */ +typedef ${shape} ${title}; diff --git a/languages/c-structs/templates/types/enum.cpp b/languages/c-structs/templates/types/enum.cpp new file mode 100644 index 00000000..6776b41a --- /dev/null +++ b/languages/c-structs/templates/types/enum.cpp @@ -0,0 +1,4 @@ + /* ${title} ${description} */ + ENUM_CONVERSION_BEGIN(${name}) + { ${NAME}_${key}, _T("${value}") }, + ENUM_CONVERSION_END(${name}) diff --git a/languages/c-structs/templates/types/enum.h b/languages/c-structs/templates/types/enum.h new file mode 100644 index 00000000..92108c75 --- /dev/null +++ b/languages/c-structs/templates/types/enum.h @@ -0,0 +1,4 @@ +/* ${title} ${description} */ +typedef enum { + ${NAME}_${key}, +} ${name}; diff --git a/languages/c-structs/templates/types/object.c b/languages/c-structs/templates/types/object.c new file mode 100644 index 00000000..e6d948cd --- /dev/null +++ b/languages/c-structs/templates/types/object.c @@ -0,0 +1,3 @@ +struct { + ${properties} +} \ No newline at end of file diff --git a/languages/c-structs/templates/types/primitive.c b/languages/c-structs/templates/types/primitive.c new file mode 100644 index 00000000..21ae259c --- /dev/null +++ b/languages/c-structs/templates/types/primitive.c @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/c-structs/templates/types/property.c b/languages/c-structs/templates/types/property.c new file mode 100644 index 00000000..7bd100e4 --- /dev/null +++ b/languages/c-structs/templates/types/property.c @@ -0,0 +1 @@ +${title} ${property}; \ No newline at end of file diff --git a/languages/c-structs/templates/types/ref.c b/languages/c-structs/templates/types/ref.c new file mode 100644 index 00000000..41d15c96 --- /dev/null +++ b/languages/c-structs/templates/types/ref.c @@ -0,0 +1 @@ +${title} \ No newline at end of file diff --git a/languages/c-structs/templates/types/title.c b/languages/c-structs/templates/types/title.c new file mode 100644 index 00000000..ca6b6897 --- /dev/null +++ b/languages/c-structs/templates/types/title.c @@ -0,0 +1 @@ +F${info.Title}_${Title} \ No newline at end of file diff --git a/languages/c-structs/templates/types/tuple.c b/languages/c-structs/templates/types/tuple.c new file mode 100644 index 00000000..e6d948cd --- /dev/null +++ b/languages/c-structs/templates/types/tuple.c @@ -0,0 +1,3 @@ +struct { + ${properties} +} \ No newline at end of file diff --git a/languages/c/templates/json-types/additionalProperties.c b/languages/c/templates/json-types/additionalProperties.c new file mode 100644 index 00000000..b814e5fb --- /dev/null +++ b/languages/c/templates/json-types/additionalProperties.c @@ -0,0 +1 @@ +// need cpp code to init, get, set, clear additional properties... \ No newline at end of file diff --git a/languages/c/templates/json-types/anyOf.c b/languages/c/templates/json-types/anyOf.c new file mode 100644 index 00000000..a2682179 --- /dev/null +++ b/languages/c/templates/json-types/anyOf.c @@ -0,0 +1 @@ +/* AnyOf is not supported in C: ${title} */ \ No newline at end of file diff --git a/languages/c/templates/json-types/array.c b/languages/c/templates/json-types/array.c new file mode 100644 index 00000000..508a9eac --- /dev/null +++ b/languages/c/templates/json-types/array.c @@ -0,0 +1,4 @@ +uint32_t ${info.Title}_${Title}Array_Size(${type} handle); +${type} ${title}Array_Get(${type} handle, uint32_t index); +void ${info.Title}_${Title}Array_Add(${propertyType} handle, ${valueType} value); +void ${info.Title}_${Title}Array_Clear(${propertyType} handle); diff --git a/languages/c/templates/json-types/boolean.c b/languages/c/templates/json-types/boolean.c new file mode 100644 index 00000000..18bbeec2 --- /dev/null +++ b/languages/c/templates/json-types/boolean.c @@ -0,0 +1 @@ +WPEFramework::Core::JSON::Boolean \ No newline at end of file diff --git a/languages/c/templates/json-types/const.c b/languages/c/templates/json-types/const.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c/templates/json-types/default.cpp b/languages/c/templates/json-types/default.cpp new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/c/templates/json-types/default.cpp @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/c/templates/json-types/enum.cpp b/languages/c/templates/json-types/enum.cpp new file mode 100644 index 00000000..6776b41a --- /dev/null +++ b/languages/c/templates/json-types/enum.cpp @@ -0,0 +1,4 @@ + /* ${title} ${description} */ + ENUM_CONVERSION_BEGIN(${name}) + { ${NAME}_${key}, _T("${value}") }, + ENUM_CONVERSION_END(${name}) diff --git a/languages/c/templates/json-types/enum.h b/languages/c/templates/json-types/enum.h new file mode 100644 index 00000000..92108c75 --- /dev/null +++ b/languages/c/templates/json-types/enum.h @@ -0,0 +1,4 @@ +/* ${title} ${description} */ +typedef enum { + ${NAME}_${key}, +} ${name}; diff --git a/languages/c/templates/json-types/float.c b/languages/c/templates/json-types/float.c new file mode 100644 index 00000000..c38bca91 --- /dev/null +++ b/languages/c/templates/json-types/float.c @@ -0,0 +1 @@ +WPEFramework::Core::JSON::Float \ No newline at end of file diff --git a/languages/c/templates/json-types/integer.c b/languages/c/templates/json-types/integer.c new file mode 100644 index 00000000..b57fe26e --- /dev/null +++ b/languages/c/templates/json-types/integer.c @@ -0,0 +1 @@ +WPEFramework::Core::JSON::DecSInt32 \ No newline at end of file diff --git a/languages/c/templates/json-types/namespace.c b/languages/c/templates/json-types/namespace.c new file mode 100644 index 00000000..8ea9d7de --- /dev/null +++ b/languages/c/templates/json-types/namespace.c @@ -0,0 +1 @@ +FireboltSDK::${info.Title}:: \ No newline at end of file diff --git a/languages/c/templates/json-types/object.cpp b/languages/c/templates/json-types/object.cpp new file mode 100644 index 00000000..db24d12c --- /dev/null +++ b/languages/c/templates/json-types/object.cpp @@ -0,0 +1,25 @@ + class ${title}: public WPEFramework::Core::JSON::Container { + public: + ~${title}() override = default; + + public: + ${title}() + : WPEFramework::Core::JSON::Container() + { + ${properties.register} + } + + ${title}(const ${title}& other) + { + ${properties.assign} + } + + ${title}& operator=(const ${title}& other) + { + ${properties.assign} + return (*this); + } + + public: + ${properties} + }; diff --git a/languages/c/templates/json-types/primitive.c b/languages/c/templates/json-types/primitive.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c/templates/json-types/property-assign.cpp b/languages/c/templates/json-types/property-assign.cpp new file mode 100644 index 00000000..88795136 --- /dev/null +++ b/languages/c/templates/json-types/property-assign.cpp @@ -0,0 +1,2 @@ + Add(_T("${property}"), &${Property}); + ${Property} = other.${Property} diff --git a/languages/c/templates/json-types/property-register.cpp b/languages/c/templates/json-types/property-register.cpp new file mode 100644 index 00000000..400b4fd5 --- /dev/null +++ b/languages/c/templates/json-types/property-register.cpp @@ -0,0 +1,2 @@ + Add(_T("${property}"), &${Property}); + \ No newline at end of file diff --git a/languages/c/templates/json-types/property.cpp b/languages/c/templates/json-types/property.cpp new file mode 100644 index 00000000..49f79c0e --- /dev/null +++ b/languages/c/templates/json-types/property.cpp @@ -0,0 +1 @@ + ${title} ${Property}; \ No newline at end of file diff --git a/languages/c/templates/json-types/ref.c b/languages/c/templates/json-types/ref.c new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/c/templates/json-types/ref.c @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/c/templates/json-types/string.c b/languages/c/templates/json-types/string.c new file mode 100644 index 00000000..2d60a3c8 --- /dev/null +++ b/languages/c/templates/json-types/string.c @@ -0,0 +1 @@ +FireboltSDK::JSON::String \ No newline at end of file diff --git a/languages/c/templates/json-types/title.c b/languages/c/templates/json-types/title.c new file mode 100644 index 00000000..ae1512e5 --- /dev/null +++ b/languages/c/templates/json-types/title.c @@ -0,0 +1 @@ +JsonData_${Title} \ No newline at end of file diff --git a/languages/c/templates/json-types/tuple.c b/languages/c/templates/json-types/tuple.c new file mode 100644 index 00000000..db24d12c --- /dev/null +++ b/languages/c/templates/json-types/tuple.c @@ -0,0 +1,25 @@ + class ${title}: public WPEFramework::Core::JSON::Container { + public: + ~${title}() override = default; + + public: + ${title}() + : WPEFramework::Core::JSON::Container() + { + ${properties.register} + } + + ${title}(const ${title}& other) + { + ${properties.assign} + } + + ${title}& operator=(const ${title}& other) + { + ${properties.assign} + return (*this); + } + + public: + ${properties} + }; diff --git a/languages/c/templates/json-types/types/object.c b/languages/c/templates/json-types/types/object.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c/templates/json-types/x-method.c b/languages/c/templates/json-types/x-method.c new file mode 100644 index 00000000..573c9f8e --- /dev/null +++ b/languages/c/templates/json-types/x-method.c @@ -0,0 +1 @@ +void* \ No newline at end of file diff --git a/languages/c/templates/language/enum-item.c b/languages/c/templates/language/enum-item.c new file mode 100644 index 00000000..79aabbeb --- /dev/null +++ b/languages/c/templates/language/enum-item.c @@ -0,0 +1 @@ + ${key} diff --git a/languages/c/templates/language/enum.c b/languages/c/templates/language/enum.c new file mode 100644 index 00000000..0551b99c --- /dev/null +++ b/languages/c/templates/language/enum.c @@ -0,0 +1,3 @@ +typedef enum { + ${items} +} F${info.title}_${title}; diff --git a/languages/c/templates/language/parameter.c b/languages/c/templates/language/parameter.c new file mode 100644 index 00000000..2ff7a678 --- /dev/null +++ b/languages/c/templates/language/parameter.c @@ -0,0 +1 @@ +${type} ${name} \ No newline at end of file diff --git a/languages/c/templates/language/schema-item.c b/languages/c/templates/language/schema-item.c new file mode 100644 index 00000000..ecded7d5 --- /dev/null +++ b/languages/c/templates/language/schema-item.c @@ -0,0 +1 @@ + ${type} ${property}; diff --git a/languages/c/templates/language/schema.c b/languages/c/templates/language/schema.c new file mode 100644 index 00000000..22760540 --- /dev/null +++ b/languages/c/templates/language/schema.c @@ -0,0 +1,3 @@ +typedef struct { + ${properties} +} F${info.title}_${title}; diff --git a/languages/c/templates/parameter-serialization/boolean.cpp b/languages/c/templates/parameter-serialization/boolean.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c/templates/parameter-serialization/boolean.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c/templates/parameter-serialization/default.cpp b/languages/c/templates/parameter-serialization/default.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c/templates/parameter-serialization/default.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c/templates/parameter-serialization/object.cpp b/languages/c/templates/parameter-serialization/object.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c/templates/parameter-serialization/object.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c/templates/parameter-serialization/primitive.cpp b/languages/c/templates/parameter-serialization/primitive.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c/templates/parameter-serialization/primitive.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c/templates/parameter-serialization/string.cpp b/languages/c/templates/parameter-serialization/string.cpp new file mode 100644 index 00000000..f924ac94 --- /dev/null +++ b/languages/c/templates/parameter-serialization/string.cpp @@ -0,0 +1,2 @@ + WPEFramework::Core::JSON::Variant ${Property} = ${property}; + jsonParameters.Set(_T("${property}"), ${Property}); diff --git a/languages/c/templates/parameters/optional.c b/languages/c/templates/parameters/optional.c new file mode 100644 index 00000000..e6e3b8ba --- /dev/null +++ b/languages/c/templates/parameters/optional.c @@ -0,0 +1 @@ +${method.param.type} ${method.param.name} \ No newline at end of file diff --git a/languages/c/templates/parameters/result.c b/languages/c/templates/parameters/result.c new file mode 100644 index 00000000..699c8b5b --- /dev/null +++ b/languages/c/templates/parameters/result.c @@ -0,0 +1 @@ +${method.param.type} *${method.param.name} \ No newline at end of file diff --git a/languages/c/templates/result-instantiation/boolean.cpp b/languages/c/templates/result-instantiation/boolean.cpp new file mode 100644 index 00000000..036b4a7d --- /dev/null +++ b/languages/c/templates/result-instantiation/boolean.cpp @@ -0,0 +1,2 @@ + *${property} = jsonResult.Value(); + \ No newline at end of file diff --git a/languages/c/templates/result-instantiation/default.cpp b/languages/c/templates/result-instantiation/default.cpp new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/c/templates/result-instantiation/default.cpp @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/c/templates/result-instantiation/object.cpp b/languages/c/templates/result-instantiation/object.cpp new file mode 100644 index 00000000..dcf0b333 --- /dev/null +++ b/languages/c/templates/result-instantiation/object.cpp @@ -0,0 +1,4 @@ + WPEFramework::Core::ProxyType* resultPtr = new WPEFramework::Core::ProxyType(); + *resultPtr = WPEFramework::Core::ProxyType::Create(); + *(*resultPtr) = jsonResult; + *${property} = static_cast<${info.Title}_${title}>(resultPtr); diff --git a/languages/c/templates/result-instantiation/primitive.cpp b/languages/c/templates/result-instantiation/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/c/templates/result-instantiation/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/c/templates/result-instantiation/string.cpp b/languages/c/templates/result-instantiation/string.cpp new file mode 100644 index 00000000..b6c9dcb5 --- /dev/null +++ b/languages/c/templates/result-instantiation/string.cpp @@ -0,0 +1,2 @@ + FireboltSDK::JSON::String* strResult = new FireboltSDK::JSON::String(jsonResult); + *value = static_cast(strResult); diff --git a/languages/c/templates/sections/enums.c b/languages/c/templates/sections/enums.c new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/c/templates/sections/enums.c @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/c/templates/types/additionalProperties.c b/languages/c/templates/types/additionalProperties.c new file mode 100644 index 00000000..ffe5ab54 --- /dev/null +++ b/languages/c/templates/types/additionalProperties.c @@ -0,0 +1,4 @@ + uint32_t ${parent.title}_KeysCount(${parent.title} handle); + void ${parent.title}_AddKey(${parent.title} handle, char* key, ${title} value); + void ${parent.title}_RemoveKey(${parent.title} handle, char* key); + ${title} ${parent.title}_FindKey(${parent.title} handle, char* key); diff --git a/languages/c/templates/types/anyOf.c b/languages/c/templates/types/anyOf.c new file mode 100644 index 00000000..a2682179 --- /dev/null +++ b/languages/c/templates/types/anyOf.c @@ -0,0 +1 @@ +/* AnyOf is not supported in C: ${title} */ \ No newline at end of file diff --git a/languages/c/templates/types/array.c b/languages/c/templates/types/array.c new file mode 100644 index 00000000..0b9863c2 --- /dev/null +++ b/languages/c/templates/types/array.c @@ -0,0 +1,4 @@ +uint32_t ${info.Title}_${Title}Array_Size(${type} handle); +${type} ${title}Array_Get(${type} handle, uint32_t index); +void ${info.Title}_${Title}Array_Add(${type} handle, ${type} value); +void ${info.Title}_${Title}Array_Clear(${type} handle); diff --git a/languages/c/templates/types/const.c b/languages/c/templates/types/const.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c/templates/types/default.c b/languages/c/templates/types/default.c new file mode 100644 index 00000000..1f1fe89d --- /dev/null +++ b/languages/c/templates/types/default.c @@ -0,0 +1,4 @@ +/* + * ${title} - ${description} + */ +${shape} diff --git a/languages/c/templates/types/object.c b/languages/c/templates/types/object.c new file mode 100644 index 00000000..7ff6a7a9 --- /dev/null +++ b/languages/c/templates/types/object.c @@ -0,0 +1,10 @@ +/* ${title} */ +typedef void* ${title}; +${title} ${title}_Create(void); +void ${title}_Addref(${title} handle); +void ${title}_Release(${title} handle); +bool ${title}_IsValid(${title} handle); + + +// Property Accessors: +${properties} \ No newline at end of file diff --git a/languages/c/templates/types/primitive.c b/languages/c/templates/types/primitive.c new file mode 100644 index 00000000..e69de29b diff --git a/languages/c/templates/types/property.c b/languages/c/templates/types/property.c new file mode 100644 index 00000000..abbba725 --- /dev/null +++ b/languages/c/templates/types/property.c @@ -0,0 +1,6 @@ +/* ${property} */ +${title} ${parent.title}_Get_${Property}(${parent.title} handle); +void ${parent.title}_Set_${Property}(${parent.title} handle, ${title} ${property}); +${if.optional}bool ${parent.title}_Has_${Property}(${parent.title} handle); +void ${parent.title}_Clear_${Property}(${parent.title} handle, ${title} ${property}); +${end.if.optional} \ No newline at end of file diff --git a/languages/c/templates/types/ref.c b/languages/c/templates/types/ref.c new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/c/templates/types/ref.c @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/c/templates/types/title.c b/languages/c/templates/types/title.c new file mode 100644 index 00000000..ca6b6897 --- /dev/null +++ b/languages/c/templates/types/title.c @@ -0,0 +1 @@ +F${info.Title}_${Title} \ No newline at end of file diff --git a/languages/c/templates/types/tuple.c b/languages/c/templates/types/tuple.c new file mode 100644 index 00000000..96a04034 --- /dev/null +++ b/languages/c/templates/types/tuple.c @@ -0,0 +1,8 @@ +/* ${title} */ +typedef void* ${title}; +${title} ${title}_Create(void); +void ${title}_Addref(${title} handle); +void ${title}_Release(${title} handle); +bool ${title}_IsValid(${title} handle); + +${properties} diff --git a/languages/c/templates/types/x-method.c b/languages/c/templates/types/x-method.c new file mode 100644 index 00000000..573c9f8e --- /dev/null +++ b/languages/c/templates/types/x-method.c @@ -0,0 +1 @@ +void* \ No newline at end of file diff --git a/languages/cpp/language.config.json b/languages/cpp/language.config.json new file mode 100644 index 00000000..724a1359 --- /dev/null +++ b/languages/cpp/language.config.json @@ -0,0 +1,34 @@ +{ + "name": "CPlusPlus", + "langcode": "cpp", + "createModuleDirectories": false, + "extractSubSchemas": true, + "unwrapResultObjects": false, + "createPolymorphicMethods": true, + "excludeDeclarations": true, + "extractProviderSchema": true, + "aggregateFiles": [ + "/include/firebolt.h", + "/src/firebolt.cpp" + ], + "templatesPerModule": [ + "/include/module.h", + "/src/module_impl.h", + "/src/module_impl.cpp" + ], + "templatesPerSchema": [ + "/include/common/module.h", + "/src/module_common.cpp", + "/src/jsondata_module.h" + ], + "persistPermission": true, + "primitives": {}, + "langVersion" : "c++17", + "additionalSchemaTemplates": [ "json-types" ], + "additionalMethodTemplates": [ "declarations", "declarations-override" ], + "templateExtensionMap": { + "methods": [ "impl.cpp", "cpp" ], + "declarations": [ "h" ], + "declarations-override": [ "impl.h" ] + } +} diff --git a/languages/cpp/src/shared/CMakeLists.txt b/languages/cpp/src/shared/CMakeLists.txt new file mode 100644 index 00000000..fe7a0085 --- /dev/null +++ b/languages/cpp/src/shared/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.3) + +project(Firebolt) + +set(FIREBOLT_TRANSPORT_WAITTIME 1000 CACHE STRING "Maximum time to wait for Transport layer to get response") +set(FIREBOLT_LOGLEVEL "Info" CACHE STRING "Log level to be enabled") +option(FIREBOLT_ENABLE_STATIC_LIB "Create Firebolt library as Static library" OFF) +option(ENABLE_TESTS "Build openrpc native test" OFF) + +if (FIREBOLT_ENABLE_STATIC_LIB) + set(FIREBOLT_LIBRARY_TYPE STATIC) +else () + set(FIREBOLT_LIBRARY_TYPE SHARED) +endif () + +if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${SYSROOT_PATH}/usr" CACHE INTERNAL "" FORCE) + set(CMAKE_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) +endif() + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/cmake" + "${SYSROOT_PATH}/usr/lib/cmake" + "${SYSROOT_PATH}/tools/cmake") +include(HelperFunctions) + +set(FIREBOLT_NAMESPACE ${PROJECT_NAME} CACHE STRING "Namespace of the project") + +find_package(WPEFramework CONFIG REQUIRED) + +add_subdirectory(src) + +if (ENABLE_TESTS) + add_subdirectory(test) +endif() + +# make sure others can make use cmake settings of Firebolt OpenRPC +configure_file( "${CMAKE_SOURCE_DIR}/cmake/project.cmake.in" + "${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}Config.cmake" + @ONLY) diff --git a/languages/cpp/src/shared/cmake/HelperFunctions.cmake b/languages/cpp/src/shared/cmake/HelperFunctions.cmake new file mode 100644 index 00000000..4b5c4e09 --- /dev/null +++ b/languages/cpp/src/shared/cmake/HelperFunctions.cmake @@ -0,0 +1,72 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +macro(GetSubDirs subdirs currentdir) + file(GLOB subdirectories RELATIVE ${currentdir} ${currentdir}/*) + set(subdirs "") + foreach(subdir ${subdirectories}) + if (IS_DIRECTORY ${currentdir}/${subdir}) + list(APPEND subdirs ${subdir}) + endif() + endforeach() +endmacro() + +function(InstallHeaders) + set(optionsArgs EXCLUDE_ROOT_DIR) + set(oneValueArgs TARGET NAMESPACE SOURCE DESTINATION) + set(multiValueArgs HEADERS) + + cmake_parse_arguments(Argument "${optionsArgs}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) + if (Argument_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to InstallHeaders(): \"${Argument_UNPARSED_ARGUMENTS}\"") + endif() + if (Argument_HEADERS) + add_custom_command( + TARGET ${Argument_TARGET} + POST_BUILD + COMMENT "=================== Installing Headers ======================" + ) + foreach(directory ${Argument_HEADERS}) + if (Argument_EXCLUDE_ROOT_DIR) + set(destination ${Argument_DESTINATION}) + else() + set(destination ${Argument_DESTINATION}/${directory}) + endif() + + if (Argument_SOURCE) + set(source ${Argument_SOURCE}) + else() + set(source ${CMAKE_CURRENT_LIST_DIR}) + endif() + + GetSubDirs(subdirs ${source}/${directory}) + list(APPEND subdirs ${directory}) + + foreach(subdir ${subdirs}) + if (NOT subdir STREQUAL ".") + set(dest ${destination}/${subdir}) + file(GLOB headers "${source}/${directory}/${subdir}/*.h") + if (headers) + install( + DIRECTORY "${source}/${directory}/${subdir}" + DESTINATION include/${dest} + FILES_MATCHING PATTERN "*.h") + endif() + endif() + endforeach(subdir) + endforeach(directory) + endif() +endfunction(InstallHeaders) diff --git a/languages/cpp/src/shared/cmake/project.cmake.in b/languages/cpp/src/shared/cmake/project.cmake.in new file mode 100644 index 00000000..eca32f8c --- /dev/null +++ b/languages/cpp/src/shared/cmake/project.cmake.in @@ -0,0 +1,35 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +set(FIREBOLT_NAMESPACE "@FIREBOLT_NAMESPACE@" CACHE INTERNAL "" FORCE) +set("${FIREBOLT_NAMESPACE}_FOUND" TRUE CACHE INTERNAL "" FORCE) + +list(APPEND CMAKE_MODULE_PATH + "${CMAKE_SOURCE_DIR}/cmake" + "${SYSROOT_PATH}/usr/lib/cmake" + "${SYSROOT_PATH}/usr/lib/cmake/Firebolt" + "${SYSROOT_PATH}/tools/cmake") + +if (NOT DEFINED CMAKE_PREFIX_PATH) + set(CMAKE_PREFIX_PATH ${SYSROOT_PATH}/usr/lib/cmake CACHE INTERNAL "" FORCE) +endif() + +if (FIREBOLT_ENABLE_STATIC_LIB) + set(FIREBOLT_LIBRARY_TYPE STATIC) +else () + set(FIREBOLT_LIBRARY_TYPE SHARED) +endif () + diff --git a/languages/cpp/src/shared/include/error.h b/languages/cpp/src/shared/include/error.h new file mode 100644 index 00000000..b02859b3 --- /dev/null +++ b/languages/cpp/src/shared/include/error.h @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +namespace Firebolt { + + enum class Error : int32_t { + None = 0, + General = 1, + Timedout = 2, + NotConnected = 3, + AlreadyConnected = 4, + //AuthenticationError, ? + InvalidRequest = -32600, + MethodNotFound = -32601, + InvalidParams = -32602, + CapabilityNotAvaialbale = -50300, + CapabilityNotSupported = -50100, + CapabilityGet = -50200, + CapabilityNotPermitted = -40300, + }; + +} diff --git a/languages/cpp/src/shared/include/types.h b/languages/cpp/src/shared/include/types.h new file mode 100644 index 00000000..7546ca2f --- /dev/null +++ b/languages/cpp/src/shared/include/types.h @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + diff --git a/languages/cpp/src/shared/src/Accessor/Accessor.cpp b/languages/cpp/src/shared/src/Accessor/Accessor.cpp new file mode 100644 index 00000000..e34630ed --- /dev/null +++ b/languages/cpp/src/shared/src/Accessor/Accessor.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Accessor.h" + +namespace FireboltSDK { + + Accessor* Accessor::_singleton = nullptr; + + Accessor::Accessor(const string& configLine) + : _workerPool() + , _transport(nullptr) + , _config() + { + ASSERT(_singleton == nullptr); + _singleton = this; + _config.FromString(configLine); + + Logger::SetLogLevel(WPEFramework::Core::EnumerateType(_config.LogLevel.Value().c_str()).Value()); + + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "Url = %s", _config.WsUrl.Value().c_str()); + _workerPool = WPEFramework::Core::ProxyType::Create(_config.WorkerPool.ThreadCount.Value(), _config.WorkerPool.StackSize.Value(), _config.WorkerPool.QueueSize.Value()); + WPEFramework::Core::WorkerPool::Assign(&(*_workerPool)); + _workerPool->Run(); + } + + Accessor::~Accessor() + { + WPEFramework::Core::IWorkerPool::Assign(nullptr); + _workerPool->Stop(); + + ASSERT(_singleton != nullptr); + _singleton = nullptr; + } + + Firebolt::Error Accessor::CreateEventHandler() + { + Event::Instance().Configure(_transport); + return Firebolt::Error::None; + } + + Firebolt::Error Accessor::DestroyEventHandler() + { + Event::Dispose(); + return Firebolt::Error::None; + } + + Event& Accessor::GetEventManager() + { + return Event::Instance(); + } + + Firebolt::Error Accessor::CreateTransport(const string& url, const Transport::Listener& listener, const uint32_t waitTime = DefaultWaitTime) + { + if (_transport != nullptr) { + delete _transport; + } + + _transport = new Transport(static_cast(url), waitTime, listener); + + ASSERT(_transport != nullptr); + return ((_transport != nullptr) ? Firebolt::Error::None : Firebolt::Error::Timedout); + } + + Firebolt::Error Accessor::DestroyTransport() + { + if (_transport != nullptr) { + delete _transport; + _transport = nullptr; + } + return Firebolt::Error::None; + } + + Transport* Accessor::GetTransport() + { + ASSERT(_transport != nullptr); + return _transport; + } + +} diff --git a/languages/cpp/src/shared/src/Accessor/Accessor.h b/languages/cpp/src/shared/src/Accessor/Accessor.h new file mode 100644 index 00000000..5441368b --- /dev/null +++ b/languages/cpp/src/shared/src/Accessor/Accessor.h @@ -0,0 +1,144 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" +#include "WorkerPool.h" +#include "Transport/Transport.h" +#include "Async/Async.h" +#include "Event/Event.h" +#include "Logger/Logger.h" + +namespace FireboltSDK { + class Accessor { + private: + static constexpr uint8_t JSONVersion = 2; + + private: + //Singleton + Accessor(const string& configLine); + + public: + class EXTERNAL Config : public WPEFramework::Core::JSON::Container { + public: + Config(const Config&) = delete; + Config& operator=(const Config&) = delete; + + class WorkerPoolConfig : public WPEFramework::Core::JSON::Container { + public: + WorkerPoolConfig& operator=(const WorkerPoolConfig&); + + WorkerPoolConfig() + : WPEFramework::Core::JSON::Container() + , QueueSize(8) + , ThreadCount(3) + , StackSize(WPEFramework::Core::Thread::DefaultStackSize()) + { + Add("queueSize", &QueueSize); + Add("threadCount", &ThreadCount); + Add("stackSize", &StackSize); + } + + virtual ~WorkerPoolConfig() = default; + + public: + WPEFramework::Core::JSON::DecUInt32 QueueSize; + WPEFramework::Core::JSON::DecUInt32 ThreadCount; + WPEFramework::Core::JSON::DecUInt32 StackSize; + }; + + + Config() + : WPEFramework::Core::JSON::Container() + , WaitTime(1000) + , LogLevel(_T("Info")) + , WorkerPool() + , WsUrl(_T("ws://127.0.0.1:9998")) + { + Add(_T("waitTime"), &WaitTime); + Add(_T("logLevel"), &LogLevel); + Add(_T("workerPool"), &WorkerPool); + Add(_T("wsUrl"), &WsUrl); + } + + public: + WPEFramework::Core::JSON::DecUInt32 WaitTime; + WPEFramework::Core::JSON::String LogLevel; + WorkerPoolConfig WorkerPool; + WPEFramework::Core::JSON::String WsUrl; + }; + + Accessor(const Accessor&) = delete; + Accessor& operator= (const Accessor&) = delete; + Accessor() = delete; + ~Accessor(); + + static Accessor& Instance(const string& configLine = "") + { + static Accessor *instance = new Accessor(configLine); + ASSERT(instance != nullptr); + return *instance; + } + + static void Dispose() + { + ASSERT(_singleton != nullptr); + + if (_singleton != nullptr) { + delete _singleton; + } + } + + Firebolt::Error Connect(const Transport::Listener& listener) + { + Firebolt::Error status = CreateTransport(_config.WsUrl.Value().c_str(), listener, _config.WaitTime.Value()); + if (status == Firebolt::Error::None) { + Async::Instance().Configure(_transport); + status = CreateEventHandler(); + } + return status; + } + + Firebolt::Error Disconnect() + { + Firebolt::Error status = Firebolt::Error::None; + status = DestroyTransport(); + if (status == Firebolt::Error::None) { + Async::Dispose(); + status = DestroyEventHandler(); + } + return status; + } + + Event& GetEventManager(); + Transport* GetTransport(); + + private: + Firebolt::Error CreateEventHandler(); + Firebolt::Error DestroyEventHandler(); + Firebolt::Error CreateTransport(const string& url, const Transport::Listener& listener, const uint32_t waitTime); + Firebolt::Error DestroyTransport(); + + private: + WPEFramework::Core::ProxyType _workerPool; + Transport* _transport; + static Accessor* _singleton; + Config _config; + }; +} diff --git a/languages/cpp/src/shared/src/Accessor/WorkerPool.h b/languages/cpp/src/shared/src/Accessor/WorkerPool.h new file mode 100644 index 00000000..69005a5e --- /dev/null +++ b/languages/cpp/src/shared/src/Accessor/WorkerPool.h @@ -0,0 +1,102 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" + +namespace FireboltSDK { + + class WorkerPoolImplementation : public WPEFramework::Core::WorkerPool { + public: + WorkerPoolImplementation() = delete; + WorkerPoolImplementation(const WorkerPoolImplementation&) = delete; + WorkerPoolImplementation& operator=(const WorkerPoolImplementation&) = delete; + + WorkerPoolImplementation(const uint8_t threads, const uint32_t stackSize, const uint32_t queueSize) + : WorkerPool(threads, stackSize, queueSize, &_dispatcher) + { + } + + ~WorkerPoolImplementation() + { + // Diable the queue so the minions can stop, even if they are processing and waiting for work.. + Stop(); + } + + public: + void Stop() + { + WPEFramework::Core::WorkerPool::Stop(); + } + + void Run() + { + WPEFramework::Core::WorkerPool::Run(); + } + + private: + class Dispatcher : public WPEFramework::Core::ThreadPool::IDispatcher { + public: + Dispatcher(const Dispatcher&) = delete; + Dispatcher& operator=(const Dispatcher&) = delete; + + Dispatcher() = default; + ~Dispatcher() override = default; + + private: + void Initialize() override { } + void Deinitialize() override { } + void Dispatch(WPEFramework::Core::IDispatch* job) override + { job->Dispatch(); } + }; + + Dispatcher _dispatcher; + }; + + class Worker : public WPEFramework::Core::IDispatch { + public: + typedef std::function Dispatcher; + + protected: + Worker(const Dispatcher& dispatcher, const void* userData) + : _dispatcher(dispatcher) + , _userData(userData) + { + } + + public: + Worker() = delete; + Worker(const Worker&) = delete; + Worker& operator=(const Worker&) = delete; + + ~Worker() = default; + + public: + static WPEFramework::Core::ProxyType Create(const Dispatcher& dispatcher, const void* userData); + + void Dispatch() override + { + _dispatcher(_userData); + } + + private: + Dispatcher _dispatcher; + const void* _userData; + }; +} diff --git a/languages/cpp/src/shared/src/Async/Async.cpp b/languages/cpp/src/shared/src/Async/Async.cpp new file mode 100644 index 00000000..f9994d7d --- /dev/null +++ b/languages/cpp/src/shared/src/Async/Async.cpp @@ -0,0 +1,78 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Transport/Transport.h" +#include "Async.h" + +namespace FireboltSDK { + Async* Async::_singleton = nullptr; + Async::Async() + : _methodMap() + , _adminLock() + , _transport(nullptr) + { + ASSERT(_singleton == nullptr); + _singleton = this; + } + + Async::~Async() /* override */ + { + Clear(); + _transport = nullptr; + _singleton = nullptr; + } + + /* static */ Async& Async::Instance() + { + static Async *instance = new Async(); + ASSERT(instance != nullptr); + return *instance; + } + + /* static */ void Async::Dispose() + { + ASSERT(_singleton != nullptr); + + if (_singleton != nullptr) { + delete _singleton; + } + } + + void Async::Configure(Transport* transport) + { + _transport = transport; + } + + void Async::Clear() + { + _adminLock.Lock(); + MethodMap::iterator index = _methodMap.begin(); + while (index != _methodMap.end()) { + CallbackMap::iterator callbackIndex = index->second.begin(); + while (callbackIndex != index->second.end()) { + if (IsValidJob(callbackIndex->second)) { + WPEFramework::Core::IWorkerPool::Instance().Revoke(callbackIndex->second.job); + } + callbackIndex = index->second.erase(callbackIndex); + } + index = _methodMap.erase(index); + } + _adminLock.Unlock(); + } +} + diff --git a/languages/cpp/src/shared/src/Async/Async.h b/languages/cpp/src/shared/src/Async/Async.h new file mode 100644 index 00000000..a141b9e5 --- /dev/null +++ b/languages/cpp/src/shared/src/Async/Async.h @@ -0,0 +1,203 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" + +namespace FireboltSDK { + + class Async { + private: + Async(); + public: + virtual ~Async(); + Async(const Async&) = delete; + Async& operator= (const Async&) = delete; + + public: + typedef std::function DispatchFunction; + + class Job : public WPEFramework::Core::IDispatch { + protected: + Job(Async& parent, const string& method, const DispatchFunction lambda, void* usercb) + : _parent(parent) + , _method(method) + , _lambda(lambda) + , _usercb(usercb) + { + } + + public: + Job() = delete; + Job(const Job&) = delete; + Job& operator=(const Job&) = delete; + + ~Job() = default; + + public: + static WPEFramework::Core::ProxyType Create(Async& parent, const string& method, const DispatchFunction lambda, void* usercb); + + void Dispatch() override + { + _lambda(_parent, _usercb); + } + + private: + Async& _parent; + string _method; + DispatchFunction _lambda; + void* _usercb; + }; + + public: + struct CallbackData { + const DispatchFunction lambda; + WPEFramework::Core::ProxyType job; + uint32_t id; + }; + + using CallbackMap = std::map; + using MethodMap = std::map; + + private: + static constexpr uint32_t DefaultId = 0xFFFFFFFF; + static constexpr uint32_t DefaultWaitTime = WPEFramework::Core::infinite; + + public: + static Async& Instance(); + static void Dispose(); + void Configure(Transport* transport); + + public: + template + Firebolt::Error Invoke(const string& method, const PARAMETERS& parameters, const CALLBACK& callback, void* usercb, uint32_t waitTime = DefaultWaitTime) + { + Firebolt::Error status = Firebolt::Error::None; + if (_transport != nullptr) { + Transport* transport = _transport; + std::function actualCallback = callback; + DispatchFunction lambda = [actualCallback, transport, method, parameters, waitTime](Async& parent, void* usercb) -> Firebolt::Error { + RESPONSE response; + uint32_t id = DefaultId; + Firebolt::Error status = transport->InvokeAsync(method, parameters, id); + if (status == Firebolt::Error::None && parent.IsActive(method, usercb) == true) { + parent.UpdateEntry(method, usercb, id); + status = transport->WaitForResponse(id, response, waitTime); + if (status == Firebolt::Error::None && parent.IsActive(method, usercb) == true) { + WPEFramework::Core::ProxyType* jsonResponse = new WPEFramework::Core::ProxyType(); + *jsonResponse = WPEFramework::Core::ProxyType::Create(); + (*jsonResponse)->FromString(response); + actualCallback(usercb, jsonResponse, status); + parent.RemoveEntry(method, usercb); + } + + } + return (status); + }; + + _adminLock.Lock(); + WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(*this, method, lambda, usercb)); + CallbackData callbackData = {lambda, job, DefaultId}; + MethodMap::iterator index = _methodMap.find(method); + if (index != _methodMap.end()) { + CallbackMap::iterator callbackIndex = index->second.find(usercb); + if (callbackIndex == index->second.end()) { + index->second.emplace(std::piecewise_construct, std::forward_as_tuple(usercb), std::forward_as_tuple(callbackData)); + } + } else { + + CallbackMap callbackMap; + callbackMap.emplace(std::piecewise_construct, std::forward_as_tuple(usercb), std::forward_as_tuple(callbackData)); + _methodMap.emplace(std::piecewise_construct, std::forward_as_tuple(method), std::forward_as_tuple(callbackMap)); + } + _adminLock.Unlock(); + + WPEFramework::Core::IWorkerPool::Instance().Submit(job); + } + + return status; + } + + Firebolt::Error Abort(const string& method, void* usercb) + { + RemoveEntry(method, usercb); + return (Firebolt::Error::None); + } + + void UpdateEntry(const string& method, void* usercb, uint32_t id) + { + _adminLock.Lock(); + MethodMap::iterator index = _methodMap.find(method); + if (index != _methodMap.end()) { + CallbackMap::iterator callbackIndex = index->second.find(usercb); + if (callbackIndex != index->second.end()) { + callbackIndex->second.id = id; + } + } + _adminLock.Unlock(); + } + + void RemoveEntry(const string& method, void* usercb) + { + _adminLock.Lock(); + MethodMap::iterator index = _methodMap.find(method); + if (index != _methodMap.end()) { + CallbackMap::iterator callbackIndex = index->second.find(usercb); + if (callbackIndex != index->second.end()) { + if (IsValidJob(callbackIndex->second)) { + WPEFramework::Core::IWorkerPool::Instance().Revoke(callbackIndex->second.job); + } + index->second.erase(callbackIndex); + if (index->second.size() == 0) { + _methodMap.erase(index); + } + } + } + _adminLock.Unlock(); + } + + bool IsActive(const string& method, void* usercb) + { + bool valid = false; + _adminLock.Lock(); + MethodMap::iterator index = _methodMap.find(method); + if (index != _methodMap.end()) { + CallbackMap::iterator callbackIndex = index->second.find(usercb); + if (callbackIndex != index->second.end()) { + valid = true; + } + } + _adminLock.Unlock(); + return valid; + } + + private: + void Clear(); + inline bool IsValidJob(CallbackData& callbackData) { + return (callbackData.job.IsValid() && (callbackData.id == DefaultId)); + } + + private: + MethodMap _methodMap; + WPEFramework::Core::CriticalSection _adminLock; + Transport* _transport; + + static Async* _singleton; + }; +} diff --git a/languages/cpp/src/shared/src/CMakeLists.txt b/languages/cpp/src/shared/src/CMakeLists.txt new file mode 100644 index 00000000..222ba35f --- /dev/null +++ b/languages/cpp/src/shared/src/CMakeLists.txt @@ -0,0 +1,79 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.3) + +project(FireboltSDK) +project_version(1.0.0) +set(TARGET ${PROJECT_NAME}) +message("Setup ${TARGET} v${PROJECT_VERSION}") + +file(GLOB SOURCES *.cpp) +add_library(${TARGET} ${FIREBOLT_LIBRARY_TYPE} + ${SOURCES} + Logger/Logger.cpp + Transport/Transport.cpp + Accessor/Accessor.cpp + Event/Event.cpp + Async/Async.cpp +) + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) + +find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) +find_package(${NAMESPACE}WebSocket CONFIG REQUIRED) +find_package(${NAMESPACE}Core CONFIG REQUIRED) + +target_link_libraries(${TARGET} + PUBLIC + ${NAMESPACE}WebSocket::${NAMESPACE}WebSocket + ${NAMESPACE}Core::${NAMESPACE}Core + ${NAMESPACE}WebSocket::${NAMESPACE}WebSocket +) + +target_include_directories(${TARGET} + PRIVATE + $ + $ +) + +set_target_properties(${TARGET} PROPERTIES + CXX_STANDARD 17 + CXX_STANDARD_REQUIRED YES + FRAMEWORK FALSE + LINK_WHAT_YOU_USE TRUE + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) + +install( + TARGETS ${TARGET} EXPORT ${TARGET}Targets + ARCHIVE DESTINATION lib COMPONENT libs # static lib + LIBRARY DESTINATION lib COMPONENT libs # shared lib +) + +install( + DIRECTORY ${CMAKE_SOURCE_DIR}/include/ + DESTINATION include/${FIREBOLT_NAMESPACE}SDK + FILES_MATCHING PATTERN "*.h") + +install( + FILES ${CMAKE_BINARY_DIR}/FireboltConfig.cmake + DESTINATION lib/cmake/${FIREBOLT_NAMESPACE}) + +InstallHeaders(TARGET ${TARGET} HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} DESTINATION ${FIREBOLT_NAMESPACE}SDK) +InstallCMakeConfig(TARGETS ${TARGET}) +InstallPackageConfig(TARGETS ${TARGET} DESCRIPTION "Firebolt SDK Library") diff --git a/languages/cpp/src/shared/src/Event/Event.cpp b/languages/cpp/src/shared/src/Event/Event.cpp new file mode 100644 index 00000000..5d739d70 --- /dev/null +++ b/languages/cpp/src/shared/src/Event/Event.cpp @@ -0,0 +1,163 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Transport/Transport.h" +#include "Event.h" + +namespace FireboltSDK { + Event* Event::_singleton = nullptr; + Event::Event() + : _eventMap() + , _adminLock() + , _transport(nullptr) + { + ASSERT(_singleton == nullptr); + _singleton = this; + } + + Event::~Event() /* override */ + { + _transport->SetEventHandler(nullptr); + _transport = nullptr; + + _singleton = nullptr; + } + + /* static */ Event& Event::Instance() + { + static Event *instance = new Event(); + ASSERT(instance != nullptr); + return *instance; + } + + /* static */ void Event::Dispose() + { + ASSERT(_singleton != nullptr); + + if (_singleton != nullptr) { + delete _singleton; + } + } + + void Event::Configure(Transport* transport) + { + _transport = transport; + _transport->SetEventHandler(this); + } + + Firebolt::Error Event::Unsubscribe(const string& eventName, void* usercb) + { + Firebolt::Error status = Revoke(eventName, usercb); + + if (status == Firebolt::Error::None) { + if (_transport != nullptr) { + + const string parameters("{\"listen\":false}"); + status = _transport->Unsubscribe(eventName, parameters); + } + } else { + status = Firebolt::Error::None; + } + return status; + } + + Firebolt::Error Event::ValidateResponse(const WPEFramework::Core::ProxyType& jsonResponse, bool& enabled) /* override */ + { + Firebolt::Error result = Firebolt::Error::General; + Response response; + _transport->FromMessage((WPEFramework::Core::JSON::IElement*)&response, *jsonResponse); + if (response.Listening.IsSet() == true) { + result = Firebolt::Error::None; + enabled = response.Listening.Value(); + } + return result; + } + + Firebolt::Error Event::Dispatch(const string& eventName, const WPEFramework::Core::ProxyType& jsonResponse) /* override */ + { + string response = jsonResponse->Result.Value(); + _adminLock.Lock(); + EventMap::iterator eventIndex = _eventMap.find(eventName); + if (eventIndex != _eventMap.end()) { + CallbackMap::iterator callbackIndex = eventIndex->second.begin(); + while(callbackIndex != eventIndex->second.end()) { + State state; + if (callbackIndex->second.state != State::REVOKED) { + callbackIndex->second.state = State::EXECUTING; + } + state = callbackIndex->second.state; + _adminLock.Unlock(); + if (state == State::EXECUTING) { + callbackIndex->second.lambda(callbackIndex->first, callbackIndex->second.userdata, (jsonResponse->Result.Value())); + } + _adminLock.Lock(); + if (callbackIndex->second.state == State::REVOKED) { + callbackIndex = eventIndex->second.erase(callbackIndex); + if (eventIndex->second.size() == 0) { + _eventMap.erase(eventIndex); + } + } else { + callbackIndex->second.state = State::IDLE; + callbackIndex++; + } + } + } + _adminLock.Unlock(); + + return Firebolt::Error::None;; + } + + Firebolt::Error Event::Revoke(const string& eventName, void* usercb) + { + Firebolt::Error status = Firebolt::Error::None; + _adminLock.Lock(); + EventMap::iterator eventIndex = _eventMap.begin(); + if (eventIndex != _eventMap.end()) { + CallbackMap::iterator callbackIndex = eventIndex->second.find(usercb); + if (callbackIndex->second.state != State::EXECUTING) { + if (callbackIndex != eventIndex->second.end()) { + eventIndex->second.erase(callbackIndex); + } + } else { + callbackIndex->second.state = State::REVOKED; + } + if (eventIndex->second.size() == 0) { + _eventMap.erase(eventIndex); + } else { + status = Firebolt::Error::General; + } + } + _adminLock.Unlock(); + + return status; + } + + void Event::Clear() + { + EventMap::iterator eventIndex = _eventMap.begin(); + while (eventIndex != _eventMap.end()) { + CallbackMap::iterator callbackIndex = eventIndex->second.begin(); + while (callbackIndex != eventIndex->second.end()) { + callbackIndex = eventIndex->second.erase(callbackIndex); + } + eventIndex = _eventMap.erase(eventIndex); + } + _adminLock.Unlock(); + } + +} diff --git a/languages/cpp/src/shared/src/Event/Event.h b/languages/cpp/src/shared/src/Event/Event.h new file mode 100644 index 00000000..7299fde0 --- /dev/null +++ b/languages/cpp/src/shared/src/Event/Event.h @@ -0,0 +1,163 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" + +namespace FireboltSDK { + + static constexpr uint32_t DefaultWaitTime = 1000; + + class Event : public IEventHandler { + public: + typedef std::function DispatchFunction; + private: + enum State : uint8_t { + IDLE, + EXECUTING, + REVOKED + }; + + struct CallbackData { + const DispatchFunction lambda; + const void* userdata; + State state; + }; + using CallbackMap = std::map; + using EventMap = std::map; + + class Response : public WPEFramework::Core::JSON::Container { + public: + Response& operator=(const Response&) = delete; + Response() + : WPEFramework::Core::JSON::Container() + , Listening(false) + { + Add(_T("listening"), &Listening); + } + Response(const Response& copy) + : WPEFramework::Core::JSON::Container() + , Listening(copy.Listening) + { + Add(_T("listening"), &Listening); + } + ~Response() override = default; + + public: + WPEFramework::Core::JSON::Boolean Listening; + }; + + private: + Event(); + public: + ~Event() override; + static Event& Instance(); + static void Dispose(); + void Configure(Transport* transport); + + public: + template + Firebolt::Error Subscribe(const string& eventName, const CALLBACK& callback, void* usercb, const void* userdata) + { + JsonObject jsonParameters; + return Subscribe(eventName, jsonParameters, callback, usercb, userdata); + } + + template + Firebolt::Error Subscribe(const string& eventName, JsonObject& jsonParameters, const CALLBACK& callback, void* usercb, const void* userdata) + { + Firebolt::Error status = Firebolt::Error::General; + if (_transport != nullptr) { + + status = Assign(eventName, callback, usercb, userdata); + if (status == Firebolt::Error::None) { + Response response; + + WPEFramework::Core::JSON::Variant Listen = true; + jsonParameters.Set(_T("listen"), Listen); + string parameters; + jsonParameters.ToString(parameters); + + status = _transport->Subscribe(eventName, parameters, response); + + if (status != Firebolt::Error::None) { + Revoke(eventName, usercb); + } else if ((response.Listening.IsSet() == true) && + (response.Listening.Value() == true)) { + status = Firebolt::Error::None; + } + } + } + + return status; + } + + Firebolt::Error Unsubscribe(const string& eventName, void* usercb); + + private: + template + Firebolt::Error Assign(const string& eventName, const CALLBACK& callback, void* usercb, const void* userdata) + { + Firebolt::Error status = Firebolt::Error::General; + std::function actualCallback = callback; + DispatchFunction implementation = [actualCallback](void* usercb, const void* userdata, const string& parameters) -> Firebolt::Error { + + WPEFramework::Core::ProxyType* inbound = new WPEFramework::Core::ProxyType(); + *inbound = WPEFramework::Core::ProxyType::Create(); + (*inbound)->FromString(parameters); + actualCallback(usercb, userdata, static_cast(inbound)); + return (Firebolt::Error::None); + }; + CallbackData callbackData = {implementation, userdata, State::IDLE}; + + _adminLock.Lock(); + EventMap::iterator eventIndex = _eventMap.find(eventName); + if (eventIndex != _eventMap.end()) { + CallbackMap::iterator callbackIndex = eventIndex->second.find(usercb); + if (callbackIndex == eventIndex->second.end()) { + eventIndex->second.emplace(std::piecewise_construct, std::forward_as_tuple(usercb), std::forward_as_tuple(callbackData)); + status = Firebolt::Error::None; + } + } else { + + CallbackMap callbackMap; + callbackMap.emplace(std::piecewise_construct, std::forward_as_tuple(usercb), std::forward_as_tuple(callbackData)); + _eventMap.emplace(std::piecewise_construct, std::forward_as_tuple(eventName), std::forward_as_tuple(callbackMap)); + status = Firebolt::Error::None; + + } + + _adminLock.Unlock(); + return status; + } + Firebolt::Error Revoke(const string& eventName, void* usercb); + + private: + void Clear(); + Firebolt::Error ValidateResponse(const WPEFramework::Core::ProxyType& jsonResponse, bool& enabled) override; + Firebolt::Error Dispatch(const string& eventName, const WPEFramework::Core::ProxyType& jsonResponse) override; + + private: + EventMap _eventMap; + WPEFramework::Core::CriticalSection _adminLock; + Transport* _transport; + + static Event* _singleton; + }; +} diff --git a/languages/cpp/src/shared/src/FireboltSDK.conf.in b/languages/cpp/src/shared/src/FireboltSDK.conf.in new file mode 100644 index 00000000..6964a7bc --- /dev/null +++ b/languages/cpp/src/shared/src/FireboltSDK.conf.in @@ -0,0 +1,3 @@ +url = "@FIREBOLT_SERVER_URL@" +waittime = "@FIREBOLT_TRANSPORT_WAITTIME@" +loglevel = "@FIREBOLT_LOGLEVEL@" diff --git a/languages/cpp/src/shared/src/FireboltSDK.h b/languages/cpp/src/shared/src/FireboltSDK.h new file mode 100644 index 00000000..3b608cce --- /dev/null +++ b/languages/cpp/src/shared/src/FireboltSDK.h @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Transport/Transport.h" +#include "Properties/Properties.h" +#include "Accessor/Accessor.h" +#include "Async/Async.h" +#include "Logger/Logger.h" +#include "TypesPriv.h" +#include "types.h" diff --git a/languages/cpp/src/shared/src/IModule.h b/languages/cpp/src/shared/src/IModule.h new file mode 100644 index 00000000..1349e78a --- /dev/null +++ b/languages/cpp/src/shared/src/IModule.h @@ -0,0 +1,25 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +namespace Firebolt { + struct IModule { + virtual ~IModule() = default; + }; +} diff --git a/languages/cpp/src/shared/src/Logger/Logger.cpp b/languages/cpp/src/shared/src/Logger/Logger.cpp new file mode 100644 index 00000000..c66c6b68 --- /dev/null +++ b/languages/cpp/src/shared/src/Logger/Logger.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" +#include "error.h" +#include "Logger.h" + +namespace WPEFramework { + +ENUM_CONVERSION_BEGIN(FireboltSDK::Logger::LogLevel) + + { FireboltSDK::Logger::LogLevel::Error, _TXT("Error") }, + { FireboltSDK::Logger::LogLevel::Warning, _TXT("Warning") }, + { FireboltSDK::Logger::LogLevel::Info, _TXT("Info") }, + { FireboltSDK::Logger::LogLevel::Debug, _TXT("Debug") }, + +ENUM_CONVERSION_END(FireboltSDK::Logger::LogLevel) + +ENUM_CONVERSION_BEGIN(FireboltSDK::Logger::Category) + + { FireboltSDK::Logger::Category::OpenRPC, _TXT("FireboltSDK::OpenRPC") }, + { FireboltSDK::Logger::Category::Core, _TXT("FireboltSDK::Core") }, + { FireboltSDK::Logger::Category::Manage, _TXT("FireboltSDK::Manage") }, + { FireboltSDK::Logger::Category::Discovery, _TXT("FireboltSDK::Discovery") }, + +ENUM_CONVERSION_END(FireboltSDK::Logger::Category) + +} + +namespace FireboltSDK { + /* static */ Logger::LogLevel Logger::_logLevel = Logger::LogLevel::Error; + + Firebolt::Error Logger::SetLogLevel(Logger::LogLevel logLevel) + { + ASSERT(logLevel < Logger::LogLevel::MaxLevel); + Firebolt::Error status = Firebolt::Error::General; + if (logLevel < Logger::LogLevel::MaxLevel) { + _logLevel = logLevel; + status = Firebolt::Error::None; + } + return status; + } + + void Logger::Log(LogLevel logLevel, Category category, const std::string& module, const std::string file, const std::string function, const uint16_t line, const std::string& format, ...) + { + if (logLevel <= _logLevel) { + va_list arg; + char msg[Logger::MaxBufSize]; + va_start(arg, format); + int length = vsnprintf(msg, Logger::MaxBufSize, format.c_str(), arg); + va_end(arg); + + uint32_t position = (length >= Logger::MaxBufSize) ? (Logger::MaxBufSize - 1) : length; + msg[position] = '\0'; + + char formattedMsg[Logger::MaxBufSize]; + const string time = WPEFramework::Core::Time::Now().ToTimeOnly(true); + const string categoryName = WPEFramework::Core::EnumerateType(category).Data(); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wformat-truncation" + if (categoryName.empty() != true) { + snprintf(formattedMsg, sizeof(formattedMsg), "--->\033[1;32m[%s]:[%s]:[%s][%s:%d](%s) : %s\n", time.c_str(), categoryName.c_str(), module.c_str(), WPEFramework::Core::File::FileName(file).c_str(), line, function.c_str(), TRACE_PROCESS_ID, TRACE_THREAD_ID, msg); + } else { + snprintf(formattedMsg, sizeof(formattedMsg), "--->\033[1;32m[%s]:[%s][%s:%d](%s) : %s\n", time.c_str(), module.c_str(), WPEFramework::Core::File::FileName(file).c_str(), line, function.c_str(), TRACE_PROCESS_ID, TRACE_THREAD_ID, msg); + } +#pragma GCC diagnostic pop + LOG_MESSAGE(formattedMsg); + } + } +} + diff --git a/languages/cpp/src/shared/src/Logger/Logger.h b/languages/cpp/src/shared/src/Logger/Logger.h new file mode 100644 index 00000000..c88b87e8 --- /dev/null +++ b/languages/cpp/src/shared/src/Logger/Logger.h @@ -0,0 +1,85 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "types.h" + +namespace FireboltSDK { + + class Logger { + private: + static constexpr uint16_t MaxBufSize = 512; + + public: + enum class LogLevel : uint8_t { + Error, + Warning, + Info, + Debug, + MaxLevel + }; + + enum class Category : uint8_t { + OpenRPC, + Core, + Manage, + Discovery + }; + + public: + Logger() = default; + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + ~Logger() = default; + + public: + static Firebolt::Error SetLogLevel(LogLevel logLevel); + static void Log(LogLevel logLevel, Category category, const std::string& module, const std::string file, const std::string function, const uint16_t line, const std::string& format, ...); + + public: + template + static const string Module() + { + return WPEFramework::Core::ClassNameOnly(typeid(CLASS).name()).Text(); + } + + private: + static LogLevel _logLevel; + }; +} + +#define FIREBOLT_LOG(level, category, module, ...) \ + FireboltSDK::Logger::Log(level, category, module, __FILE__, __func__, __LINE__, __VA_ARGS__) + +#define FIREBOLT_LOG_ERROR(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Error, category, module, __VA_ARGS__) +#define FIREBOLT_LOG_WARNING(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Warning, category, module, __VA_ARGS__) +#define FIREBOLT_LOG_INFO(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Info, category, module, __VA_ARGS__) +#define FIREBOLT_LOG_DEBUG(category, module, ...) \ + FIREBOLT_LOG(FireboltSDK::Logger::LogLevel::Debug, category, module, __VA_ARGS__) + +#ifdef ENABLE_SYSLOG +#define LOG_MESSAGE(message) \ + syslog(sLOG_NOTIC, "%s", message); +#else +#define LOG_MESSAGE(message) \ + fprintf(stderr, "%s", message); fflush(stdout); +#endif diff --git a/languages/cpp/src/shared/src/Module.cpp b/languages/cpp/src/shared/src/Module.cpp new file mode 100644 index 00000000..d63badc4 --- /dev/null +++ b/languages/cpp/src/shared/src/Module.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/languages/cpp/src/shared/src/Module.h b/languages/cpp/src/shared/src/Module.h new file mode 100644 index 00000000..00ea64bb --- /dev/null +++ b/languages/cpp/src/shared/src/Module.h @@ -0,0 +1,29 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifndef MODULE_NAME +#define MODULE_NAME OpenRPCNativeSDK +#endif + +#include +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/languages/cpp/src/shared/src/Properties/Properties.h b/languages/cpp/src/shared/src/Properties/Properties.h new file mode 100644 index 00000000..3729ba6b --- /dev/null +++ b/languages/cpp/src/shared/src/Properties/Properties.h @@ -0,0 +1,148 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Accessor/Accessor.h" +#include "Event/Event.h" + +namespace FireboltSDK { + + class Properties { + public: + Properties(const Properties&) = delete; + Properties& operator= (const Properties&) = delete; + + Properties() = default; + ~Properties() = default; + + public: + template + static Firebolt::Error Get(const string& propertyName, WPEFramework::Core::ProxyType& response) + { + Firebolt::Error status = Firebolt::Error::General; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + JsonObject parameters; + RESPONSETYPE responseType; + status = transport->Invoke(propertyName, parameters, responseType); + if (status == Firebolt::Error::None) { + ASSERT(response.IsValid() == false); + if (response.IsValid() == true) { + response.Release(); + } + response = WPEFramework::Core::ProxyType::Create(); + (*response) = responseType; + } + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static Firebolt::Error Get(const string& propertyName, const PARAMETERS& parameters, WPEFramework::Core::ProxyType& response) + { + Firebolt::Error status = Firebolt::Error::General; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + RESPONSETYPE responseType; + status = transport->Invoke(propertyName, parameters, responseType); + if (status == Firebolt::Error::None) { + ASSERT(response.IsValid() == false); + if (response.IsValid() == true) { + response.Release(); + } + response = WPEFramework::Core::ProxyType::Create(); + (*response) = responseType; + } + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + + template + static Firebolt::Error Get(const string& propertyName, RESPONSETYPE& response) + { + Firebolt::Error status = Firebolt::Error::General; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + JsonObject parameters; + status = transport->Invoke(propertyName, parameters, response); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static Firebolt::Error Get(const string& propertyName, const PARAMETERS& parameters, RESPONSETYPE& response) + { + Firebolt::Error status = Firebolt::Error::General; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + status = transport->Invoke(propertyName, parameters, response); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static Firebolt::Error Set(const string& propertyName, const PARAMETERS& parameters) + { + Firebolt::Error status = Firebolt::Error::General; + Transport* transport = Accessor::Instance().GetTransport(); + if (transport != nullptr) { + JsonObject responseType; + status = transport->Invoke(propertyName, parameters, responseType); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Error in getting Transport err = %d", status); + } + + return status; + } + + template + static Firebolt::Error Subscribe(const string& propertyName, JsonObject& paramsters, const CALLBACK& callback, void* usercb, const void* userdata) + { + return Event::Instance().Subscribe(EventName(propertyName), paramsters, callback, usercb, userdata); + } + + static Firebolt::Error Unsubscribe(const string& propertyName, void* usercb) + { + return Event::Instance().Unsubscribe(EventName(propertyName), usercb); + } + private: + static inline string EventName(const string& propertyName) { + size_t pos = propertyName.find_first_of('.'); + string eventName = propertyName; + if (pos != std::string::npos) { + eventName[pos + 1] = std::toupper(eventName[pos + 1]); + eventName = string(eventName.substr(0, pos + 1) + "on" + eventName.substr(pos + 1) + "Changed"); + } + return eventName; + } + }; +} diff --git a/languages/cpp/src/shared/src/Transport/Transport.cpp b/languages/cpp/src/shared/src/Transport/Transport.cpp new file mode 100644 index 00000000..280944c6 --- /dev/null +++ b/languages/cpp/src/shared/src/Transport/Transport.cpp @@ -0,0 +1,24 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Transport.h" + +namespace FireboltSDK { + +} + diff --git a/languages/cpp/src/shared/src/Transport/Transport.h b/languages/cpp/src/shared/src/Transport/Transport.h new file mode 100644 index 00000000..0caa7603 --- /dev/null +++ b/languages/cpp/src/shared/src/Transport/Transport.h @@ -0,0 +1,983 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "Module.h" +#include "error.h" + +namespace FireboltSDK { + + using namespace WPEFramework::Core::TypeTraits; + + template + class CommunicationChannel { + public: + typedef std::function Callback; + class Entry { + private: + Entry(const Entry&) = delete; + Entry& operator=(const Entry& rhs) = delete; + struct Synchronous { + Synchronous() + : _signal(false, true) + , _response() + { + } + WPEFramework::Core::Event _signal; + std::list> _response; + }; + struct ASynchronous { + ASynchronous(const uint32_t waitTime, const Callback& completed) + : _waitTime(WPEFramework::Core::Time::Now().Add(waitTime).Ticks()) + , _completed(completed) + { + } + uint64_t _waitTime; + Callback _completed; + }; + + public: + Entry() + : _synchronous(true) + , _info() + { + } + Entry(const uint32_t waitTime, const Callback& completed) + : _synchronous(false) + , _info(waitTime, completed) + { + } + ~Entry() + { + if (_synchronous == true) { + _info.sync.~Synchronous(); + } + else { + _info.async.~ASynchronous(); + } + } + + public: + const WPEFramework::Core::ProxyType& Response() const + { + return (*(_info.sync._response.begin())); + } + bool Signal(const WPEFramework::Core::ProxyType& response) + { + if (_synchronous == true) { + _info.sync._response.push_back(response); + _info.sync._signal.SetEvent(); + } + else { + _info.async._completed(*response); + } + + return (_synchronous == false); + } + const uint64_t& Expiry() const + { + return (_info.async._waitTime); + } + void Abort(const uint32_t id) + { + if (_synchronous == true) { + _info.sync._signal.SetEvent(); + } + else { + MESSAGETYPE message; + ToMessage(id, message, WPEFramework::Core::ERROR_ASYNC_ABORTED); + _info.async._completed(message); + } + } + bool Expired(const uint32_t id, const uint64_t& currentTime, uint64_t& nextTime) + { + bool expired = false; + + if (_synchronous == false) { + if (_info.async._waitTime > currentTime) { + if (_info.async._waitTime < nextTime) { + nextTime = _info.async._waitTime; + } + } + else { + MESSAGETYPE message; + ToMessage(id, message, WPEFramework::Core::ERROR_TIMEDOUT); + _info.async._completed(message); + expired = true; + } + } + return (expired); + } + bool WaitForResponse(const uint32_t waitTime) + { + return (_info.sync._signal.Lock(waitTime) == WPEFramework::Core::ERROR_NONE); + } + + private: + void ToMessage(const uint32_t id, WPEFramework::Core::JSONRPC::Message& message, uint32_t error) + { + message.Id = id; + message.Error.Code = error; + switch (error) { + case WPEFramework::Core::ERROR_ASYNC_ABORTED: { + message.Error.Text = _T("Pending a-sync call has been aborted"); + break; + } + case WPEFramework::Core::ERROR_TIMEDOUT: { + message.Error.Text = _T("Pending a-sync call has timed out"); + break; + } + } + } + + bool _synchronous; + union Info { + public: + Info() + : sync() + { + } + Info(const uint32_t waitTime, const Callback& completed) + : async(waitTime, completed) + { + } + ~Info() + { + } + Synchronous sync; + ASynchronous async; + } _info; + }; + + + + private: + class FactoryImpl { + private: + FactoryImpl(const FactoryImpl&) = delete; + FactoryImpl& operator=(const FactoryImpl&) = delete; + + class WatchDog { + private: + WatchDog() = delete; + WatchDog& operator=(const WatchDog&) = delete; + + public: + WatchDog(CLIENT* client) + : _client(client) + { + } + WatchDog(const WatchDog& copy) + : _client(copy._client) + { + } + ~WatchDog() + { + } + + bool operator==(const WatchDog& rhs) const + { + return (rhs._client == _client); + } + bool operator!=(const WatchDog& rhs) const + { + return (!operator==(rhs)); + } + + public: + uint64_t Timed(const uint64_t scheduledTime) { + return (_client->Timed()); + } + + private: + CLIENT* _client; + }; + + friend WPEFramework::Core::SingletonType; + + FactoryImpl() + : _messageFactory(2) + , _watchDog(WPEFramework::Core::Thread::DefaultStackSize(), _T("TransportCleaner")) + { + } + + public: + static FactoryImpl& Instance() + { + return (WPEFramework::Core::SingletonType::Instance()); + } + + ~FactoryImpl() + { + } + + public: + WPEFramework::Core::ProxyType Element(const string&) + { + return (_messageFactory.Element()); + } + void Trigger(const uint64_t& time, CLIENT* client) + { + _watchDog.Trigger(time, client); + } + void Revoke(CLIENT* client) + { + _watchDog.Revoke(client); + } + private: + WPEFramework::Core::ProxyPoolType _messageFactory; + WPEFramework::Core::TimerType _watchDog; + }; + + class ChannelImpl : public WPEFramework::Core::StreamJSONType, FactoryImpl&, INTERFACE> { + private: + ChannelImpl(const ChannelImpl&) = delete; + ChannelImpl& operator=(const ChannelImpl&) = delete; + + typedef WPEFramework::Core::StreamJSONType, FactoryImpl&, INTERFACE> BaseClass; + + public: + ChannelImpl(CommunicationChannel* parent, const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask) + : BaseClass(5, FactoryImpl::Instance(), path, _T("JSON"), query, "", false, mask, false, remoteNode.AnyInterface(), remoteNode, 512, 512) + , _parent(*parent) + { + } + ~ChannelImpl() override = default; + + public: + void Received(WPEFramework::Core::ProxyType& response) override + { + WPEFramework::Core::ProxyType inbound(response); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) { + _parent.Inbound(inbound); + } + } + void Send(WPEFramework::Core::ProxyType& msg) override + { +#ifdef __DEBUG__ + string message; + ToMessage(msg, message); + TRACE_L1("Message: %s send", message.c_str()); +#endif + } + void StateChange() override + { + _parent.StateChange(); + } + bool IsIdle() const override + { + return (true); + } + + private: + void ToMessage(const WPEFramework::Core::ProxyType& jsonObject, string& message) const + { + WPEFramework::Core::ProxyType inbound(jsonObject); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) { + inbound->ToString(message); + } + } + void ToMessage(const WPEFramework::Core::ProxyType& jsonObject, string& message) const + { + WPEFramework::Core::ProxyType inbound(jsonObject); + + ASSERT(inbound.IsValid() == true); + if (inbound.IsValid() == true) { + std::vector values; + inbound->ToBuffer(values); + if (values.empty() != true) { + WPEFramework::Core::ToString(values.data(), static_cast(values.size()), false, message); + } + } + } + + private: + CommunicationChannel& _parent; + }; + + protected: + CommunicationChannel(const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask) + : _channel(this, remoteNode, path, query, mask) + , _sequence(0) + { + } + + public: + ~CommunicationChannel() = default; + static WPEFramework::Core::ProxyType Instance(const WPEFramework::Core::NodeId& remoteNode, const string& path, const string& query, const bool mask = true) + { + static WPEFramework::Core::ProxyMapType channelMap; + + string searchLine = remoteNode.HostAddress() + '@' + path; + + return (channelMap.template Instance(searchLine, remoteNode, path, query, mask)); + } + + public: + static void Trigger(const uint64_t& time, CLIENT* client) + { + FactoryImpl::Instance().Trigger(time, client); + } + static WPEFramework::Core::ProxyType Message() + { + return (FactoryImpl::Instance().Element(string())); + } + uint32_t Sequence() const + { + return (++_sequence); + } + void Register(CLIENT& client) + { + _adminLock.Lock(); + ASSERT(std::find(_observers.begin(), _observers.end(), &client) == _observers.end()); + _observers.push_back(&client); + if (_channel.IsOpen() == true) { + client.Opened(); + } + _adminLock.Unlock(); + } + void Unregister(CLIENT& client) + { + _adminLock.Lock(); + typename std::list::iterator index(std::find(_observers.begin(), _observers.end(), &client)); + if (index != _observers.end()) { + _observers.erase(index); + } + FactoryImpl::Instance().Revoke(&client); + _adminLock.Unlock(); + } + + void Submit(const WPEFramework::Core::ProxyType& message) + { + _channel.Submit(message); + } + bool IsSuspended() const + { + return (_channel.IsSuspended()); + } + uint32_t Initialize() + { + return (Open(0)); + } + void Deinitialize() + { + Close(); + } + bool IsOpen() + { + return (_channel.IsOpen() == true); + } + + protected: + void StateChange() + { + _adminLock.Lock(); + typename std::list::iterator index(_observers.begin()); + while (index != _observers.end()) { + if (_channel.IsOpen() == true) { + (*index)->Opened(); + } + else { + (*index)->Closed(); + } + index++; + } + _adminLock.Unlock(); + } + bool Open(const uint32_t waitTime) + { + bool result = true; + if (_channel.IsClosed() == true) { + result = (_channel.Open(waitTime) == WPEFramework::Core::ERROR_NONE); + } + return (result); + } + void Close() + { + _channel.Close(WPEFramework::Core::infinite); + } + + private: + int32_t Inbound(const WPEFramework::Core::ProxyType& inbound) + { + int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + _adminLock.Lock(); + typename std::list::iterator index(_observers.begin()); + while ((result != WPEFramework::Core::ERROR_NONE) && (index != _observers.end())) { + result = (*index)->Submit(inbound); + index++; + } + _adminLock.Unlock(); + + return (result); + } + + private: + WPEFramework::Core::CriticalSection _adminLock; + ChannelImpl _channel; + mutable std::atomic _sequence; + std::list _observers; + }; + + class IEventHandler { + public: + virtual Firebolt::Error ValidateResponse(const WPEFramework::Core::ProxyType& jsonResponse, bool& enabled) = 0; + virtual Firebolt::Error Dispatch(const string& eventName, const WPEFramework::Core::ProxyType& jsonResponse) = 0; + virtual ~IEventHandler() = default; + }; + + template + class Transport { + private: + using Channel = CommunicationChannel; + using Entry = typename CommunicationChannel::Entry; + using PendingMap = std::unordered_map; + using EventMap = std::map; + typedef std::function& jsonResponse, bool& enabled)> EventResponseValidatioionFunction; + + class CommunicationJob : public WPEFramework::Core::IDispatch { + protected: + CommunicationJob(const WPEFramework::Core::ProxyType& inbound, class Transport* parent) + : _inbound(inbound) + , _parent(parent) + { + } + + public: + CommunicationJob() = delete; + CommunicationJob(const CommunicationJob&) = delete; + CommunicationJob& operator=(const CommunicationJob&) = delete; + + ~CommunicationJob() = default; + + public: + static WPEFramework::Core::ProxyType Create(const WPEFramework::Core::ProxyType& inbound, class Transport* parent); + + void Dispatch() override + { + _parent->Inbound(_inbound); + } + + private: + const WPEFramework::Core::ProxyType _inbound; + class Transport* _parent; + }; + + class ConnectionJob : public WPEFramework::Core::IDispatch { + protected: + ConnectionJob(class Transport* parent) + : _parent(parent) + { + } + + public: + ConnectionJob() = delete; + ConnectionJob(const ConnectionJob&) = delete; + ConnectionJob& operator=(const ConnectionJob&) = delete; + + ~ConnectionJob() = default; + + public: + static WPEFramework::Core::ProxyType Create(class Transport* parent); + + void Dispatch() override + { + if (Firebolt::Error::None != _parent->WaitForLinkReady()) { + _parent->NotifyStatus(Firebolt::Error::Timedout); + } + } + + private: + const WPEFramework::Core::ProxyType _inbound; + class Transport* _parent; + }; + + + protected: + static constexpr uint32_t DefaultWaitTime = 10000; + + inline void Announce() { + _channel->Register(*this); + } + + private: + static constexpr const TCHAR* PathPrefix = _T("/"); + + public: + typedef std::function Listener; + + public: + Transport() = delete; + Transport(const Transport&) = delete; + Transport& operator=(Transport&) = delete; + Transport(const WPEFramework::Core::URL& url, const uint32_t waitTime, const Listener listener) + : _adminLock() + , _connectId(WPEFramework::Core::NodeId(url.Host().Value().c_str(), url.Port().Value())) + , _channel(Channel::Instance(_connectId, ((url.Path().Value().rfind(PathPrefix, 0) == 0) ? url.Path().Value() : string(PathPrefix + url.Path().Value())), url.Query().Value(), true)) + , _eventHandler(nullptr) + , _pendingQueue() + , _scheduledTime(0) + , _waitTime(waitTime) + , _listener(listener) + , _connected(false) + , _status(Firebolt::Error::NotConnected) + { + _channel->Register(*this); + WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(this)); + WPEFramework::Core::IWorkerPool::Instance().Submit(job); + } + + virtual ~Transport() + { + _channel->Unregister(*this); + + for (auto& element : _pendingQueue) { + element.second.Abort(element.first); + } + } + + public: + inline bool IsOpen() + { + return _channel->IsOpen(); + } + + void Revoke(const string& eventName) + { + _adminLock.Lock(); + _eventMap.erase(eventName); + _adminLock.Unlock(); + } + + void SetEventHandler(IEventHandler* eventHandler) + { + _eventHandler = eventHandler; + } + + template + Firebolt::Error Invoke(const string& method, const PARAMETERS& parameters, RESPONSE& response) + { + Entry slot; + uint32_t id = _channel->Sequence(); + Firebolt::Error result = Send(method, parameters, id); + if (result == Firebolt::Error::None) { + result = WaitForResponse(id, response, _waitTime); + } + + return (result); + } + + template + Firebolt::Error InvokeAsync(const string& method, const PARAMETERS& parameters, uint32_t& id) + { + Entry slot; + id = _channel->Sequence(); + return Send(method, parameters, id); + } + + template + Firebolt::Error WaitForResponse(const uint32_t& id, RESPONSE& response, const uint32_t waitTime) + { + int32_t result = WPEFramework::Core::ERROR_TIMEDOUT; + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry& slot(index->second); + _adminLock.Unlock(); + + if (slot.WaitForResponse(waitTime) == true) { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + + // See if we have a jsonResponse, maybe it was just the connection + // that closed? + if (jsonResponse.IsValid() == true) { + if (jsonResponse->Error.IsSet() == true) { + result = jsonResponse->Error.Code.Value(); + } + else { + result = WPEFramework::Core::ERROR_NONE; + if ((jsonResponse->Result.IsSet() == true) + && (jsonResponse->Result.Value().empty() == false)) { + FromMessage((INTERFACE*)&response, *jsonResponse); + } + } + } + } else { + result = WPEFramework::Core::ERROR_TIMEDOUT; + } + _adminLock.Lock(); + _pendingQueue.erase(id); + _adminLock.Unlock(); + return FireboltErrorValue(result); + } + + void Abort(uint32_t id) + { + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry& slot(index->second); + _adminLock.Unlock(); + slot.Abort(id); + } + + template + Firebolt::Error Subscribe(const string& eventName, const string& parameters, RESPONSE& response) + { + Entry slot; + uint32_t id = _channel->Sequence(); + Firebolt::Error result = Send(eventName, parameters, id); + if (result == Firebolt::Error::None) { + _adminLock.Lock(); + _eventMap.emplace(std::piecewise_construct, + std::forward_as_tuple(eventName), + std::forward_as_tuple(~0)); + _adminLock.Unlock(); + + result = WaitForEventResponse(id, eventName, response, _waitTime); + } + + return (result); + } + + Firebolt::Error Unsubscribe(const string& eventName, const string& parameters) + { + Revoke(eventName); + Entry slot; + uint32_t id = _channel->Sequence(); + + return Send(eventName, parameters, id); + } + + void NotifyStatus(Firebolt::Error status) + { + _listener(false, status); + } + + Firebolt::Error WaitForLinkReady() + { + uint32_t waiting = _waitTime; + static constexpr uint32_t SLEEPSLOT_TIME = 100; + + // Right, a wait till connection is closed is requested.. + while ((waiting > 0) && (IsOpen() == false) && (_status == Firebolt::Error::NotConnected)) { + + uint32_t sleepSlot = (waiting > SLEEPSLOT_TIME ? SLEEPSLOT_TIME : waiting); + + // Right, lets sleep in slices of 100 ms + SleepMs(sleepSlot); + + waiting -= (waiting == WPEFramework::Core::infinite ? 0 : sleepSlot); + } + return (((waiting == 0) || (IsOpen() == true)) ? Firebolt::Error::None : Firebolt::Error::Timedout); + } + + private: + friend Channel; + inline bool IsEvent(const uint32_t id, string& eventName) + { + _adminLock.Lock(); + for (auto& event : _eventMap) { + if (event.second == id) { + eventName = event.first; + break; + } + } + _adminLock.Unlock(); + return (eventName.empty() != true); + } + uint64_t Timed() + { + uint64_t result = ~0; + uint64_t currentTime = WPEFramework::Core::Time::Now().Ticks(); + + // Lets see if some callback are expire. If so trigger and remove... + _adminLock.Lock(); + + typename PendingMap::iterator index = _pendingQueue.begin(); + + while (index != _pendingQueue.end()) { + + if (index->second.Expired(index->first, currentTime, result) == true) { + index = _pendingQueue.erase(index); + } + else { + index++; + } + } + _scheduledTime = (result != static_cast(~0) ? result : 0); + + _adminLock.Unlock(); + + return (_scheduledTime); + } + + virtual void Opened() + { + _status = Firebolt::Error::None; + if (_connected != true) { + _connected = true; + _listener(_connected, _status); + } + } + + void Closed() + { + // Abort any in progress RPC command: + _adminLock.Lock(); + + // See if we issued anything, if so abort it.. + while (_pendingQueue.size() != 0) { + + _pendingQueue.begin()->second.Abort(_pendingQueue.begin()->first); + _pendingQueue.erase(_pendingQueue.begin()); + } + + _adminLock.Unlock(); + if (_connected != false) { + _connected = false; + _listener(_connected, _status); + } + } + + int32_t Submit(const WPEFramework::Core::ProxyType& inbound) + { + int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(inbound, this)); + WPEFramework::Core::IWorkerPool::Instance().Submit(job); + return result; + } + + int32_t Inbound(const WPEFramework::Core::ProxyType& inbound) + { + int32_t result = WPEFramework::Core::ERROR_INVALID_SIGNATURE; + + ASSERT(inbound.IsValid() == true); + + if ((inbound->Id.IsSet() == true) && (inbound->Result.IsSet() || inbound->Error.IsSet())) { + // Looks like this is a response.. + ASSERT(inbound->Parameters.IsSet() == false); + ASSERT(inbound->Designator.IsSet() == false); + + _adminLock.Lock(); + + // See if we issued this.. + typename PendingMap::iterator index = _pendingQueue.find(inbound->Id.Value()); + + if (index != _pendingQueue.end()) { + + if (index->second.Signal(inbound) == true) { + _pendingQueue.erase(index); + } + + result = WPEFramework::Core::ERROR_NONE; + _adminLock.Unlock(); + } else { + _adminLock.Unlock(); + string eventName; + if (IsEvent(inbound->Id.Value(), eventName)) { + _eventHandler->Dispatch(eventName, inbound); + } + + } + } + + return (result); + } + + + template + Firebolt::Error Send(const string& method, const PARAMETERS& parameters, const uint32_t& id) + { + int32_t result = WPEFramework::Core::ERROR_UNAVAILABLE; + + if ((_channel.IsValid() == true) && (_channel->IsSuspended() == true)) { + result = WPEFramework::Core::ERROR_ASYNC_FAILED; + } + else if (_channel.IsValid() == true) { + + result = WPEFramework::Core::ERROR_ASYNC_FAILED; + + WPEFramework::Core::ProxyType message(Channel::Message()); + message->Id = id; + message->Designator = method; + ToMessage(parameters, message); + + _adminLock.Lock(); + + typename std::pair< typename PendingMap::iterator, bool> newElement = + _pendingQueue.emplace(std::piecewise_construct, + std::forward_as_tuple(id), + std::forward_as_tuple()); + ASSERT(newElement.second == true); + + if (newElement.second == true) { + + _adminLock.Unlock(); + + _channel->Submit(WPEFramework::Core::ProxyType(message)); + + message.Release(); + result = WPEFramework::Core::ERROR_NONE; + } + } + return FireboltErrorValue(result); + } + + static constexpr uint32_t WAITSLOT_TIME = 100; + template + Firebolt::Error WaitForEventResponse(const uint32_t& id, const string& eventName, RESPONSE& response, const uint32_t waitTime) + { + Firebolt::Error result = Firebolt::Error::Timedout; + _adminLock.Lock(); + typename PendingMap::iterator index = _pendingQueue.find(id); + Entry& slot(index->second); + _adminLock.Unlock(); + + uint8_t waiting = waitTime; + do { + uint32_t waitSlot = (waiting > WAITSLOT_TIME ? WAITSLOT_TIME : waiting); + if (slot.WaitForResponse(waitSlot) == true) { + WPEFramework::Core::ProxyType jsonResponse = slot.Response(); + + // See if we have a jsonResponse, maybe it was just the connection + // that closed? + if (jsonResponse.IsValid() == true) { + if (jsonResponse->Error.IsSet() == true) { + result = FireboltErrorValue(jsonResponse->Error.Code.Value()); + } else { + if ((jsonResponse->Result.IsSet() == true) + && (jsonResponse->Result.Value().empty() == false)) { + bool enabled; + result = _eventHandler->ValidateResponse(jsonResponse, enabled); + if (result == Firebolt::Error::None) { + FromMessage((INTERFACE*)&response, *jsonResponse); + if (enabled) { + _adminLock.Lock(); + typename EventMap::iterator index = _eventMap.find(eventName); + if (index != _eventMap.end()) { + index->second = id; + } + _adminLock.Unlock(); + } + } + } + } + } + } else { + result = Firebolt::Error::Timedout; + } + waiting -= (waiting == WPEFramework::Core::infinite ? 0 : waitSlot); + } while ((result != Firebolt::Error::None) && (waiting > 0 )); + _adminLock.Lock(); + _pendingQueue.erase(id); + _adminLock.Unlock(); + + return result; + } + + public: + void FromMessage(WPEFramework::Core::JSON::IElement* response, const WPEFramework::Core::JSONRPC::Message& message) const + { + response->FromString(message.Result.Value()); + } + + void FromMessage(WPEFramework::Core::JSON::IMessagePack* response, const WPEFramework::Core::JSONRPC::Message& message) const + { + string value = message.Result.Value(); + std::vector result(value.begin(), value.end()); + response->FromBuffer(result); + } + + + private: + + void ToMessage(const string& parameters, WPEFramework::Core::ProxyType& message) const + { + if (parameters.empty() != true) { + message->Parameters = parameters; + } + } + + template + void ToMessage(PARAMETERS& parameters, WPEFramework::Core::ProxyType& message) const + { + ToMessage((INTERFACE*)(¶meters), message); + return; + } + + void ToMessage(WPEFramework::Core::JSON::IMessagePack* parameters, WPEFramework::Core::ProxyType& message) const + { + std::vector values; + parameters->ToBuffer(values); + if (values.empty() != true) { + string strValues(values.begin(), values.end()); + message->Parameters = strValues; + } + return; + } + + void ToMessage(WPEFramework::Core::JSON::IElement* parameters, WPEFramework::Core::ProxyType& message) const + { + string values; + parameters->ToString(values); + if (values.empty() != true) { + message->Parameters = values; + } + return; + } + + Firebolt::Error FireboltErrorValue(const uint32_t error) + { + + Firebolt::Error fireboltError = static_cast(error); + switch (error) { + case WPEFramework::Core::ERROR_NONE: + fireboltError = Firebolt::Error::None; + break; + case WPEFramework::Core::ERROR_GENERAL: + case WPEFramework::Core::ERROR_UNAVAILABLE: + fireboltError = Firebolt::Error::General; + break; + case WPEFramework::Core::ERROR_TIMEDOUT: + fireboltError = Firebolt::Error::Timedout; + break; + default: + break; + } + + return fireboltError; + } + + private: + WPEFramework::Core::CriticalSection _adminLock; + WPEFramework::Core::NodeId _connectId; + WPEFramework::Core::ProxyType _channel; + IEventHandler* _eventHandler; + PendingMap _pendingQueue; + EventMap _eventMap; + uint64_t _scheduledTime; + uint32_t _waitTime; + Listener _listener; + bool _connected; + Firebolt::Error _status; + }; +} diff --git a/languages/cpp/src/shared/src/TypesPriv.h b/languages/cpp/src/shared/src/TypesPriv.h new file mode 100644 index 00000000..224f0552 --- /dev/null +++ b/languages/cpp/src/shared/src/TypesPriv.h @@ -0,0 +1,62 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include + +namespace FireboltSDK { +namespace JSON { +class String : public WPEFramework::Core::JSON::String { + using Base = WPEFramework::Core::JSON::String; + public: + String() + : Base() + , _value() + { + } + String(const char value[]) + : Base(value) + , _value(value) + { + } + String& operator=(const char RHS[]) + { + Base::operator = (RHS); + _value = RHS; + return (*this); + } + String& operator=(const string RHS) + { + Base::operator = (RHS); + _value = RHS; + return (*this); + } + + public: + const string& Value() const + { + _value = Base::Value(); + return _value; + } + + private: + mutable std::string _value; + }; +} +} diff --git a/languages/cpp/src/shared/test/CMakeLists.txt b/languages/cpp/src/shared/test/CMakeLists.txt new file mode 100644 index 00000000..012f1a6d --- /dev/null +++ b/languages/cpp/src/shared/test/CMakeLists.txt @@ -0,0 +1,86 @@ +# Copyright 2023 Comcast Cable Communications Management, LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.3) + +project(FireboltSDKTests) +project_version(1.0.0) + +set(TESTLIB ${PROJECT_NAME}) + +message("Setup ${TESTLIB} v${PROJECT_VERSION}") + +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +find_package(${NAMESPACE}Core CONFIG REQUIRED) + +add_library(${TESTLIB} STATIC OpenRPCTests.cpp) + +target_link_libraries(${TESTLIB} + PUBLIC + ${NAMESPACE}Core::${NAMESPACE}Core + ${FIREBOLT_NAMESPACE}SDK +) + +target_include_directories(${TESTLIB} + PRIVATE + $ + $ +) + +set_target_properties(${TESTLIB} PROPERTIES + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED YES + LINK_WHAT_YOU_USE TRUE + FRAMEWORK FALSE +) + +install( + TARGETS ${TESTLIB} EXPORT ${TESTLIB}Targets + LIBRARY DESTINATION lib COMPONENT libs + PUBLIC_HEADER DESTINATION include/${FIREBOLT_NAMESPACE}Test COMPONENT devel # headers for mac (note the different component -> different package) + INCLUDES DESTINATION include/${FIREBOLT_NAMESPACE}Test # headers +) + +InstallCMakeConfig(TARGETS ${TESTLIB}) +InstallCMakeConfigs(TARGET ${TESTLIB} DESTINATION ${FIREBOLT_NAMESPACE}) +InstallHeaders(TARGET ${TESTLIB} HEADERS . NAMESPACE ${FIREBOLT_NAMESPACE} DESTINATION FireboltTest) +InstallLibraries(TARGET ${TESTLIB} STATIC LIBRARIES ${TESTLIB} DESTINATION ${FIREBOLT_NAMESPACE}) + +set(TESTAPP "FireboltSDKTestApp") + +message("Setup ${TESTAPP}") + +add_executable(${TESTAPP} Main.cpp) + +target_link_libraries(${TESTAPP} + PRIVATE + ${TESTLIB} +) + +target_include_directories(${TESTAPP} + PRIVATE + $ + $ +) + +add_custom_command( + TARGET ${TESTAPP} + POST_BUILD + COMMENT "=================== Installing TestApp ======================" + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/bin + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${TESTAPP} ${CMAKE_BINARY_DIR}/${FIREBOLT_NAMESPACE}/usr/bin +) + diff --git a/languages/cpp/src/shared/test/Main.cpp b/languages/cpp/src/shared/test/Main.cpp new file mode 100644 index 00000000..e41dff1d --- /dev/null +++ b/languages/cpp/src/shared/test/Main.cpp @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "OpenRPCTests.h" + +int __cnt = 0; +int __pass = 0; + +int TotalTests = 0; +int TotalTestsPassed = 0; + +int main() +{ + const std::string config = _T("{\ + \"waitTime\": 1000,\ + \"logLevel\": \"Info\",\ + \"workerPool\":{\ + \"queueSize\": 8,\ + \"threadCount\": 3\ + },\ + \"wsUrl\": \"ws://127.0.0.1:9998\"\ +}"); + FireboltSDK::Accessor::Instance(config); + FireboltSDK::Tests::Main(); + + + printf("TOTAL: %i tests; %i PASSED, %i FAILED\n", TotalTests, TotalTestsPassed, (TotalTests - TotalTestsPassed)); + FireboltSDK::Accessor::Dispose(); +} diff --git a/languages/cpp/src/shared/test/Module.cpp b/languages/cpp/src/shared/test/Module.cpp new file mode 100644 index 00000000..d63badc4 --- /dev/null +++ b/languages/cpp/src/shared/test/Module.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" + +MODULE_NAME_DECLARATION(BUILD_REFERENCE) diff --git a/languages/cpp/src/shared/test/Module.h b/languages/cpp/src/shared/test/Module.h new file mode 100644 index 00000000..a147ff75 --- /dev/null +++ b/languages/cpp/src/shared/test/Module.h @@ -0,0 +1,28 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#ifndef MODULE_NAME +#define MODULE_NAME OpenRPCTestApp +#endif + +#include + +#undef EXTERNAL +#define EXTERNAL diff --git a/languages/cpp/src/shared/test/OpenRPCTests.cpp b/languages/cpp/src/shared/test/OpenRPCTests.cpp new file mode 100644 index 00000000..8fc4f4fb --- /dev/null +++ b/languages/cpp/src/shared/test/OpenRPCTests.cpp @@ -0,0 +1,319 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "Module.h" +#include "OpenRPCTests.h" + +namespace WPEFramework { + +ENUM_CONVERSION_BEGIN(::JsonValue::type) + + { JsonValue::type::EMPTY, _TXT("empty") }, + { JsonValue::type::BOOLEAN, _TXT("boolean") }, + { JsonValue::type::NUMBER, _TXT("number") }, + { JsonValue::type::STRING, _TXT("string") }, + +ENUM_CONVERSION_END(::JsonValue::type) + +ENUM_CONVERSION_BEGIN(TestEnum) + { TestEnum::Test1, _TXT("Test1ValueCheck") }, + { TestEnum::Test2, _TXT("Test2ValueCheck") }, + { TestEnum::Test3, _TXT("Test3ValueCheck") }, + { TestEnum::Test4, _TXT("Test4ValueCheck") }, +ENUM_CONVERSION_END(TestEnum) +} + +typedef void (*OnNotifyDeviceNameChanged)(const void* userData, const char* data); +static void NotifyEvent(const void* userData, const char* data) +{ + printf("NotifyEvent data : %s\n", data); +} + +namespace FireboltSDK { + Tests::Tests() + { + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("SubscribeEventWithMultipleCallback"), + std::forward_as_tuple(&SubscribeEventWithMultipleCallback)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("SubscribeEventwithSameCallback"), + std::forward_as_tuple(&SubscribeEventwithSameCallback)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("SubscribeEvent"), + std::forward_as_tuple(&SubscribeEvent)); + + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Set UnKnown Method"), + std::forward_as_tuple(&SetUnKnownMethod)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Set LifeCycle Close"), + std::forward_as_tuple(&SetLifeCycleClose)); + + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Get UnKnown Method"), + std::forward_as_tuple(&GetUnKnownMethod)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Get Device Version"), + std::forward_as_tuple(&GetDeviceVersion)); + _functionMap.emplace(std::piecewise_construct, std::forward_as_tuple("Get Device Id"), + std::forward_as_tuple(&GetDeviceId)); + } + + /* static */ void Tests::PrintJsonObject(const JsonObject::Iterator& iterator) + { + JsonObject::Iterator index = iterator; + while (index.Next() == true) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "Element [%s]: <%s> = \"%s\"\n", + index.Label(), + WPEFramework::Core::EnumerateType(index.Current().Content()).Data(), + index.Current().Value().c_str()); + } + } + + /* static */ Firebolt::Error Tests::GetDeviceId() + { + const string method = _T("device.id"); + WPEFramework::Core::ProxyType response; + Firebolt::Error status = FireboltSDK::Properties::Get(method, response); + + EXPECT_EQ(status, Firebolt::Error::None); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "DeviceId : %s", response->Value().c_str()); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Get %s status = %d\n", method.c_str(), status); + } + + return status; + } + + /*static */ Firebolt::Error Tests::GetDeviceVersion() + { + const string method = _T("device.version"); + WPEFramework::Core::ProxyType response; + Firebolt::Error status = FireboltSDK::Properties::Get(method, response); + + EXPECT_EQ(status, Firebolt::Error::None); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "DeviceVersion"); + PrintJsonObject(response->Variants()); + } else { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Get %s status = %d", method.c_str(), status); + } + + return status; + } + + /* static */ Firebolt::Error Tests::GetUnKnownMethod() + { + const string method = _T("get.unknownMethod"); + WPEFramework::Core::ProxyType response; + Firebolt::Error status = FireboltSDK::Properties::Get(method, response); + + EXPECT_NE(status, Firebolt::Error::None); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Get %s status = %d\n", method.c_str(), status); + } + + return status; + } + + /* static */ Firebolt::Error Tests::SetLifeCycleClose() + { + const string method = _T("lifecycle.close"); + JsonObject parameters; + parameters["reason"] = "remoteButton"; + Firebolt::Error status = FireboltSDK::Properties::Set(method, parameters); + + EXPECT_EQ(status, Firebolt::Error::None); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Set %s status = %d\n", method.c_str(), status); + } + + return status; + } + + /* static */ Firebolt::Error Tests::SetUnKnownMethod() + { + const string method = _T("set.unknownMethod"); + JsonObject parameters; + Firebolt::Error status = FireboltSDK::Properties::Set(method, parameters); + + EXPECT_NE(status, Firebolt::Error::None); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Set %s status = %d", method.c_str(), status); + } + + return status; + } + + static void deviceNameChangeCallback(void* userCB, const void* userData, void* response) + { + WPEFramework::Core::ProxyType& jsonResponse = *(reinterpret_cast*>(response)); + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), "Received a new event: %s", jsonResponse->Value().c_str()); + FireboltSDK::Tests::EventControl* eventControl = reinterpret_cast(const_cast(userData)); + OnNotifyDeviceNameChanged notifyDeviceNameChanged = reinterpret_cast(userCB); + notifyDeviceNameChanged(userData, jsonResponse->Value().c_str()); + eventControl->NotifyEvent(); + jsonResponse.Release(); + } + + /* static */ Firebolt::Error Tests::SubscribeEvent() + { + FireboltSDK::Tests::EventControl* eventControl = new FireboltSDK::Tests::EventControl("EventControl"); + const string eventName = _T("device.Name"); + const void* userdata = static_cast(eventControl); + + eventControl->ResetEvent(); + + JsonObject jsonParameters; + Firebolt::Error status = Properties::Subscribe(eventName, jsonParameters, deviceNameChangeCallback,reinterpret_cast(NotifyEvent), userdata); + + EXPECT_EQ(status, Firebolt::Error::None); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), + "Set %s status = %d", eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes registered successfully, Waiting for event...", __func__); + + eventControl->WaitForEvent(WPEFramework::Core::infinite); + } + + EXPECT_EQ(Properties::Unsubscribe(eventName, reinterpret_cast(NotifyEvent)), Firebolt::Error::None); + delete eventControl; + + return status; + } + + /* static */ Firebolt::Error Tests::SubscribeEventwithSameCallback() + { + FireboltSDK::Tests::EventControl* eventControl = new FireboltSDK::Tests::EventControl("EventControl"); + const string eventName = _T("device.Name"); + const void* userdata = static_cast(eventControl); + + eventControl->ResetEvent(); + + JsonObject jsonParameters; + Firebolt::Error status = Properties::Subscribe(eventName, jsonParameters, deviceNameChangeCallback,reinterpret_cast(NotifyEvent), userdata); + + EXPECT_EQ(status, Firebolt::Error::None); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), + "Set %s status = %d", eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes registered successfully", __func__); + + status = Properties::Subscribe(eventName, jsonParameters, deviceNameChangeCallback, reinterpret_cast(NotifyEvent), userdata); + EXPECT_EQ(status, Firebolt::Error::General); + if (status == Firebolt::Error::General) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes this device.name event is already registered with same callback", __func__); + } + status = ((status == Firebolt::Error::General) ? Firebolt::Error::None : status); + } + + EXPECT_EQ(Properties::Unsubscribe(eventName, reinterpret_cast(NotifyEvent)), Firebolt::Error::None); + delete eventControl; + + return status; + } + + + static void NotifyEvent1(const void* userData, const char* data) + { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "NotifyEvent1 data : %s", data); + if (userData) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "NotifyEvent1 userData : %s\n", reinterpret_cast(userData)->Name().c_str()); + } + } + static void NotifyEvent2(const void* userData, const char* data) + { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "NotifyEvent2 data : %s", data); + if (userData) { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "NotifyEvent2 userData : %s\n", reinterpret_cast(userData)->Name().c_str()); + } + } + + template + /* static */ Firebolt::Error Tests::SubscribeEventForC(const string& eventName, JsonObject& jsonParameters, CALLBACK& callbackFunc, void* usercb, const void* userdata) + { + return Properties::Subscribe(eventName, jsonParameters, callbackFunc, usercb, userdata); + } + + static void deviceNameChangeMultipleCallback(void* userCB, const void* userData, void* response) + { + WPEFramework::Core::ProxyType& jsonResponse = *(reinterpret_cast*>(response)); + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "Received a new event from deviceNameChangeMultipleCallback: %s", jsonResponse->Value().c_str()); + FireboltSDK::Tests::EventControl* eventControl = reinterpret_cast(const_cast(userData)); + OnNotifyDeviceNameChanged notifyDeviceNameChanged = reinterpret_cast(userCB); + notifyDeviceNameChanged(userData, jsonResponse->Value().c_str()); + + eventControl->NotifyEvent(); + jsonResponse.Release(); + } + + /* static */ Firebolt::Error Tests::SubscribeEventWithMultipleCallback() + { + FireboltSDK::Tests::EventControl* eventControl1 = new FireboltSDK::Tests::EventControl("EventControl1"); + const string eventName = _T("device.Name"); + const void* userdata = static_cast(eventControl1); + + eventControl1->ResetEvent(); + + JsonObject jsonParameters; + Firebolt::Error status = Properties::Subscribe(eventName, jsonParameters, deviceNameChangeMultipleCallback, reinterpret_cast(NotifyEvent1), userdata); + + EXPECT_EQ(status, Firebolt::Error::None); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), + "Set %s status = %d", eventName.c_str(), status); + } else { + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes registered successfully, Waiting for event...", __func__); + } + + if (status == Firebolt::Error::None) { + FireboltSDK::Tests::EventControl* eventControl2 = new FireboltSDK::Tests::EventControl("EventControl2"); + userdata = static_cast(eventControl2); + + status = Properties::Subscribe(eventName, jsonParameters, deviceNameChangeMultipleCallback, reinterpret_cast(NotifyEvent2), userdata); + + EXPECT_EQ(status, Firebolt::Error::None); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(Logger::Category::OpenRPC, Logger::Module(), "Set %s status = %d", eventName.c_str(), status); + } else { + status = Properties::Subscribe(eventName, jsonParameters, deviceNameChangeMultipleCallback, reinterpret_cast(NotifyEvent2), userdata); + EXPECT_EQ(status, Firebolt::Error::General); + status = ((status == Firebolt::Error::General) ? Firebolt::Error::None : status); + + FIREBOLT_LOG_INFO(Logger::Category::OpenRPC, Logger::Module(), + "%s Yes registered second callback also successfully, waiting for events...\n", __func__); + + eventControl1->WaitForEvent(WPEFramework::Core::infinite); + eventControl2->WaitForEvent(WPEFramework::Core::infinite); + } + EXPECT_EQ(Properties::Unsubscribe(eventName, reinterpret_cast(NotifyEvent2)), Firebolt::Error::None); + delete eventControl2; + } + EXPECT_EQ(Properties::Unsubscribe(eventName, reinterpret_cast(NotifyEvent1)), Firebolt::Error::None); + + delete eventControl1; + return status; + } + +} diff --git a/languages/cpp/src/shared/test/OpenRPCTests.h b/languages/cpp/src/shared/test/OpenRPCTests.h new file mode 100644 index 00000000..9c84bcf4 --- /dev/null +++ b/languages/cpp/src/shared/test/OpenRPCTests.h @@ -0,0 +1,118 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "FireboltSDK.h" +#include "TestUtils.h" + +typedef enum { + Test1, + Test2, + Test3, + Test4 +} TestEnum; + +namespace FireboltSDK { + typedef Firebolt::Error (*Func)(); + + class Tests { + public: + class EventControl { + public: + EventControl() + : _event(false, true) + , _name("EventControl") + { + } + EventControl(string name) + : _event(false, true) + , _name(name) + { + } + ~EventControl() = default; + + public: + void NotifyEvent() + { + _event.SetEvent(); + } + uint32_t WaitForEvent(uint32_t waitTime) + { + return _event.Lock(waitTime); + } + void ResetEvent() + { + _event.ResetEvent(); + } + string Name() const + { + return _name; + } + private: + WPEFramework::Core::Event _event; + string _name; + }; + + private: + typedef std::unordered_map TestFunctionMap; + + public: + Tests(); + virtual ~Tests() = default; + + inline TestFunctionMap& TestList() + { + return _functionMap; + } + + template + static int32_t Main() + { + TESTS fireboltTest; + for (auto i = fireboltTest.TestList().begin(); i != fireboltTest.TestList().end(); i++) { + EXECUTE(i->first.c_str(), i->second); + } + + printf("TOTAL: %i tests; %i PASSED, %i FAILED\n", TotalTests, TotalTestsPassed, (TotalTests - TotalTestsPassed)); + + return 0; + } + + static Firebolt::Error GetDeviceId(); + static Firebolt::Error GetDeviceVersion(); + static Firebolt::Error GetUnKnownMethod(); + + static Firebolt::Error SetLifeCycleClose(); + static Firebolt::Error SetUnKnownMethod(); + + static Firebolt::Error SubscribeEvent(); + static Firebolt::Error SubscribeEventwithSameCallback(); + static Firebolt::Error SubscribeEventWithMultipleCallback(); + + template + static Firebolt::Error SubscribeEventForC(const string& eventName, JsonObject& jsonParameters, CALLBACK& callbackFunc, void* usercb, const void* userdata); + + protected: + static void PrintJsonObject(const JsonObject::Iterator& iterator); + + protected: + std::list menu; + TestFunctionMap _functionMap; + }; +} diff --git a/languages/cpp/src/shared/test/TestUtils.h b/languages/cpp/src/shared/test/TestUtils.h new file mode 100644 index 00000000..c54db9f0 --- /dev/null +++ b/languages/cpp/src/shared/test/TestUtils.h @@ -0,0 +1,38 @@ +#ifndef TEST_UTILS_H +#define TEST_UTILS_H + +#include +#include +#include + +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +#define _RESULT(expr, exprorig, result) if (expr) { printf("TestStatus: SUCCESS: %s\n", #exprorig); __pass++; } else printf("TestStatus: FAILED: %s, actual: %lu\n", #exprorig, result) +#define _EVAL(result, expected, op) do { __cnt++; long resval = ((long)(result)); long expval = ((long)(expected)); _RESULT(resval op expval, result op expected, resval); } while(0) +#define _HEAD(name) printf("\n======== %s\n", name); __cnt = 0; __pass = 0 +#define _FOOT(name) printf("\n======== %s - %i PASSED, %i FAILED\n", name, __pass, (__cnt - __pass)); TotalTests += __cnt; TotalTestsPassed += __pass; + +#define EXECUTE(name, test) do { _HEAD(name); test(); _FOOT(name); printf("\n"); } while(0) +#define EXPECT_EQ(result, expected) _EVAL(result, expected, ==) +#define EXPECT_NE(result, expected) _EVAL(result, expected, !=) +#define EXPECT_LT(result, expected) _EVAL(result, expected, <) +#define EXPECT_LE(result, expected) _EVAL(result, expected, <=) +#define EXPECT_GT(result, expected) _EVAL(result, expected, >) +#define EXPECT_GE(result, expected) _EVAL(result, expected, >=) + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __cnt; +extern int __pass; + +extern int TotalTests ; +extern int TotalTestsPassed; + +#ifdef __cplusplus +} +#endif + +#endif // TEST_UTILS_H diff --git a/languages/cpp/templates/additional-types/boolean.cpp b/languages/cpp/templates/additional-types/boolean.cpp new file mode 100644 index 00000000..feef819e --- /dev/null +++ b/languages/cpp/templates/additional-types/boolean.cpp @@ -0,0 +1 @@ +Boolean() \ No newline at end of file diff --git a/languages/cpp/templates/additional-types/integer.cpp b/languages/cpp/templates/additional-types/integer.cpp new file mode 100644 index 00000000..15c659a4 --- /dev/null +++ b/languages/cpp/templates/additional-types/integer.cpp @@ -0,0 +1 @@ +Number() \ No newline at end of file diff --git a/languages/cpp/templates/additional-types/number.cpp b/languages/cpp/templates/additional-types/number.cpp new file mode 100644 index 00000000..a5d368a4 --- /dev/null +++ b/languages/cpp/templates/additional-types/number.cpp @@ -0,0 +1 @@ +Float() \ No newline at end of file diff --git a/languages/cpp/templates/additional-types/string.cpp b/languages/cpp/templates/additional-types/string.cpp new file mode 100644 index 00000000..d2a52c84 --- /dev/null +++ b/languages/cpp/templates/additional-types/string.cpp @@ -0,0 +1 @@ +String() \ No newline at end of file diff --git a/languages/cpp/templates/callback-context-instantiation/generic.cpp b/languages/cpp/templates/callback-context-instantiation/generic.cpp new file mode 100644 index 00000000..a715e2cc --- /dev/null +++ b/languages/cpp/templates/callback-context-instantiation/generic.cpp @@ -0,0 +1,3 @@ + if (strcmp(elements.Label(), "${property}") == 0) { + ${property} = elements.Current().Value(); + } \ No newline at end of file diff --git a/languages/cpp/templates/callback-context-instantiation/primitive.cpp b/languages/cpp/templates/callback-context-instantiation/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-context-instantiation/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-context-instantiation/ref.h b/languages/cpp/templates/callback-context-instantiation/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-context-instantiation/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization-optional/generic.cpp b/languages/cpp/templates/callback-initialization-optional/generic.cpp new file mode 100644 index 00000000..fc9cb1dd --- /dev/null +++ b/languages/cpp/templates/callback-initialization-optional/generic.cpp @@ -0,0 +1 @@ + std::optional<${type}> ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization-optional/primitive.cpp b/languages/cpp/templates/callback-initialization-optional/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-initialization-optional/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization/additionalProperties.cpp b/languages/cpp/templates/callback-initialization/additionalProperties.cpp new file mode 100644 index 00000000..754b0fed --- /dev/null +++ b/languages/cpp/templates/callback-initialization/additionalProperties.cpp @@ -0,0 +1 @@ + ${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame}${title} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization/anyOf.cpp b/languages/cpp/templates/callback-initialization/anyOf.cpp new file mode 100644 index 00000000..30f233a7 --- /dev/null +++ b/languages/cpp/templates/callback-initialization/anyOf.cpp @@ -0,0 +1 @@ +{property}; diff --git a/languages/cpp/templates/callback-initialization/array.cpp b/languages/cpp/templates/callback-initialization/array.cpp new file mode 100644 index 00000000..7ecdb522 --- /dev/null +++ b/languages/cpp/templates/callback-initialization/array.cpp @@ -0,0 +1 @@ + ${type} ${property}; diff --git a/languages/cpp/templates/callback-initialization/enum.cpp b/languages/cpp/templates/callback-initialization/enum.cpp new file mode 100644 index 00000000..754b0fed --- /dev/null +++ b/languages/cpp/templates/callback-initialization/enum.cpp @@ -0,0 +1 @@ + ${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame}${title} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization/generic.cpp b/languages/cpp/templates/callback-initialization/generic.cpp new file mode 100644 index 00000000..b8231bf0 --- /dev/null +++ b/languages/cpp/templates/callback-initialization/generic.cpp @@ -0,0 +1 @@ + ${type} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization/object-empty-property.cpp b/languages/cpp/templates/callback-initialization/object-empty-property.cpp new file mode 100644 index 00000000..92a17554 --- /dev/null +++ b/languages/cpp/templates/callback-initialization/object-empty-property.cpp @@ -0,0 +1 @@ + std::string ${property}; diff --git a/languages/cpp/templates/callback-initialization/object.cpp b/languages/cpp/templates/callback-initialization/object.cpp new file mode 100644 index 00000000..754b0fed --- /dev/null +++ b/languages/cpp/templates/callback-initialization/object.cpp @@ -0,0 +1 @@ + ${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame}${title} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization/primitive.cpp b/languages/cpp/templates/callback-initialization/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-initialization/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-initialization/title.cpp b/languages/cpp/templates/callback-initialization/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/callback-initialization/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/callback-instantiation/generic.cpp b/languages/cpp/templates/callback-instantiation/generic.cpp new file mode 100644 index 00000000..c4204a65 --- /dev/null +++ b/languages/cpp/templates/callback-instantiation/generic.cpp @@ -0,0 +1,22 @@ + std::string str; + proxyResponse->ToString(str); + WPEFramework::Core::JSON::VariantContainer variantContainer(str); + WPEFramework::Core::JSON::Variant resultVariant; + if (variantContainer.HasLabel("context") == true) { + WPEFramework::Core::JSON::VariantContainer::Iterator elements = variantContainer.Variants(); + while (elements.Next()) { + ${callback.result.initialization.with.indent} + else if (strcmp(elements.Label(), "context") == 0) { + WPEFramework::Core::JSON::VariantContainer::Iterator params = elements.Current().Object().Variants(); + while (params.Next()) { + ${callback.param.instantiation.with.indent} + } + } else { + ASSERT(false); + } + } + } else { + resultVariant = variantContainer; + } + +${callback.result.instantiation} diff --git a/languages/cpp/templates/callback-parameter-serialization/generic.cpp b/languages/cpp/templates/callback-parameter-serialization/generic.cpp new file mode 100644 index 00000000..530005ca --- /dev/null +++ b/languages/cpp/templates/callback-parameter-serialization/generic.cpp @@ -0,0 +1 @@ + WPEFramework::Core::ProxyType<${method.result.json}>* proxyResponse = reinterpret_cast*>(jsonResponse); \ No newline at end of file diff --git a/languages/cpp/templates/callback-parameter-serialization/primitive.cpp b/languages/cpp/templates/callback-parameter-serialization/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-parameter-serialization/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-parameter-serialization/ref.h b/languages/cpp/templates/callback-parameter-serialization/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-parameter-serialization/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-response-instantiation/additionalProperties.cpp b/languages/cpp/templates/callback-response-instantiation/additionalProperties.cpp new file mode 100644 index 00000000..b39e4545 --- /dev/null +++ b/languages/cpp/templates/callback-response-instantiation/additionalProperties.cpp @@ -0,0 +1 @@ +${property} \ No newline at end of file diff --git a/languages/cpp/templates/callback-response-instantiation/generic.cpp b/languages/cpp/templates/callback-response-instantiation/generic.cpp new file mode 100644 index 00000000..b39e4545 --- /dev/null +++ b/languages/cpp/templates/callback-response-instantiation/generic.cpp @@ -0,0 +1 @@ +${property} \ No newline at end of file diff --git a/languages/cpp/templates/callback-response-instantiation/primitive.cpp b/languages/cpp/templates/callback-response-instantiation/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-response-instantiation/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-response-instantiation/ref.h b/languages/cpp/templates/callback-response-instantiation/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-response-instantiation/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/additionalProperties.cpp b/languages/cpp/templates/callback-result-instantiation/additionalProperties.cpp new file mode 100644 index 00000000..59e3acf2 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/additionalProperties.cpp @@ -0,0 +1,7 @@ + ${if.namespace.notsame}${info.Title}::${end.if.namespace.notsame}JsonData_${title}::Iterator elements = proxyResponse->Variants(); + while (elements.Next()) { +${if.not.default} ${namespace}${key} key = WPEFramework::Core::EnumerateType<${namespace}${key}>(elements.Label(), false).Value();${end.if.not.default}${if.default} ${key} key = elements.Label();${end.if.default} + ${property}.emplace(std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(elements.Current().${additional.type})); + } diff --git a/languages/cpp/templates/callback-result-instantiation/anyOf.cpp b/languages/cpp/templates/callback-result-instantiation/anyOf.cpp new file mode 100644 index 00000000..120561dd --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/anyOf.cpp @@ -0,0 +1 @@ + ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/array.cpp b/languages/cpp/templates/callback-result-instantiation/array.cpp new file mode 100644 index 00000000..0e48a92b --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/array.cpp @@ -0,0 +1,4 @@ + auto index(proxyResponse->Elements()); + while (index.Next() == true) { + ${property}.push_back(index.Current().Value()); + } \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/enum.cpp b/languages/cpp/templates/callback-result-instantiation/enum.cpp new file mode 100644 index 00000000..6d8fa873 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/enum.cpp @@ -0,0 +1 @@ + ${property} = proxyResponse->Value(); \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/generic.cpp b/languages/cpp/templates/callback-result-instantiation/generic.cpp new file mode 100644 index 00000000..4a22d102 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/generic.cpp @@ -0,0 +1 @@ + ${property} = proxyResponse->Value(); diff --git a/languages/cpp/templates/callback-result-instantiation/namespace.cpp b/languages/cpp/templates/callback-result-instantiation/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/object.cpp b/languages/cpp/templates/callback-result-instantiation/object.cpp new file mode 100644 index 00000000..c04d0a50 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/object.cpp @@ -0,0 +1 @@ +${properties} diff --git a/languages/cpp/templates/callback-result-instantiation/primitive.cpp b/languages/cpp/templates/callback-result-instantiation/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/property.cpp b/languages/cpp/templates/callback-result-instantiation/property.cpp new file mode 100644 index 00000000..bb62934b --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/property.cpp @@ -0,0 +1,3 @@ +${shape} ${if.non.const}${if.non.anyOf}${if.non.array}${if.non.object}${if.optional}if (proxyResponse->${Property}.IsSet()) { + ${base.title}.${property} = proxyResponse->${Property}; + }${end.if.optional}${if.non.optional}${base.title}.${property} = proxyResponse->${Property};${end.if.non.optional}${end.if.non.object}${end.if.non.array}${end.if.non.anyOf}${end.if.non.const} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/ref.h b/languages/cpp/templates/callback-result-instantiation/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/anyOf.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/anyOf.cpp new file mode 100644 index 00000000..e2e80c7c --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/anyOf.cpp @@ -0,0 +1 @@ +${Title} diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/anyOfSchemaShape.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/anyOfSchemaShape.cpp new file mode 100644 index 00000000..25269b21 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/anyOfSchemaShape.cpp @@ -0,0 +1,7 @@ + ${if.optional}if ((*proxyResponse)${Property.dependency}.${Property}.IsSet()) { + string ${property}Str; + (*proxyResponse)${Property.dependency}.${Property}.ToString(${property}Str); + ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = ${property}Str; + }${end.if.optional}${if.non.optional}string ${property}Str; + (*proxyResponse)${Property.dependency}.${Property}.ToString(${property}Str); + ${base.title}${property.dependency}.${property} = ${property}Str;${end.if.non.optional} diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/array.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/array.cpp new file mode 100644 index 00000000..c9775b99 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/array.cpp @@ -0,0 +1,10 @@ + ${if.optional}if ((*proxyResponse)${Property.dependency}.${Property}.IsSet()) { + ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = std::make_optional<${type}>(); + auto index((*proxyResponse)${Property.dependency}.${Property}.Elements()); + while (index.Next() == true) { + ${if.object}${items.with.indent}${end.if.object}${if.non.object} ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.value().push_back(index.Current().Value());${end.if.non.object} + } + }${end.if.optional}${if.non.optional}auto index((*proxyResponse)${Property.dependency}.${Property}.Elements()); + while (index.Next() == true) { +${if.object}${items.with.indent}${end.if.object}${if.non.object} ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.value().push_back(index.Current().Value());${end.if.non.object} + }${end.if.non.optional} diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/const.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/const.cpp new file mode 100644 index 00000000..d7969951 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/const.cpp @@ -0,0 +1,3 @@ + ${if.optional}if ((*proxyResponse)${Property.dependency}.${Property}.IsSet()) { + ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = (*proxyResponse)${Property.dependency}.${Property}; + }${end.if.optional}${if.non.optional}${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = (*proxyResponse)${Property.dependency}.${Property};${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/namespace.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/object-array.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/object-array.cpp new file mode 100644 index 00000000..7bcc042c --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/object-array.cpp @@ -0,0 +1,4 @@ + ${type} ${property}Result${level}; + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} jsonResult = index.Current(); +${properties} + ${property}response.${property}${if.impl.array.optional}.value()${end.if.impl.array.optional}.push_back(${property}Result${level}); \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/object-separator.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/object-separator.cpp new file mode 100644 index 00000000..945c9b46 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/object-separator.cpp @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/object.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/object.cpp new file mode 100644 index 00000000..f41b4a30 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/object.cpp @@ -0,0 +1,6 @@ +${if.optional} if ((*proxyResponse)${Property.dependency}.IsSet()) { + ${base.title}${property.dependency} = std::make_optional<${type}>(); +${properties} + }${end.if.optional}${if.non.optional} { +${properties} + }${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/property.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/property.cpp new file mode 100644 index 00000000..8cd83ce0 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/property.cpp @@ -0,0 +1,3 @@ +${shape} ${if.non.const}${if.non.anyOf}${if.non.array}${if.non.object}${if.optional}if ((*proxyResponse)${Property.dependency}.${Property}.IsSet()) { + ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = (*proxyResponse)${Property.dependency}.${Property}; + }${end.if.optional}${if.non.optional}${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = (*proxyResponse)${Property.dependency}.${Property};${end.if.non.optional}${end.if.non.object}${end.if.non.array}${end.if.non.anyOf}${end.if.non.const} diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/ref.h b/languages/cpp/templates/callback-result-instantiation/sub-property/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/sub-property/title.cpp b/languages/cpp/templates/callback-result-instantiation/sub-property/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/sub-property/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/title.cpp b/languages/cpp/templates/callback-result-instantiation/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-instantiation/tuple.cpp b/languages/cpp/templates/callback-result-instantiation/tuple.cpp new file mode 100644 index 00000000..09c06ed0 --- /dev/null +++ b/languages/cpp/templates/callback-result-instantiation/tuple.cpp @@ -0,0 +1,2 @@ + ${property}.first = proxyResponse->Get(0); + ${property}.second = proxyResponse->Get(1); \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-serialization/additionalProperties.cpp b/languages/cpp/templates/callback-result-serialization/additionalProperties.cpp new file mode 100644 index 00000000..1ee44075 --- /dev/null +++ b/languages/cpp/templates/callback-result-serialization/additionalProperties.cpp @@ -0,0 +1 @@ + WPEFramework::Core::ProxyType& proxyResponse = *(reinterpret_cast*>(jsonResponse)); diff --git a/languages/cpp/templates/callback-result-serialization/generic.cpp b/languages/cpp/templates/callback-result-serialization/generic.cpp new file mode 100644 index 00000000..c08e747a --- /dev/null +++ b/languages/cpp/templates/callback-result-serialization/generic.cpp @@ -0,0 +1 @@ + WPEFramework::Core::ProxyType<${method.result.json}>& proxyResponse = *(reinterpret_cast*>(jsonResponse)); diff --git a/languages/cpp/templates/callback-result-serialization/primitive.cpp b/languages/cpp/templates/callback-result-serialization/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-result-serialization/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-result-serialization/ref.h b/languages/cpp/templates/callback-result-serialization/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-result-serialization/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-serialization/generic.cpp b/languages/cpp/templates/callback-serialization/generic.cpp new file mode 100644 index 00000000..1ee44075 --- /dev/null +++ b/languages/cpp/templates/callback-serialization/generic.cpp @@ -0,0 +1 @@ + WPEFramework::Core::ProxyType& proxyResponse = *(reinterpret_cast*>(jsonResponse)); diff --git a/languages/cpp/templates/callback-value-initialization/generic.cpp b/languages/cpp/templates/callback-value-initialization/generic.cpp new file mode 100644 index 00000000..e1538b8a --- /dev/null +++ b/languages/cpp/templates/callback-value-initialization/generic.cpp @@ -0,0 +1,3 @@ + if (strcmp(elements.Label(), "${property}") == 0) { + resultVariant = elements.Current(); + } \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/additionalProperties.cpp b/languages/cpp/templates/callback-value-instantiation/additionalProperties.cpp new file mode 100644 index 00000000..8d5b8f69 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/additionalProperties.cpp @@ -0,0 +1,4 @@ + ${if.namespace.notsame}${info.Title}::${end.if.namespace.notsame}JsonData_${title}::Iterator elements = resultVariant.Variants(); + while (elements.Next()) { + ${property}.insert(elements.Label(), elements.Current().${additional.type}; + } \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/anyOf.cpp b/languages/cpp/templates/callback-value-instantiation/anyOf.cpp new file mode 100644 index 00000000..51f4817e --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/anyOf.cpp @@ -0,0 +1 @@ + ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/array.cpp b/languages/cpp/templates/callback-value-instantiation/array.cpp new file mode 100644 index 00000000..003dd0c7 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/array.cpp @@ -0,0 +1,4 @@ + auto index(element.Current().Elements()); + while (index.Next() == true) { + ${property}.push_back(resultVariant.Value()); + } \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/enum.cpp b/languages/cpp/templates/callback-value-instantiation/enum.cpp new file mode 100644 index 00000000..b61f87c9 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/enum.cpp @@ -0,0 +1 @@ + ${property} = WPEFramework::Core::EnumerateType<${if.namespace.notsame}${info.Title}::${end.if.namespace.notsame}${title}>(resultVariant.String().c_str(), false).Value(); diff --git a/languages/cpp/templates/callback-value-instantiation/generic.cpp b/languages/cpp/templates/callback-value-instantiation/generic.cpp new file mode 100644 index 00000000..ee9af58f --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/generic.cpp @@ -0,0 +1 @@ + ${property} = resultVariant.Value(); \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/namespace.cpp b/languages/cpp/templates/callback-value-instantiation/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/object.cpp b/languages/cpp/templates/callback-value-instantiation/object.cpp new file mode 100644 index 00000000..f60624b9 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/object.cpp @@ -0,0 +1,6 @@ + WPEFramework::Core::JSON::VariantContainer container = resultVariant.Object(); + std::string strContainer; + container.ToString(strContainer); + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} response; + response.FromString(strContainer); +${properties} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/primitive.cpp b/languages/cpp/templates/callback-value-instantiation/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/property.cpp b/languages/cpp/templates/callback-value-instantiation/property.cpp new file mode 100644 index 00000000..c9d1326e --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/property.cpp @@ -0,0 +1 @@ +${shape} ${if.non.const}${if.non.anyOf}${if.non.array}${if.non.object}${base.title}.${property} = response.${Property};${end.if.non.object}${end.if.non.array}${end.if.non.anyOf}${end.if.non.const} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/ref.h b/languages/cpp/templates/callback-value-instantiation/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/string.cpp b/languages/cpp/templates/callback-value-instantiation/string.cpp new file mode 100644 index 00000000..e5179870 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/string.cpp @@ -0,0 +1 @@ + ${property} = resultVariant.Value().c_str(); \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/anyOf.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/anyOf.cpp new file mode 100644 index 00000000..e2e80c7c --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/anyOf.cpp @@ -0,0 +1 @@ +${Title} diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/anyOfSchemaShape.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/anyOfSchemaShape.cpp new file mode 100644 index 00000000..ec176ef3 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/anyOfSchemaShape.cpp @@ -0,0 +1,3 @@ + string ${property}Str; + response${Property.dependency}.${Property}.ToString(${property}Str); + ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = ${property}Str; diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/array.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/array.cpp new file mode 100644 index 00000000..0fe4db79 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/array.cpp @@ -0,0 +1,5 @@ + ${if.impl.array.optional}${base.title}.${property} = std::make_optional<${type}>();${end.if.impl.array.optional} + auto index(response.${Property}.Elements()); + while (index.Next() == true) { +${if.object}${items.with.indent}${end.if.object}${if.non.object} ${base.title}.${property}${if.impl.array.optional}.value()${end.if.impl.array.optional}.push_back(index.Current().Value());${end.if.non.object} + } \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/const.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/const.cpp new file mode 100644 index 00000000..9d8917da --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/const.cpp @@ -0,0 +1 @@ + ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = response${Property.dependency}.${Property}; diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/generic.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/generic.cpp new file mode 100644 index 00000000..9d8917da --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/generic.cpp @@ -0,0 +1 @@ + ${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = response${Property.dependency}.${Property}; diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/namespace.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/object-array.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/object-array.cpp new file mode 100644 index 00000000..ea737fd0 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/object-array.cpp @@ -0,0 +1,4 @@ + ${type} ${property}Result${level}; + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} jsonResult = index.Current(); +${properties} + ${base.title}.${property}${if.impl.array.optional}.value()${end.if.impl.array.optional}.push_back(${property}Result${level}); \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/object-separator.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/object-separator.cpp new file mode 100644 index 00000000..945c9b46 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/object-separator.cpp @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/object.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/object.cpp new file mode 100644 index 00000000..e3462f94 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/object.cpp @@ -0,0 +1,4 @@ +${if.impl.optional} ${base.title}${property.dependency} = std::make_optional<${type}>();${end.if.impl.optional} + { +${properties} + } \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/property.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/property.cpp new file mode 100644 index 00000000..2df36ee8 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/property.cpp @@ -0,0 +1 @@ +${shape} ${if.non.const}${if.non.anyOf}${if.non.array}${if.non.object}${base.title}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = response${Property.dependency}.${Property};${end.if.non.object}${end.if.non.array}${end.if.non.anyOf}${end.if.non.const} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/ref.h b/languages/cpp/templates/callback-value-instantiation/sub-property/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/sub-property/title.cpp b/languages/cpp/templates/callback-value-instantiation/sub-property/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/sub-property/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/title.cpp b/languages/cpp/templates/callback-value-instantiation/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/callback-value-instantiation/tuple.cpp b/languages/cpp/templates/callback-value-instantiation/tuple.cpp new file mode 100644 index 00000000..2180211f --- /dev/null +++ b/languages/cpp/templates/callback-value-instantiation/tuple.cpp @@ -0,0 +1,2 @@ + ${property}.first = resultVariant.Get(0); + ${property}.second = resultVariant.Get(1); \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/interface.cpp b/languages/cpp/templates/codeblocks/interface.cpp new file mode 100644 index 00000000..b62df483 --- /dev/null +++ b/languages/cpp/templates/codeblocks/interface.cpp @@ -0,0 +1,47 @@ + static void ProviderInvokeSession(std::string& methodName, JsonObject& jsonParameters, Firebolt::Error *err = nullptr) + { + Firebolt::Error status = Firebolt::Error::NotConnected; + FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); + if (transport != nullptr) { + + JsonObject jsonResult; + status = transport->Invoke(methodName, jsonParameters, jsonResult); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "%s is successfully invoked", methodName.c_str()); + } + + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Transport err = %d", status); + } + if (err != nullptr) { + *err = status; + } + } + static void ProviderFocusSession(std::string methodName, std::string& correlationId, Firebolt::Error *err = nullptr) + { + JsonObject jsonParameters; + WPEFramework::Core::JSON::Variant CorrelationId(correlationId); + jsonParameters.Set(_T("correlationId"), CorrelationId); + + ProviderInvokeSession(methodName, jsonParameters, err); + } + static void ProviderResultSession(std::string methodName, std::string& correlationId, ${provider.xresponse.name} result, Firebolt::Error *err = nullptr) + { + JsonObject jsonParameters; + WPEFramework::Core::JSON::Variant CorrelationId(correlationId); + jsonParameters.Set(_T("correlationId"), CorrelationId); + +${provider.xresponse.serialization} + ProviderInvokeSession(methodName, jsonParameters, err); + } + static void ProviderErrorSession(std::string methodName, std::string& correlationId, ${provider.xerror.name} result, Firebolt::Error *err = nullptr) + { + JsonObject jsonParameters; + WPEFramework::Core::JSON::Variant CorrelationId(correlationId); + jsonParameters.Set(_T("correlationId"), CorrelationId); + +${provider.xerror.serialization} + ProviderInvokeSession(methodName, jsonParameters, err); + } + +${methods} diff --git a/languages/cpp/templates/codeblocks/interface.h b/languages/cpp/templates/codeblocks/interface.h new file mode 100644 index 00000000..eb299a9c --- /dev/null +++ b/languages/cpp/templates/codeblocks/interface.h @@ -0,0 +1,12 @@ +struct I${info.Title}Session : virtual public IFocussableProviderSession { + virtual ~I${info.Title}Session() override = default; + + virtual void error( ${provider.xerror.name} error, Firebolt::Error *err = nullptr ) = 0; + virtual void result( ${provider.xresponse.name} result, Firebolt::Error *err = nullptr ) = 0; +}; + +struct I${info.Title}Provider { + virtual ~I${info.Title}Provider() = default; + +${methods} +}; \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/module-include-private.cpp b/languages/cpp/templates/codeblocks/module-include-private.cpp new file mode 100644 index 00000000..b311fedd --- /dev/null +++ b/languages/cpp/templates/codeblocks/module-include-private.cpp @@ -0,0 +1,2 @@ +${if.modules}#include "${info.title.lowercase}_impl.h" +${end.if.modules}${module.includes.private} \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/module-include.h b/languages/cpp/templates/codeblocks/module-include.h new file mode 100644 index 00000000..c91e46d0 --- /dev/null +++ b/languages/cpp/templates/codeblocks/module-include.h @@ -0,0 +1,2 @@ +${if.modules}#include "${info.title.lowercase}.h" +${end.if.modules}${module.includes} \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/module-init.cpp b/languages/cpp/templates/codeblocks/module-init.cpp new file mode 100644 index 00000000..4d166513 --- /dev/null +++ b/languages/cpp/templates/codeblocks/module-init.cpp @@ -0,0 +1,15 @@ +${if.modules} ${info.Title}::I${info.Title}& ${info.Title}Interface() const override + { + auto module = _moduleMap.find("${info.Title}"); + ${info.Title}::I${info.Title}* ${info.title.lowercase} = nullptr; + + if (module != _moduleMap.end()) { + ${info.title.lowercase} = reinterpret_cast<${info.Title}::I${info.Title}*>(module->second); + } else { + ${info.title.lowercase} = reinterpret_cast<${info.Title}::I${info.Title}*>(new ${info.Title}::${info.Title}Impl()); + _moduleMap.emplace("${info.Title}", reinterpret_cast(${info.title.lowercase})); + } + return *${info.title.lowercase}; + } + +${end.if.modules}${module.init} \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/module-init.h b/languages/cpp/templates/codeblocks/module-init.h new file mode 100644 index 00000000..c25e9380 --- /dev/null +++ b/languages/cpp/templates/codeblocks/module-init.h @@ -0,0 +1,3 @@ +${if.modules} virtual ${info.Title}::I${info.Title}& ${info.Title}Interface() const = 0; + +${end.if.modules}${module.init} \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/provider-subscribe.cpp b/languages/cpp/templates/codeblocks/provider-subscribe.cpp new file mode 100644 index 00000000..88c3dffe --- /dev/null +++ b/languages/cpp/templates/codeblocks/provider-subscribe.cpp @@ -0,0 +1 @@ +${subscribe} \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/provider.cpp b/languages/cpp/templates/codeblocks/provider.cpp new file mode 100644 index 00000000..5b4cf7b8 --- /dev/null +++ b/languages/cpp/templates/codeblocks/provider.cpp @@ -0,0 +1 @@ +${interface} \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/provider.h b/languages/cpp/templates/codeblocks/provider.h new file mode 100644 index 00000000..b2895fe0 --- /dev/null +++ b/languages/cpp/templates/codeblocks/provider.h @@ -0,0 +1 @@ +${interface} diff --git a/languages/cpp/templates/codeblocks/setter.cpp b/languages/cpp/templates/codeblocks/setter.cpp new file mode 100644 index 00000000..8038cf8a --- /dev/null +++ b/languages/cpp/templates/codeblocks/setter.cpp @@ -0,0 +1,16 @@ + + /* ${method.name} - ${method.description} */ + void ${info.Title}Impl::${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err ) + { + const string method = _T("${info.title.lowercase}.${method.name}"); + + JsonObject jsonParameters; +${if.params}${method.params.serialization}${end.if.params} + + Firebolt::Error status = FireboltSDK::Properties::Set(method, jsonParameters); + if (err != nullptr) { + *err = status; + } + + return; + } \ No newline at end of file diff --git a/languages/cpp/templates/codeblocks/subscribe.cpp b/languages/cpp/templates/codeblocks/subscribe.cpp new file mode 100644 index 00000000..4039ca95 --- /dev/null +++ b/languages/cpp/templates/codeblocks/subscribe.cpp @@ -0,0 +1,5 @@ + eventName = _T("${info.title.lowercase}.onRequest${method.Name}"); + status = FireboltSDK::Event::Instance().Subscribe<${event.result.json.type}>(eventName, jsonParameters, ${info.Title}${method.Name}SessionInnerCallback, reinterpret_cast(&provider), nullptr); + if (status != Firebolt::Error::None) { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in %s subscribe = %d", eventName.c_str(), status); + } diff --git a/languages/cpp/templates/declarations-override/allowsFocus.h b/languages/cpp/templates/declarations-override/allowsFocus.h new file mode 100644 index 00000000..e48d9e75 --- /dev/null +++ b/languages/cpp/templates/declarations-override/allowsFocus.h @@ -0,0 +1,6 @@ + /* + ${method.name} + ${method.description} + */ + void request${method.Name}(${method.signature.params}${if.params}, ${end.if.params}I${info.Title}AsyncResponse& response, Firebolt::Error *err = nullptr ) override; + void abort${method.Name}(I${info.Title}AsyncResponse& response, Firebolt::Error *err = nullptr) override; diff --git a/languages/cpp/templates/declarations-override/calls-metrics.h b/languages/cpp/templates/declarations-override/calls-metrics.h new file mode 100644 index 00000000..47554196 --- /dev/null +++ b/languages/cpp/templates/declarations-override/calls-metrics.h @@ -0,0 +1,5 @@ + /* + ${method.name} + ${method.description} + */ + ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr )${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} override; diff --git a/languages/cpp/templates/declarations-override/clear.h b/languages/cpp/templates/declarations-override/clear.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations-override/default.h b/languages/cpp/templates/declarations-override/default.h new file mode 100644 index 00000000..47554196 --- /dev/null +++ b/languages/cpp/templates/declarations-override/default.h @@ -0,0 +1,5 @@ + /* + ${method.name} + ${method.description} + */ + ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr )${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} override; diff --git a/languages/cpp/templates/declarations-override/event.h b/languages/cpp/templates/declarations-override/event.h new file mode 100644 index 00000000..25fd5887 --- /dev/null +++ b/languages/cpp/templates/declarations-override/event.h @@ -0,0 +1,4 @@ + // signature callback params: ${event.signature.callback.params} + // method result properties : ${method.result.properties} + void subscribe( ${event.signature.params}${if.event.params}, ${end.if.event.params}I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) override; + void unsubscribe( I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) override; diff --git a/languages/cpp/templates/declarations-override/listen.h b/languages/cpp/templates/declarations-override/listen.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations-override/once.h b/languages/cpp/templates/declarations-override/once.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations-override/polymorphic-pull-event.h b/languages/cpp/templates/declarations-override/polymorphic-pull-event.h new file mode 100644 index 00000000..99a4f5e0 --- /dev/null +++ b/languages/cpp/templates/declarations-override/polymorphic-pull-event.h @@ -0,0 +1,2 @@ + void subscribe( I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) override; + void unsubscribe( I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) override; diff --git a/languages/cpp/templates/declarations-override/polymorphic-pull.h b/languages/cpp/templates/declarations-override/polymorphic-pull.h new file mode 100644 index 00000000..47554196 --- /dev/null +++ b/languages/cpp/templates/declarations-override/polymorphic-pull.h @@ -0,0 +1,5 @@ + /* + ${method.name} + ${method.description} + */ + ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr )${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} override; diff --git a/languages/cpp/templates/declarations-override/polymorphic-reducer.h b/languages/cpp/templates/declarations-override/polymorphic-reducer.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations-override/property.h b/languages/cpp/templates/declarations-override/property.h new file mode 100644 index 00000000..d62e1bd9 --- /dev/null +++ b/languages/cpp/templates/declarations-override/property.h @@ -0,0 +1,5 @@ + /* + * ${method.description} + * ${method.params} + */ + ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr ) const override; \ No newline at end of file diff --git a/languages/cpp/templates/declarations-override/provide.h b/languages/cpp/templates/declarations-override/provide.h new file mode 100644 index 00000000..5fca741f --- /dev/null +++ b/languages/cpp/templates/declarations-override/provide.h @@ -0,0 +1 @@ + void provide( I${info.Title}Provider& provider ) override; diff --git a/languages/cpp/templates/declarations-override/rpc-only.h b/languages/cpp/templates/declarations-override/rpc-only.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations-override/setter.h b/languages/cpp/templates/declarations-override/setter.h new file mode 100644 index 00000000..752833ff --- /dev/null +++ b/languages/cpp/templates/declarations-override/setter.h @@ -0,0 +1,5 @@ + /* + * ${method.name} + * ${method.description} + */ + void ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr ) override; diff --git a/languages/cpp/templates/declarations/allowsFocus.h b/languages/cpp/templates/declarations/allowsFocus.h new file mode 100644 index 00000000..707a6ff1 --- /dev/null +++ b/languages/cpp/templates/declarations/allowsFocus.h @@ -0,0 +1,6 @@ + /* + ${method.name} + ${method.description} + */ + virtual void request${method.Name}(${method.signature.params}${if.params}, ${end.if.params}I${info.Title}AsyncResponse& response, Firebolt::Error *err = nullptr ) = 0; + virtual void abort${method.Name}(I${info.Title}AsyncResponse& response, Firebolt::Error *err = nullptr) = 0; diff --git a/languages/cpp/templates/declarations/calls-metrics.h b/languages/cpp/templates/declarations/calls-metrics.h new file mode 100644 index 00000000..fa1890c5 --- /dev/null +++ b/languages/cpp/templates/declarations/calls-metrics.h @@ -0,0 +1,6 @@ + /* + ${method.name} + ${method.description} + ${method.params.annotations}${if.deprecated} * @deprecated ${method.deprecation}${end.if.deprecated} + */ + virtual ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr )${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} = 0; diff --git a/languages/cpp/templates/declarations/clear.h b/languages/cpp/templates/declarations/clear.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations/default.h b/languages/cpp/templates/declarations/default.h new file mode 100644 index 00000000..ba37cb9d --- /dev/null +++ b/languages/cpp/templates/declarations/default.h @@ -0,0 +1,6 @@ + /* + ${method.name} + ${method.description} + ${method.params.annotations}${if.deprecated} * @deprecated ${method.deprecation}${end.if.deprecated} + */ + virtual ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr )${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} = 0; \ No newline at end of file diff --git a/languages/cpp/templates/declarations/event.h b/languages/cpp/templates/declarations/event.h new file mode 100644 index 00000000..29803793 --- /dev/null +++ b/languages/cpp/templates/declarations/event.h @@ -0,0 +1,8 @@ + /* ${method.name} - ${method.description} */ + struct I${method.Name}Notification { + virtual void ${method.rpc.name}( ${event.signature.callback.params}${if.event.callback.params}, ${end.if.event.callback.params}${event.result.type} ) = 0; + }; + // signature callback params: ${event.signature.callback.params} + // method result properties : ${method.result.properties} + virtual void subscribe( ${event.signature.params}${if.event.params}, ${end.if.event.params}I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) = 0; + virtual void unsubscribe( I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) = 0; diff --git a/languages/cpp/templates/declarations/listen.h b/languages/cpp/templates/declarations/listen.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations/once.h b/languages/cpp/templates/declarations/once.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations/polymorphic-pull-event.h b/languages/cpp/templates/declarations/polymorphic-pull-event.h new file mode 100644 index 00000000..cb6e8dee --- /dev/null +++ b/languages/cpp/templates/declarations/polymorphic-pull-event.h @@ -0,0 +1,6 @@ + /* ${method.name} - ${method.description} */ + struct I${method.Name}Notification { + virtual ${method.pulls.type} ${method.rpc.name}( ${method.pulls.param.type} ) = 0; + }; + virtual void subscribe( I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) = 0; + virtual void unsubscribe( I${method.Name}Notification& notification, Firebolt::Error *err = nullptr ) = 0; diff --git a/languages/cpp/templates/declarations/polymorphic-pull.h b/languages/cpp/templates/declarations/polymorphic-pull.h new file mode 100644 index 00000000..fa1890c5 --- /dev/null +++ b/languages/cpp/templates/declarations/polymorphic-pull.h @@ -0,0 +1,6 @@ + /* + ${method.name} + ${method.description} + ${method.params.annotations}${if.deprecated} * @deprecated ${method.deprecation}${end.if.deprecated} + */ + virtual ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr )${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} = 0; diff --git a/languages/cpp/templates/declarations/polymorphic-reducer.h b/languages/cpp/templates/declarations/polymorphic-reducer.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations/property.h b/languages/cpp/templates/declarations/property.h new file mode 100644 index 00000000..503f4698 --- /dev/null +++ b/languages/cpp/templates/declarations/property.h @@ -0,0 +1,5 @@ + /* + * ${method.description} + * ${method.params} + */ + virtual ${method.signature.result} ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr ) const = 0; diff --git a/languages/cpp/templates/declarations/provide.h b/languages/cpp/templates/declarations/provide.h new file mode 100644 index 00000000..0cd03159 --- /dev/null +++ b/languages/cpp/templates/declarations/provide.h @@ -0,0 +1 @@ + virtual void provide( I${info.Title}Provider& provider ) = 0; diff --git a/languages/cpp/templates/declarations/rpc-only.h b/languages/cpp/templates/declarations/rpc-only.h new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/declarations/setter.h b/languages/cpp/templates/declarations/setter.h new file mode 100644 index 00000000..115f39f0 --- /dev/null +++ b/languages/cpp/templates/declarations/setter.h @@ -0,0 +1,5 @@ + /* + ${method.name} + ${method.description} + */ + virtual void ${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err = nullptr ) = 0; diff --git a/languages/cpp/templates/imports/calls-metrics.cpp b/languages/cpp/templates/imports/calls-metrics.cpp new file mode 100644 index 00000000..4ba289b3 --- /dev/null +++ b/languages/cpp/templates/imports/calls-metrics.cpp @@ -0,0 +1 @@ +#include "metrics.h" diff --git a/languages/cpp/templates/imports/calls-metrics.impl b/languages/cpp/templates/imports/calls-metrics.impl new file mode 100644 index 00000000..75fd87c6 --- /dev/null +++ b/languages/cpp/templates/imports/calls-metrics.impl @@ -0,0 +1 @@ +#include "metrics_impl.h" diff --git a/languages/cpp/templates/imports/default.cpp b/languages/cpp/templates/imports/default.cpp new file mode 100644 index 00000000..caf84bc9 --- /dev/null +++ b/languages/cpp/templates/imports/default.cpp @@ -0,0 +1 @@ +#include "jsondata_${info.title.lowercase}.h" diff --git a/languages/cpp/templates/imports/default.h b/languages/cpp/templates/imports/default.h new file mode 100644 index 00000000..56a97a40 --- /dev/null +++ b/languages/cpp/templates/imports/default.h @@ -0,0 +1 @@ +#include "common/${info.title.lowercase}.h" diff --git a/languages/cpp/templates/imports/default.impl b/languages/cpp/templates/imports/default.impl new file mode 100644 index 00000000..caf84bc9 --- /dev/null +++ b/languages/cpp/templates/imports/default.impl @@ -0,0 +1 @@ +#include "jsondata_${info.title.lowercase}.h" diff --git a/languages/cpp/templates/imports/default.jsondata b/languages/cpp/templates/imports/default.jsondata new file mode 100644 index 00000000..caf84bc9 --- /dev/null +++ b/languages/cpp/templates/imports/default.jsondata @@ -0,0 +1 @@ +#include "jsondata_${info.title.lowercase}.h" diff --git a/languages/cpp/templates/interfaces/default.cpp b/languages/cpp/templates/interfaces/default.cpp new file mode 100644 index 00000000..4cba2764 --- /dev/null +++ b/languages/cpp/templates/interfaces/default.cpp @@ -0,0 +1,17 @@ + class ${info.Title}${method.Name}Session : virtual public IProviderSession { + public: + std::string correlationId () const override + { + return _correlationId; + } + + public: + std::string _correlationId; + }; + static void ${info.Title}${method.Name}SessionInnerCallback( void* provider, const void* userData, void* jsonResponse ) + { + //TODO: code to convert jsonResponse to ${method.name} session + I${info.Title}Provider& ${info.title.lowercase}Provider = *(reinterpret_cast(provider)); + ${info.title.lowercase}Provider.${method.name}( parameters, session ); + } + diff --git a/languages/cpp/templates/interfaces/default.h b/languages/cpp/templates/interfaces/default.h new file mode 100644 index 00000000..05ebc463 --- /dev/null +++ b/languages/cpp/templates/interfaces/default.h @@ -0,0 +1 @@ + virtual void ${method.name}( ${method.signature.params}, IProviderSession& session ) = 0; \ No newline at end of file diff --git a/languages/cpp/templates/interfaces/focusable.cpp b/languages/cpp/templates/interfaces/focusable.cpp new file mode 100644 index 00000000..caf7ba46 --- /dev/null +++ b/languages/cpp/templates/interfaces/focusable.cpp @@ -0,0 +1,44 @@ + class ${info.Title}${method.Name}Session : virtual public I${info.Title}Session { + public: + ${info.Title}${method.Name}Session( const std::string& correlationId ) + : _correlationId(correlationId) + { + } + + std::string correlationId() const override + { + return _correlationId; + } + void focus( Firebolt::Error *err = nullptr ) override + { + ProviderFocusSession("${info.title.lowercase}.${method.name}Focus", _correlationId, err); + } + void result( ${provider.xresponse.name} response, Firebolt::Error *err = nullptr ) override + { + ProviderResultSession("${info.title.lowercase}.${method.name}Response", _correlationId, response, err); + } + void error( ${provider.xerror.name} error, Firebolt::Error *err = nullptr ) override + { + ProviderErrorSession("${info.title.lowercase}.${method.name}Error", _correlationId, error, err); + } + + public: + std::string _correlationId; + }; + static void ${info.Title}${method.Name}SessionInnerCallback( void* provider, const void* userData, void* jsonResponse ) + { +${event.callback.serialization} + ASSERT(proxyResponse.IsValid() == true); + + if (proxyResponse.IsValid() == true) { +${event.callback.initialization} + +${event.callback.instantiation} + proxyResponse.Release(); + + std::unique_ptr ${info.title.lowercase}${method.Name}Session = std::make_unique<${info.Title}${method.Name}Session>(${method.result.name}.correlationId); + I${info.Title}Provider& ${info.title.lowercase}Provider = *(reinterpret_cast(provider)); + ${info.title.lowercase}Provider.${method.name}(${method.result.name}.parameters, std::move(${info.title.lowercase}${method.Name}Session)); + } + } + diff --git a/languages/cpp/templates/interfaces/focusable.h b/languages/cpp/templates/interfaces/focusable.h new file mode 100644 index 00000000..a967e096 --- /dev/null +++ b/languages/cpp/templates/interfaces/focusable.h @@ -0,0 +1 @@ + virtual void ${method.name}( ${method.signature.params}, std::unique_ptr session ) = 0; diff --git a/languages/cpp/templates/json-types/additionalProperties.cpp b/languages/cpp/templates/json-types/additionalProperties.cpp new file mode 100644 index 00000000..3466cdce --- /dev/null +++ b/languages/cpp/templates/json-types/additionalProperties.cpp @@ -0,0 +1 @@ + using ${title} = WPEFramework::Core::JSON::VariantContainer; \ No newline at end of file diff --git a/languages/cpp/templates/json-types/anyOf.h b/languages/cpp/templates/json-types/anyOf.h new file mode 100644 index 00000000..fc121f63 --- /dev/null +++ b/languages/cpp/templates/json-types/anyOf.h @@ -0,0 +1 @@ +WPEFramework::Core::JSON::VariantContainer \ No newline at end of file diff --git a/languages/cpp/templates/json-types/anyOfSchemaShape.h b/languages/cpp/templates/json-types/anyOfSchemaShape.h new file mode 100644 index 00000000..ab257971 --- /dev/null +++ b/languages/cpp/templates/json-types/anyOfSchemaShape.h @@ -0,0 +1 @@ + /* anyOf schema shape is not supported right now */ \ No newline at end of file diff --git a/languages/cpp/templates/json-types/array.h b/languages/cpp/templates/json-types/array.h new file mode 100644 index 00000000..4ab0d5ac --- /dev/null +++ b/languages/cpp/templates/json-types/array.h @@ -0,0 +1 @@ +WPEFramework::Core::JSON::ArrayType<${json.type}> \ No newline at end of file diff --git a/languages/cpp/templates/json-types/boolean.h b/languages/cpp/templates/json-types/boolean.h new file mode 100644 index 00000000..18bbeec2 --- /dev/null +++ b/languages/cpp/templates/json-types/boolean.h @@ -0,0 +1 @@ +WPEFramework::Core::JSON::Boolean \ No newline at end of file diff --git a/languages/cpp/templates/json-types/const.h b/languages/cpp/templates/json-types/const.h new file mode 100644 index 00000000..2d60a3c8 --- /dev/null +++ b/languages/cpp/templates/json-types/const.h @@ -0,0 +1 @@ +FireboltSDK::JSON::String \ No newline at end of file diff --git a/languages/cpp/templates/json-types/default.h b/languages/cpp/templates/json-types/default.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/json-types/default.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/json-types/enum.cpp b/languages/cpp/templates/json-types/enum.cpp new file mode 100644 index 00000000..9acb1ea5 --- /dev/null +++ b/languages/cpp/templates/json-types/enum.cpp @@ -0,0 +1 @@ + using JsonData_${type} = WPEFramework::Core::JSON::EnumType<${type}>; diff --git a/languages/cpp/templates/json-types/integer.h b/languages/cpp/templates/json-types/integer.h new file mode 100644 index 00000000..b57fe26e --- /dev/null +++ b/languages/cpp/templates/json-types/integer.h @@ -0,0 +1 @@ +WPEFramework::Core::JSON::DecSInt32 \ No newline at end of file diff --git a/languages/cpp/templates/json-types/namespace.h b/languages/cpp/templates/json-types/namespace.h new file mode 100644 index 00000000..c63c1089 --- /dev/null +++ b/languages/cpp/templates/json-types/namespace.h @@ -0,0 +1 @@ +Firebolt::${info.Title}:: \ No newline at end of file diff --git a/languages/cpp/templates/json-types/null.h b/languages/cpp/templates/json-types/null.h new file mode 100644 index 00000000..fc121f63 --- /dev/null +++ b/languages/cpp/templates/json-types/null.h @@ -0,0 +1 @@ +WPEFramework::Core::JSON::VariantContainer \ No newline at end of file diff --git a/languages/cpp/templates/json-types/number.h b/languages/cpp/templates/json-types/number.h new file mode 100644 index 00000000..c38bca91 --- /dev/null +++ b/languages/cpp/templates/json-types/number.h @@ -0,0 +1 @@ +WPEFramework::Core::JSON::Float \ No newline at end of file diff --git a/languages/cpp/templates/json-types/object-empty-property.h b/languages/cpp/templates/json-types/object-empty-property.h new file mode 100644 index 00000000..94be02b1 --- /dev/null +++ b/languages/cpp/templates/json-types/object-empty-property.h @@ -0,0 +1 @@ + using ${title} = WPEFramework::Core::JSON::VariantContainer; diff --git a/languages/cpp/templates/json-types/object.cpp b/languages/cpp/templates/json-types/object.cpp new file mode 100644 index 00000000..43c667d3 --- /dev/null +++ b/languages/cpp/templates/json-types/object.cpp @@ -0,0 +1,25 @@ + class ${title}: public WPEFramework::Core::JSON::Container { + public: + ~${title}() override = default; + + public: + ${title}() + : WPEFramework::Core::JSON::Container() + { +${properties.register} + } + + ${title}(const ${title}& other) + { +${properties.assign} + } + + ${title}& operator=(const ${title}& other) + { +${properties.assign} + return (*this); + } + + public: +${properties} + }; diff --git a/languages/cpp/templates/json-types/primitive.h b/languages/cpp/templates/json-types/primitive.h new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/json-types/primitive.h @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/json-types/property-assign.cpp b/languages/cpp/templates/json-types/property-assign.cpp new file mode 100644 index 00000000..36bdbf83 --- /dev/null +++ b/languages/cpp/templates/json-types/property-assign.cpp @@ -0,0 +1,2 @@ + Add(_T("${property}"), &${Property}); + ${Property} = other.${Property}; diff --git a/languages/cpp/templates/json-types/property-register.cpp b/languages/cpp/templates/json-types/property-register.cpp new file mode 100644 index 00000000..01e2a7e0 --- /dev/null +++ b/languages/cpp/templates/json-types/property-register.cpp @@ -0,0 +1 @@ + Add(_T("${property}"), &${Property}); \ No newline at end of file diff --git a/languages/cpp/templates/json-types/property.cpp b/languages/cpp/templates/json-types/property.cpp new file mode 100644 index 00000000..49f79c0e --- /dev/null +++ b/languages/cpp/templates/json-types/property.cpp @@ -0,0 +1 @@ + ${title} ${Property}; \ No newline at end of file diff --git a/languages/cpp/templates/json-types/ref.h b/languages/cpp/templates/json-types/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/json-types/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/json-types/string.h b/languages/cpp/templates/json-types/string.h new file mode 100644 index 00000000..2d60a3c8 --- /dev/null +++ b/languages/cpp/templates/json-types/string.h @@ -0,0 +1 @@ +FireboltSDK::JSON::String \ No newline at end of file diff --git a/languages/cpp/templates/json-types/title.h b/languages/cpp/templates/json-types/title.h new file mode 100644 index 00000000..ae1512e5 --- /dev/null +++ b/languages/cpp/templates/json-types/title.h @@ -0,0 +1 @@ +JsonData_${Title} \ No newline at end of file diff --git a/languages/cpp/templates/json-types/tuple.cpp b/languages/cpp/templates/json-types/tuple.cpp new file mode 100644 index 00000000..f734872b --- /dev/null +++ b/languages/cpp/templates/json-types/tuple.cpp @@ -0,0 +1 @@ + using ${title} = WPEFramework::Core::JSON::ArrayType<${json.type}>; \ No newline at end of file diff --git a/languages/cpp/templates/json-types/void.cpp b/languages/cpp/templates/json-types/void.cpp new file mode 100644 index 00000000..fc121f63 --- /dev/null +++ b/languages/cpp/templates/json-types/void.cpp @@ -0,0 +1 @@ +WPEFramework::Core::JSON::VariantContainer \ No newline at end of file diff --git a/languages/cpp/templates/json-types/x-method.h b/languages/cpp/templates/json-types/x-method.h new file mode 100644 index 00000000..573c9f8e --- /dev/null +++ b/languages/cpp/templates/json-types/x-method.h @@ -0,0 +1 @@ +void* \ No newline at end of file diff --git a/languages/cpp/templates/language/enum-item.h b/languages/cpp/templates/language/enum-item.h new file mode 100644 index 00000000..79aabbeb --- /dev/null +++ b/languages/cpp/templates/language/enum-item.h @@ -0,0 +1 @@ + ${key} diff --git a/languages/cpp/templates/language/enum.h b/languages/cpp/templates/language/enum.h new file mode 100644 index 00000000..5056ec3f --- /dev/null +++ b/languages/cpp/templates/language/enum.h @@ -0,0 +1,3 @@ +enum class ${title} { + ${items} +}; diff --git a/languages/cpp/templates/language/parameter.h b/languages/cpp/templates/language/parameter.h new file mode 100644 index 00000000..2ff7a678 --- /dev/null +++ b/languages/cpp/templates/language/parameter.h @@ -0,0 +1 @@ +${type} ${name} \ No newline at end of file diff --git a/languages/cpp/templates/language/schema-item.h b/languages/cpp/templates/language/schema-item.h new file mode 100644 index 00000000..ecded7d5 --- /dev/null +++ b/languages/cpp/templates/language/schema-item.h @@ -0,0 +1 @@ + ${type} ${property}; diff --git a/languages/cpp/templates/language/schema.h b/languages/cpp/templates/language/schema.h new file mode 100644 index 00000000..2e4c66d6 --- /dev/null +++ b/languages/cpp/templates/language/schema.h @@ -0,0 +1,3 @@ +struct ${title} { + ${properties} +}; diff --git a/languages/cpp/templates/methods/allowsFocus.cpp b/languages/cpp/templates/methods/allowsFocus.cpp new file mode 100644 index 00000000..d990ba96 --- /dev/null +++ b/languages/cpp/templates/methods/allowsFocus.cpp @@ -0,0 +1,45 @@ + + /* ${method.name}AsyncResponseInnerCallback */ + static void ${method.name}AsyncResponseInnerCallback(void* notification, void* jsonResponse, Firebolt::Error status) + { + WPEFramework::Core::ProxyType<${method.result.json.type}>& proxyResponse = *(reinterpret_cast*>(jsonResponse)); + + ASSERT(proxyResponse.IsValid() == true); + + if (proxyResponse.IsValid() == true) { + + ${if.result.nonvoid}${method.result.initialization}${end.if.result.nonvoid} + ${method.result.json.type} jsonResult(proxyResponse->Value().c_str()); +${if.result.nonvoid}${method.result.instantiation}${end.if.result.nonvoid} + proxyResponse.Release(); + + I${info.Title}AsyncResponse& notifier = *(reinterpret_cast(notification)); + notifier.response(${method.result.name}, &status); + } + } + + /* ${method.name} - ${method.description} */ + void ${info.Title}Impl::request${method.Name}(${method.signature.params}${if.params}, ${end.if.params}I${info.Title}AsyncResponse& response, Firebolt::Error *err) + { + JsonObject jsonParameters; +${method.params.serialization} + + Firebolt::Error status = FireboltSDK::Async::Instance().Invoke<${method.result.json.type}>(_T("${info.title.lowercase}.${method.name}"), jsonParameters, ${method.name}AsyncResponseInnerCallback, reinterpret_cast(&response)); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully invoked"); + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Async::Invoke err = %d", status); + } + + if (err != nullptr) { + *err = status; + } + return; + } + void ${info.Title}Impl::abort${method.Name}(I${info.Title}AsyncResponse& response, Firebolt::Error *err) + { + Firebolt::Error status = FireboltSDK::Async::Instance().Abort(_T("${info.title.lowercase}.${method.name}"), reinterpret_cast(&response)); + if (err != nullptr) { + *err = status; + } + } diff --git a/languages/cpp/templates/methods/calls-metrics.cpp b/languages/cpp/templates/methods/calls-metrics.cpp new file mode 100644 index 00000000..1b6765c6 --- /dev/null +++ b/languages/cpp/templates/methods/calls-metrics.cpp @@ -0,0 +1,29 @@ + /* ${method.name} - ${method.description} */ + static void ${method.name}Dispatcher(const void* result) { + Metrics::MetricsImpl::${method.name}(${if.result.nonboolean}${if.result.nonvoid}(static_cast<${method.result.json.type}>(const_cast(result)))${end.if.result.nonvoid}${end.if.result.nonboolean}); + } + /* ${method.name} - ${method.description} */ + ${method.signature.result} ${info.Title}Impl::${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err ) ${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} + { + Firebolt::Error status = Firebolt::Error::NotConnected; +${if.result.nonvoid}${method.result.initialization}${end.if.result.nonvoid} + FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); + if (transport != nullptr) { + + JsonObject jsonParameters; + ${method.params.serialization.with.indent} + ${method.result.json.type} jsonResult; + status = transport->Invoke("${info.title.lowercase}.${method.name}", jsonParameters, jsonResult); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully invoked"); + ${if.result.nonvoid}${method.result.instantiation.with.indent}${end.if.result.nonvoid} + WPEFramework::Core::ProxyType job = WPEFramework::Core::ProxyType(WPEFramework::Core::ProxyType::Create(${method.name}Dispatcher, nullptr)); + WPEFramework::Core::IWorkerPool::Instance().Submit(job); + } + + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Transport err = %d", status); + } + + return${if.result.nonvoid} ${method.result.name}${end.if.result.nonvoid}; + } diff --git a/languages/cpp/templates/methods/clear.cpp b/languages/cpp/templates/methods/clear.cpp new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/methods/default.cpp b/languages/cpp/templates/methods/default.cpp new file mode 100644 index 00000000..0b5b31bf --- /dev/null +++ b/languages/cpp/templates/methods/default.cpp @@ -0,0 +1,26 @@ + /* ${method.name} - ${method.description} */ + ${method.signature.result} ${info.Title}Impl::${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err ) ${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} + { + Firebolt::Error status = Firebolt::Error::NotConnected; +${if.result.nonvoid}${method.result.initialization}${end.if.result.nonvoid} + FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); + if (transport != nullptr) { + + JsonObject jsonParameters; + ${method.params.serialization.with.indent} + ${method.result.json.type} jsonResult; + status = transport->Invoke("${info.title.lowercase}.${method.name}", jsonParameters, jsonResult); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully invoked"); + ${if.result.nonvoid}${method.result.instantiation.with.indent}${end.if.result.nonvoid} + } + + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Transport err = %d", status); + } + if (err != nullptr) { + *err = status; + } + + return${if.result.nonvoid} ${method.result.name}${end.if.result.nonvoid}; + } diff --git a/languages/cpp/templates/methods/event.cpp b/languages/cpp/templates/methods/event.cpp new file mode 100644 index 00000000..ec3ba93f --- /dev/null +++ b/languages/cpp/templates/methods/event.cpp @@ -0,0 +1,37 @@ + /* ${method.name} - ${method.description} */ + static void ${method.name}InnerCallback( void* notification, const void* userData, void* jsonResponse ) + { +${event.callback.serialization} + ASSERT(proxyResponse.IsValid() == true); + + if (proxyResponse.IsValid() == true) { +${event.callback.initialization} + +${event.callback.instantiation} + proxyResponse.Release(); + + I${info.Title}::I${method.Name}Notification& notifier = *(reinterpret_cast(notification)); + notifier.${method.rpc.name}(${event.callback.response.instantiation}); + } + } + void ${info.Title}Impl::subscribe( ${event.signature.params}${if.event.params}, ${end.if.event.params}I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err ) + { + const string eventName = _T("${info.title.lowercase}.${method.rpc.name}"); + Firebolt::Error status = Firebolt::Error::None; + + JsonObject jsonParameters; +${event.params.serialization} + status = FireboltSDK::Event::Instance().Subscribe<${event.result.json.type}>(eventName, jsonParameters, ${method.name}InnerCallback, reinterpret_cast(¬ification), nullptr); + + if (err != nullptr) { + *err = status; + } + } + void ${info.Title}Impl::unsubscribe( I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err ) + { + Firebolt::Error status = FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title.lowercase}.${method.rpc.name}"), reinterpret_cast(¬ification)); + + if (err != nullptr) { + *err = status; + } + } diff --git a/languages/cpp/templates/methods/listen.cpp b/languages/cpp/templates/methods/listen.cpp new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/methods/once.cpp b/languages/cpp/templates/methods/once.cpp new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/methods/polymorphic-pull-event.cpp b/languages/cpp/templates/methods/polymorphic-pull-event.cpp new file mode 100644 index 00000000..43a33df5 --- /dev/null +++ b/languages/cpp/templates/methods/polymorphic-pull-event.cpp @@ -0,0 +1,61 @@ + /* ${method.name} - ${method.description} */ + static void ${method.name}InnerCallback( void* notification, const void* userData, void* jsonResponse ) + { +${event.callback.serialization} + ASSERT(proxyResponse.IsValid() == true); + + if (proxyResponse.IsValid() == true) { + ${method.pulls.param.json.type} jsonResult = proxyResponse->Parameters; + ${method.pulls.response.initialization} +${method.pulls.response.instantiation} + + I${info.Title}::I${method.Name}Notification& notifier = *(reinterpret_cast(notification)); + ${method.pulls.type} element = notifier.${method.rpc.name}(${method.pulls.param.title}); + Firebolt::Error status = Firebolt::Error::NotConnected; + FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); + if (transport != nullptr) { + JsonObject jsonParameters; + WPEFramework::Core::JSON::Variant CorrelationId = proxyResponse->CorrelationId.Value(); + jsonParameters.Set(_T("correlationId"), CorrelationId); + ${method.pulls.json.type} ${method.pulls.result.title}Container; + { + ${method.pulls.result.serialization.with.indent} + } + string resultStr; + ${method.pulls.result.title}Container.ToString(resultStr); + WPEFramework::Core::JSON::VariantContainer resultContainer(resultStr); + WPEFramework::Core::JSON::Variant Result = resultContainer; + jsonParameters.Set(_T("result"), Result); + WPEFramework::Core::JSON::Boolean jsonResult; + + status = transport->Invoke("${info.title.lowercase}.${method.pulls.for}", jsonParameters, jsonResult); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully pushed with status as %d", jsonResult.Value()); + } + + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Transport err = %d", status); + } + proxyResponse.Release(); + } + } + void ${info.Title}Impl::subscribe( I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err ) + { + const string eventName = _T("${info.title.lowercase}.${method.rpc.name}"); + Firebolt::Error status = Firebolt::Error::None; + + JsonObject jsonParameters; + status = FireboltSDK::Event::Instance().Subscribe<${event.result.json.type}>(eventName, jsonParameters, ${method.name}InnerCallback, reinterpret_cast(¬ification), nullptr); + + if (err != nullptr) { + *err = status; + } + } + void ${info.Title}Impl::unsubscribe( I${info.Title}::I${method.Name}Notification& notification, Firebolt::Error *err ) + { + Firebolt::Error status = FireboltSDK::Event::Instance().Unsubscribe(_T("${info.title.lowercase}.${method.rpc.name}"), reinterpret_cast(¬ification)); + + if (err != nullptr) { + *err = status; + } + } diff --git a/languages/cpp/templates/methods/polymorphic-pull.cpp b/languages/cpp/templates/methods/polymorphic-pull.cpp new file mode 100644 index 00000000..1146f2d1 --- /dev/null +++ b/languages/cpp/templates/methods/polymorphic-pull.cpp @@ -0,0 +1,26 @@ + /* ${method.name} - ${method.description} */ + ${method.signature.result} ${info.Title}Impl::${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err ) ${if.result.nonvoid}${if.params.empty} const${end.if.params.empty}${end.if.result.nonvoid} + { + Firebolt::Error status = Firebolt::Error::NotConnected; +${if.result.nonvoid}${method.result.initialization}${end.if.result.nonvoid} + FireboltSDK::Transport* transport = FireboltSDK::Accessor::Instance().GetTransport(); + if (transport != nullptr) { + string correlationId = ""; + JsonObject jsonParameters; + ${method.params.serialization.with.indent} + ${method.result.json.type} jsonResult; + status = transport->Invoke("${info.title.lowercase}.${method.name}", jsonParameters, jsonResult); + if (status == Firebolt::Error::None) { + FIREBOLT_LOG_INFO(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "${info.Title}.${method.name} is successfully invoked"); + ${if.result.nonvoid}${method.result.instantiation.with.indent}${end.if.result.nonvoid} + } + + } else { + FIREBOLT_LOG_ERROR(FireboltSDK::Logger::Category::OpenRPC, FireboltSDK::Logger::Module(), "Error in getting Transport err = %d", status); + } + if (err != nullptr) { + *err = status; + } + + return${if.result.nonvoid} ${method.result.name}${end.if.result.nonvoid}; + } diff --git a/languages/cpp/templates/methods/polymorphic-reducer.cpp b/languages/cpp/templates/methods/polymorphic-reducer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/methods/property.cpp b/languages/cpp/templates/methods/property.cpp new file mode 100644 index 00000000..ce38ebd0 --- /dev/null +++ b/languages/cpp/templates/methods/property.cpp @@ -0,0 +1,19 @@ + /* ${method.name} - ${method.description} */ + ${method.signature.result} ${info.Title}Impl::${method.name}( ${method.signature.params}${if.params}, ${end.if.params}Firebolt::Error *err ) const + { + const string method = _T("${info.title.lowercase}.${method.name}"); + ${if.params}JsonObject jsonParameters;${end.if.params} + ${if.params}${method.params.serialization}${end.if.params} + ${method.result.json} jsonResult; +${method.result.initialization} + ${if.params}Firebolt::Error status = FireboltSDK::Properties::Get(method, jsonParameters, jsonResult);${end.if.params} + ${if.params.empty}Firebolt::Error status = FireboltSDK::Properties::Get(method, jsonResult);${end.if.params.empty} + if (status == Firebolt::Error::None) { +${method.result.instantiation} + } + if (err != nullptr) { + *err = status; + } + + return ${method.result.name}; + }${method.setter} diff --git a/languages/cpp/templates/methods/provide.cpp b/languages/cpp/templates/methods/provide.cpp new file mode 100644 index 00000000..85492d38 --- /dev/null +++ b/languages/cpp/templates/methods/provide.cpp @@ -0,0 +1,8 @@ + void ${info.Title}Impl::provide( I${info.Title}Provider& provider ) + { + string eventName; + Firebolt::Error status = Firebolt::Error::None; + JsonObject jsonParameters; + + /* ${PROVIDERS_SUBSCRIBE} */ + } diff --git a/languages/cpp/templates/methods/rpc-only.cpp b/languages/cpp/templates/methods/rpc-only.cpp new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/methods/setter.cpp b/languages/cpp/templates/methods/setter.cpp new file mode 100644 index 00000000..e69de29b diff --git a/languages/cpp/templates/modules/include/module.h b/languages/cpp/templates/modules/include/module.h new file mode 100644 index 00000000..ff3b1080 --- /dev/null +++ b/languages/cpp/templates/modules/include/module.h @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "error.h" +/* ${IMPORTS} */ + +${if.declarations}namespace Firebolt { +namespace ${info.Title} { +${if.enums} + +// Enums +/* ${ENUMS} */${end.if.enums} +${if.types} +// Types +/* ${TYPES} */${end.if.types} +${if.providers}/* ${PROVIDERS} */${end.if.providers}${if.xuses}/* ${XUSES} */${end.if.xuses} +${if.methods}struct I${info.Title} { + + virtual ~I${info.Title}() = default; + + // Methods & Events + /* ${METHODS:declarations} */ +};${end.if.methods} + +} //namespace ${info.Title} +}${end.if.declarations} diff --git a/languages/cpp/templates/modules/src/module_impl.cpp b/languages/cpp/templates/modules/src/module_impl.cpp new file mode 100644 index 00000000..abdbecd6 --- /dev/null +++ b/languages/cpp/templates/modules/src/module_impl.cpp @@ -0,0 +1,39 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "${info.title.lowercase}_impl.h" + +${if.implementations} +namespace Firebolt { +namespace ${info.Title} { +${if.providers} +/* ${PROVIDERS} */${end.if.providers} + // Methods + /* ${METHODS} */ + + // Events + /* ${EVENTS} */ + +}//namespace ${info.Title} +}${end.if.implementations} +${if.enums} + +namespace WPEFramework { + +/* ${ENUMS} */ +}${end.if.enums} diff --git a/languages/cpp/templates/modules/src/module_impl.h b/languages/cpp/templates/modules/src/module_impl.h new file mode 100644 index 00000000..ce2837f4 --- /dev/null +++ b/languages/cpp/templates/modules/src/module_impl.h @@ -0,0 +1,49 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "FireboltSDK.h" +#include "IModule.h" +/* ${IMPORTS} */ +#include "${info.title.lowercase}.h" + +${if.implementations} +namespace Firebolt { +namespace ${info.Title} { +${if.enums} + +/* ${ENUMS:json-types} */${end.if.enums} +${if.types} + // Types +/* ${TYPES:json-types} */${end.if.types} + ${if.methods}class ${info.Title}Impl : public I${info.Title}, public IModule { + + public: + ${info.Title}Impl() = default; + ${info.Title}Impl(const ${info.Title}Impl&) = delete; + ${info.Title}Impl& operator=(const ${info.Title}Impl&) = delete; + + ~${info.Title}Impl() override = default; + + // Methods & Events + /* ${METHODS:declarations-override} */ + };${end.if.methods} + +}//namespace ${info.Title} +}${end.if.implementations} diff --git a/languages/cpp/templates/parameter-serialization/additionalProperties.cpp b/languages/cpp/templates/parameter-serialization/additionalProperties.cpp new file mode 100644 index 00000000..eea6f6a7 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/additionalProperties.cpp @@ -0,0 +1,12 @@ + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}${title} map; + ${if.impl.optional}if (${property}.has_value()) { + map = ${property}.value(); + }${end.if.impl.optional}${if.impl.non.optional}map = ${property};${end.if.impl.non.optional} + WPEFramework::Core::JSON::Variant ${property}Variant; + for (auto element: map) { + WPEFramework::Core::JSON::Variant jsonElement = element.second; + WPEFramework::Core::JSON::VariantContainer jsonContainer; + jsonContainer.Set(element.first.c_str(), jsonElement); + ${property}Variant = jsonContainer; + } + jsonParameters.Set(_T("${property}"), ${property}Variant); diff --git a/languages/cpp/templates/parameter-serialization/array.cpp b/languages/cpp/templates/parameter-serialization/array.cpp new file mode 100644 index 00000000..f623711b --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/array.cpp @@ -0,0 +1,11 @@ + WPEFramework::Core::JSON::ArrayType ${property}Array; + ${if.impl.array.optional}if (${property}.has_value()) { + for (auto& element : ${property}.value()) { + ${if.object}${items.with.indent}${end.if.object}${if.non.object} ${property}Array.Add() = element;${end.if.non.object} + } + }${end.if.impl.array.optional}${if.impl.array.non.optional}for (auto& element : ${property}) { +${if.object}${items.with.indent}${end.if.object}${if.non.object} ${property}Array.Add() = element;${end.if.non.object} + }${end.if.impl.array.non.optional} + WPEFramework::Core::JSON::Variant ${property}Variant; + ${property}Variant.Array(${property}Array); + jsonParameters.Set(_T("${property}"), ${property}Variant); \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/enum.cpp b/languages/cpp/templates/parameter-serialization/enum.cpp new file mode 100644 index 00000000..478e1d8e --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/enum.cpp @@ -0,0 +1,7 @@ + ${if.optional}if (${property}.has_value()) { + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} jsonValue = ${property}.value(); + WPEFramework::Core::JSON::Variant ${property}Variant(jsonValue.Data()); + jsonParameters.Set(_T("${property}"), ${property}Variant); + }${end.if.optional}${if.non.optional}${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} jsonValue = ${property}; + WPEFramework::Core::JSON::Variant ${property}Variant(jsonValue.Data()); + jsonParameters.Set(_T("${property}"), ${property}Variant);${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/generic.cpp b/languages/cpp/templates/parameter-serialization/generic.cpp new file mode 100644 index 00000000..69893434 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/generic.cpp @@ -0,0 +1,5 @@ + ${if.optional}if (${property}.has_value()) { + WPEFramework::Core::JSON::Variant ${property}Variant(${property}.value()); + jsonParameters.Set(_T("${property}"), ${property}Variant); + }${end.if.optional}${if.non.optional}WPEFramework::Core::JSON::Variant ${property}Variant(${property}); + jsonParameters.Set(_T("${property}"), ${property}Variant);${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/namespace.cpp b/languages/cpp/templates/parameter-serialization/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/object-array.cpp b/languages/cpp/templates/parameter-serialization/object-array.cpp new file mode 100644 index 00000000..a8426a59 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/object-array.cpp @@ -0,0 +1,9 @@ + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} ${property}Container; + { +${properties} + } + string ${property}Str; + ${property}Container.ToString(${property}Str); + WPEFramework::Core::JSON::VariantContainer ${property}VariantContainer(${property}Str); + WPEFramework::Core::JSON::Variant ${property}Variant = ${property}VariantContainer; + ${property}Array.Add() = ${property}Variant; \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/object-empty-property.cpp b/languages/cpp/templates/parameter-serialization/object-empty-property.cpp new file mode 100644 index 00000000..69893434 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/object-empty-property.cpp @@ -0,0 +1,5 @@ + ${if.optional}if (${property}.has_value()) { + WPEFramework::Core::JSON::Variant ${property}Variant(${property}.value()); + jsonParameters.Set(_T("${property}"), ${property}Variant); + }${end.if.optional}${if.non.optional}WPEFramework::Core::JSON::Variant ${property}Variant(${property}); + jsonParameters.Set(_T("${property}"), ${property}Variant);${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/object.cpp b/languages/cpp/templates/parameter-serialization/object.cpp new file mode 100644 index 00000000..877b0d13 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/object.cpp @@ -0,0 +1,19 @@ + ${if.impl.optional}if (${property}.has_value()) { + auto element = ${property}.value(); + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} ${property}Container; +${properties} + string ${property}Str; + ${property}Container.ToString(${property}Str); + WPEFramework::Core::JSON::VariantContainer ${property}VariantContainer(${property}Str); + WPEFramework::Core::JSON::Variant ${property}Variant = ${property}VariantContainer; + jsonParameters.Set(_T("${property}"), ${property}Variant); + }${end.if.impl.optional}${if.impl.non.optional}auto element = ${property}; + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} ${property}Container; + { +${properties} + } + string ${property}Str; + ${property}Container.ToString(${property}Str); + WPEFramework::Core::JSON::VariantContainer ${property}VariantContainer(${property}Str); + WPEFramework::Core::JSON::Variant ${property}Variant = ${property}VariantContainer; + jsonParameters.Set(_T("${property}"), ${property}Variant);${end.if.impl.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/patternProperties.cpp b/languages/cpp/templates/parameter-serialization/patternProperties.cpp new file mode 100644 index 00000000..9346bb6b --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/patternProperties.cpp @@ -0,0 +1,9 @@ + ${if.impl.optional}if (${property}.has_value()) { + WPEFramework::Core::JSON::VariantContainer ${property}Container; + ${property}Container.FromString(${property}.value()); + WPEFramework::Core::JSON::Variant ${property}Variant(${property}Container); + jsonParameters.Set(_T("${property}"), ${property}Variant); + }${end.if.impl.optional}${if.impl.non.optional}WPEFramework::Core::JSON::VariantContainer ${property}Container; + ${property}Container.FromString(${property}); + WPEFramework::Core::JSON::Variant ${property}Variant(${property}Container); + jsonParameters.Set(_T("${property}"), ${property}Variant);${end.if.impl.non.optional} diff --git a/languages/cpp/templates/parameter-serialization/primitive.cpp b/languages/cpp/templates/parameter-serialization/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/property.cpp b/languages/cpp/templates/parameter-serialization/property.cpp new file mode 100644 index 00000000..d6bddfdb --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/property.cpp @@ -0,0 +1,3 @@ +${shape} ${if.non.const}${if.non.anyOf}${if.non.array}${if.non.object}${if.optional}if (element.${property}.has_value()) { + ${base.title}Container.${Property} = element.${property}.value(); + }${end.if.optional}${if.non.optional}${base.title}Container.${Property} = element.${property};${end.if.non.optional}${end.if.non.object}${end.if.non.array}${end.if.non.anyOf}${end.if.non.const} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/ref.h b/languages/cpp/templates/parameter-serialization/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/anyOf.cpp b/languages/cpp/templates/parameter-serialization/sub-property/anyOf.cpp new file mode 100644 index 00000000..e2e80c7c --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/anyOf.cpp @@ -0,0 +1 @@ +${Title} diff --git a/languages/cpp/templates/parameter-serialization/sub-property/anyOfSchemaShape.cpp b/languages/cpp/templates/parameter-serialization/sub-property/anyOfSchemaShape.cpp new file mode 100644 index 00000000..5e750f10 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/anyOfSchemaShape.cpp @@ -0,0 +1,3 @@ + ${if.optional}if (element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.has_value()) { + ${base.title}Container${Property.dependency}.${Property} = element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.value(); + }${end.if.optional}${if.non.optional}${base.title}Container${Property.dependency}.${Property} = element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property};${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/array.cpp b/languages/cpp/templates/parameter-serialization/sub-property/array.cpp new file mode 100644 index 00000000..331d59a8 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/array.cpp @@ -0,0 +1,13 @@ + ${if.impl.array.optional}if (element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.has_value()) { + WPEFramework::Core::JSON::ArrayType<${json.type}> ${property}Array; + ${type} ${property} = element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.value(); + for (auto& element : ${property}) { + ${if.object}${items.with.indent}${end.if.object}${if.non.object} ${property}Array.Add() = element;${end.if.non.object} + } + ${base.title}Container${Property.dependency}.${Property} = ${property}Array; + }${end.if.impl.array.optional}${if.impl.array.non.optional}WPEFramework::Core::JSON::ArrayType<${json.type}> ${property}Array; + ${type} ${property} = element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}; + for (auto& element : ${property}) { +${if.object}${items.with.indent}${end.if.object}${if.non.object} ${property}Array.Add() = element;${end.if.non.object} + } + ${base.title}Container${Property.dependency}.${Property} = ${property}Array;${end.if.impl.array.non.optional} diff --git a/languages/cpp/templates/parameter-serialization/sub-property/const.cpp b/languages/cpp/templates/parameter-serialization/sub-property/const.cpp new file mode 100644 index 00000000..0d25c572 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/const.cpp @@ -0,0 +1,3 @@ + ${if.optional}if (element${property.dependency}.${property}.has_value()) { + ${base.title}Container${Property.dependency}.${Property} = element${property.dependency}.${property}.value(); + }${end.if.optional}${if.non.optional}${base.title}Container${Property.dependency}.${Property} = element${property.dependency}.${property};${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/namespace.cpp b/languages/cpp/templates/parameter-serialization/sub-property/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/object-array.cpp b/languages/cpp/templates/parameter-serialization/sub-property/object-array.cpp new file mode 100644 index 00000000..11c5b76b --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/object-array.cpp @@ -0,0 +1,5 @@ + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} ${property}Container; + { +${properties} + } + ${property}Array.Add() = ${property}Container; \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/object-separator.cpp b/languages/cpp/templates/parameter-serialization/sub-property/object-separator.cpp new file mode 100644 index 00000000..945c9b46 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/object-separator.cpp @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/object.cpp b/languages/cpp/templates/parameter-serialization/sub-property/object.cpp new file mode 100644 index 00000000..086f7169 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/object.cpp @@ -0,0 +1,3 @@ + { +${properties} + } \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/property.cpp b/languages/cpp/templates/parameter-serialization/sub-property/property.cpp new file mode 100644 index 00000000..e52679d3 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/property.cpp @@ -0,0 +1,3 @@ +${shape} ${if.non.const}${if.non.anyOf}${if.non.array}${if.non.object}${if.optional}if (element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.has_value()) { + ${base.title}Container${Property.dependency}.${Property} = element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property}.value(); + }${end.if.optional}${if.non.optional}${base.title}Container${Property.dependency}.${Property} = element${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property};${end.if.non.optional}${end.if.non.object}${end.if.non.array}${end.if.non.anyOf}${end.if.non.const} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/ref.h b/languages/cpp/templates/parameter-serialization/sub-property/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/sub-property/title.cpp b/languages/cpp/templates/parameter-serialization/sub-property/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/sub-property/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/parameter-serialization/title.cpp b/languages/cpp/templates/parameter-serialization/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/parameter-serialization/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/parameters/default.h b/languages/cpp/templates/parameters/default.h new file mode 100644 index 00000000..f4fc002f --- /dev/null +++ b/languages/cpp/templates/parameters/default.h @@ -0,0 +1 @@ +const ${method.param.type} ${method.param.name} \ No newline at end of file diff --git a/languages/cpp/templates/parameters/json.cpp b/languages/cpp/templates/parameters/json.cpp new file mode 100644 index 00000000..5ee36bec --- /dev/null +++ b/languages/cpp/templates/parameters/json.cpp @@ -0,0 +1,3 @@ + ${json.param.type} ${method.param.Name} = ${method.param.name}; + jsonParameters.Add("_T(${method.param.name})", &${method.param.Name}); + diff --git a/languages/cpp/templates/parameters/nonprimitive.h b/languages/cpp/templates/parameters/nonprimitive.h new file mode 100644 index 00000000..5f48625c --- /dev/null +++ b/languages/cpp/templates/parameters/nonprimitive.h @@ -0,0 +1 @@ +const ${method.param.type}& ${method.param.name} \ No newline at end of file diff --git a/languages/cpp/templates/parameters/optional.h b/languages/cpp/templates/parameters/optional.h new file mode 100644 index 00000000..d21cf868 --- /dev/null +++ b/languages/cpp/templates/parameters/optional.h @@ -0,0 +1 @@ +const std::optional<${method.param.type}>& ${method.param.name} \ No newline at end of file diff --git a/languages/cpp/templates/parameters/string.h b/languages/cpp/templates/parameters/string.h new file mode 100644 index 00000000..5f48625c --- /dev/null +++ b/languages/cpp/templates/parameters/string.h @@ -0,0 +1 @@ +const ${method.param.type}& ${method.param.name} \ No newline at end of file diff --git a/languages/cpp/templates/result-callback/default.h b/languages/cpp/templates/result-callback/default.h new file mode 100644 index 00000000..d66fdb8a --- /dev/null +++ b/languages/cpp/templates/result-callback/default.h @@ -0,0 +1 @@ +const ${method.result.type} \ No newline at end of file diff --git a/languages/cpp/templates/result-callback/nonprimitive.h b/languages/cpp/templates/result-callback/nonprimitive.h new file mode 100644 index 00000000..84b86816 --- /dev/null +++ b/languages/cpp/templates/result-callback/nonprimitive.h @@ -0,0 +1 @@ +const ${method.result.type}& \ No newline at end of file diff --git a/languages/cpp/templates/result-callback/string.h b/languages/cpp/templates/result-callback/string.h new file mode 100644 index 00000000..84b86816 --- /dev/null +++ b/languages/cpp/templates/result-callback/string.h @@ -0,0 +1 @@ +const ${method.result.type}& \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/additionalProperties.cpp b/languages/cpp/templates/result-initialization/additionalProperties.cpp new file mode 100644 index 00000000..6f7f63f0 --- /dev/null +++ b/languages/cpp/templates/result-initialization/additionalProperties.cpp @@ -0,0 +1 @@ + ${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame}${Title} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/anyOf.cpp b/languages/cpp/templates/result-initialization/anyOf.cpp new file mode 100644 index 00000000..6a698651 --- /dev/null +++ b/languages/cpp/templates/result-initialization/anyOf.cpp @@ -0,0 +1 @@ + std::string ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/array.cpp b/languages/cpp/templates/result-initialization/array.cpp new file mode 100644 index 00000000..1bcce663 --- /dev/null +++ b/languages/cpp/templates/result-initialization/array.cpp @@ -0,0 +1 @@ + ${type} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/boolean.cpp b/languages/cpp/templates/result-initialization/boolean.cpp new file mode 100644 index 00000000..23bbf1be --- /dev/null +++ b/languages/cpp/templates/result-initialization/boolean.cpp @@ -0,0 +1 @@ + ${type} ${property} = false; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/generic.cpp b/languages/cpp/templates/result-initialization/generic.cpp new file mode 100644 index 00000000..1bcce663 --- /dev/null +++ b/languages/cpp/templates/result-initialization/generic.cpp @@ -0,0 +1 @@ + ${type} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/namespace.cpp b/languages/cpp/templates/result-initialization/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/result-initialization/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/number.cpp b/languages/cpp/templates/result-initialization/number.cpp new file mode 100644 index 00000000..6e34ae51 --- /dev/null +++ b/languages/cpp/templates/result-initialization/number.cpp @@ -0,0 +1 @@ + ${type} ${property} = 0; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/object-empty-property.cpp b/languages/cpp/templates/result-initialization/object-empty-property.cpp new file mode 100644 index 00000000..6f7f63f0 --- /dev/null +++ b/languages/cpp/templates/result-initialization/object-empty-property.cpp @@ -0,0 +1 @@ + ${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame}${Title} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/primitive.cpp b/languages/cpp/templates/result-initialization/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/result-initialization/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/ref.h b/languages/cpp/templates/result-initialization/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/result-initialization/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/string.cpp b/languages/cpp/templates/result-initialization/string.cpp new file mode 100644 index 00000000..1bcce663 --- /dev/null +++ b/languages/cpp/templates/result-initialization/string.cpp @@ -0,0 +1 @@ + ${type} ${property}; \ No newline at end of file diff --git a/languages/cpp/templates/result-initialization/title.cpp b/languages/cpp/templates/result-initialization/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/result-initialization/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/additionalProperties.cpp b/languages/cpp/templates/result-instantiation/additionalProperties.cpp new file mode 100644 index 00000000..2d5f03d0 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/additionalProperties.cpp @@ -0,0 +1,7 @@ + WPEFramework::Core::JSON::VariantContainer::Iterator variants = jsonResult.Variants(); + while (variants.Next()) { +${if.not.default} ${namespace}${key} key = WPEFramework::Core::EnumerateType<${namespace}${key}>(variants.Label(), false).Value();${end.if.not.default}${if.default} ${key} key = variants.Label();${end.if.default} + ${property}.emplace(std::piecewise_construct, + std::forward_as_tuple(key), + std::forward_as_tuple(variants.Current().${additional.type})); + } diff --git a/languages/cpp/templates/result-instantiation/anyOf.cpp b/languages/cpp/templates/result-instantiation/anyOf.cpp new file mode 100644 index 00000000..8ca96768 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/anyOf.cpp @@ -0,0 +1,3 @@ + std::string resultStr; + jsonResult.ToString(resultStr); + ${property} = resultStr; \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/array.cpp b/languages/cpp/templates/result-instantiation/array.cpp new file mode 100644 index 00000000..0e2f01ef --- /dev/null +++ b/languages/cpp/templates/result-instantiation/array.cpp @@ -0,0 +1,4 @@ + auto index(jsonResult.Elements()); + while (index.Next() == true) { + ${if.object}${items}${end.if.object}${if.non.object} ${property}.push_back(index.Current().Value());${end.if.non.object} + } \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/generic.cpp b/languages/cpp/templates/result-instantiation/generic.cpp new file mode 100644 index 00000000..8949a5fb --- /dev/null +++ b/languages/cpp/templates/result-instantiation/generic.cpp @@ -0,0 +1 @@ + ${property} = jsonResult.Value(); \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/namespace.cpp b/languages/cpp/templates/result-instantiation/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/result-instantiation/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/object-array.cpp b/languages/cpp/templates/result-instantiation/object-array.cpp new file mode 100644 index 00000000..f992be35 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/object-array.cpp @@ -0,0 +1,4 @@ + ${type} ${property}Result${level}; + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} jsonResult = index.Current(); +${properties} + ${property}.push_back(${property}Result${level}); \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/object-empty-property.cpp b/languages/cpp/templates/result-instantiation/object-empty-property.cpp new file mode 100644 index 00000000..8ca96768 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/object-empty-property.cpp @@ -0,0 +1,3 @@ + std::string resultStr; + jsonResult.ToString(resultStr); + ${property} = resultStr; \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/object.cpp b/languages/cpp/templates/result-instantiation/object.cpp new file mode 100644 index 00000000..5119c58b --- /dev/null +++ b/languages/cpp/templates/result-instantiation/object.cpp @@ -0,0 +1,3 @@ + ${type} ${property}Result${level}; +${properties} + ${property} = ${property}Result${level}; \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/primitive.cpp b/languages/cpp/templates/result-instantiation/primitive.cpp new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/primitive.cpp @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/property.cpp b/languages/cpp/templates/result-instantiation/property.cpp new file mode 100644 index 00000000..5d3c2db8 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/property.cpp @@ -0,0 +1,3 @@ +${shape} ${if.non.anyOf}${if.non.array}${if.non.object}${if.optional}if (jsonResult.${Property}.IsSet()) { + ${base.title}Result${level}.${property} = jsonResult.${Property}.Value(); + }${end.if.optional}${if.non.optional}${base.title}Result${level}.${property} = jsonResult.${Property}.Value();${end.if.non.optional}${end.if.non.object}${end.if.non.array}${end.if.non.anyOf} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/ref.h b/languages/cpp/templates/result-instantiation/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/string.cpp b/languages/cpp/templates/result-instantiation/string.cpp new file mode 100644 index 00000000..886728a4 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/string.cpp @@ -0,0 +1 @@ + ${property} = jsonResult.Value().c_str(); \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/sub-property/anyOf.cpp b/languages/cpp/templates/result-instantiation/sub-property/anyOf.cpp new file mode 100644 index 00000000..e2e80c7c --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/anyOf.cpp @@ -0,0 +1 @@ +${Title} diff --git a/languages/cpp/templates/result-instantiation/sub-property/anyOfSchemaShape.cpp b/languages/cpp/templates/result-instantiation/sub-property/anyOfSchemaShape.cpp new file mode 100644 index 00000000..4398ae8b --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/anyOfSchemaShape.cpp @@ -0,0 +1,7 @@ + ${if.optional}if (jsonResult${Property.dependency}.${Property}.IsSet()) { + string ${property}Str; + jsonResult${Property.dependency}.${Property}.ToString(${property}Str); + ${base.title}Result${level}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = ${property}Str; + }${end.if.optional}${if.non.optional}string ${property}Str; + jsonResult${Property.dependency}.${Property}.ToString(${property}Str); + ${base.title}Result${level}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = ${property}Str;${end.if.non.optional} diff --git a/languages/cpp/templates/result-instantiation/sub-property/array.cpp b/languages/cpp/templates/result-instantiation/sub-property/array.cpp new file mode 100644 index 00000000..a9badd14 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/array.cpp @@ -0,0 +1,10 @@ + ${if.optional}if (jsonResult.${Property}.IsSet()) { + ${base.title}Result${level}.${property} = std::make_optional<${type}>(); + auto index(jsonResult.${Property}.Elements()); + while (index.Next() == true) { + ${if.object}${items.with.indent}${end.if.object}${if.non.object} ${base.title}Result${level}.${property}.value().push_back(index.Current().Value());${end.if.non.object} + } + }${end.if.optional}${if.non.optional}auto index(jsonResult.${Property}.Elements()); + while (index.Next() == true) { + ${if.object}${items.with.indent}${end.if.object}${if.non.object} ${base.title}Result${level}.${property}.push_back(index.Current().Value());${end.if.non.object} + }${end.if.non.optional} diff --git a/languages/cpp/templates/result-instantiation/sub-property/namespace.cpp b/languages/cpp/templates/result-instantiation/sub-property/namespace.cpp new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/namespace.cpp @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/sub-property/object-array.cpp b/languages/cpp/templates/result-instantiation/sub-property/object-array.cpp new file mode 100644 index 00000000..1274a0da --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/object-array.cpp @@ -0,0 +1,6 @@ + ${type} ${property}Result${level}; + ${if.namespace.notsame}Firebolt::${info.Title}::${end.if.namespace.notsame}JsonData_${title} jsonResult = index.Current(); + { +${properties} + } + ${property}Result.${property}${if.impl.array.optional}.value()${end.if.impl.array.optional}.push_back(${property}Result${level}); \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/sub-property/object-separator.cpp b/languages/cpp/templates/result-instantiation/sub-property/object-separator.cpp new file mode 100644 index 00000000..945c9b46 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/object-separator.cpp @@ -0,0 +1 @@ +. \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/sub-property/object.cpp b/languages/cpp/templates/result-instantiation/sub-property/object.cpp new file mode 100644 index 00000000..4d789f75 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/object.cpp @@ -0,0 +1,6 @@ +${if.optional} if (jsonResult${Property.dependency}.IsSet()) { + ${base.title}Result${level}${property.dependency} = std::make_optional<${type}>(); +${properties} + }${end.if.optional}${if.non.optional} { +${properties} + }${end.if.non.optional} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/sub-property/property.cpp b/languages/cpp/templates/result-instantiation/sub-property/property.cpp new file mode 100644 index 00000000..98f5fbb4 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/property.cpp @@ -0,0 +1,3 @@ +${shape} ${if.non.anyOf}${if.non.array}${if.non.object}${if.optional}if (jsonResult${Property.dependency}.${Property}.IsSet()) { + ${base.title}Result${level}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = jsonResult${Property.dependency}.${Property}; + }${end.if.optional}${if.non.optional}${base.title}Result${level}${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}.${property} = jsonResult${Property.dependency}.${Property};${end.if.non.optional}${end.if.non.object}${end.if.non.array}${end.if.non.anyOf} diff --git a/languages/cpp/templates/result-instantiation/sub-property/ref.h b/languages/cpp/templates/result-instantiation/sub-property/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/sub-property/title.cpp b/languages/cpp/templates/result-instantiation/sub-property/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/result-instantiation/sub-property/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/title.cpp b/languages/cpp/templates/result-instantiation/title.cpp new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/result-instantiation/title.cpp @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/result-instantiation/tuple.cpp b/languages/cpp/templates/result-instantiation/tuple.cpp new file mode 100644 index 00000000..9ba6f3af --- /dev/null +++ b/languages/cpp/templates/result-instantiation/tuple.cpp @@ -0,0 +1,5 @@ + ASSERT((jsonResult.Length() == 0) || (jsonResult.Length() == 2)); + if (jsonResult.Length() == 2) { + ${property}.first = jsonResult.Get(0); + ${property}.second = jsonResult.Get(1); + } diff --git a/languages/cpp/templates/result/default.h b/languages/cpp/templates/result/default.h new file mode 100644 index 00000000..30cba475 --- /dev/null +++ b/languages/cpp/templates/result/default.h @@ -0,0 +1 @@ +${method.result.type} \ No newline at end of file diff --git a/languages/cpp/templates/schemas/default.cpp b/languages/cpp/templates/schemas/default.cpp new file mode 100644 index 00000000..13239ca8 --- /dev/null +++ b/languages/cpp/templates/schemas/default.cpp @@ -0,0 +1 @@ +${schema.shape} \ No newline at end of file diff --git a/languages/cpp/templates/schemas/include/common/module.h b/languages/cpp/templates/schemas/include/common/module.h new file mode 100644 index 00000000..87ba7425 --- /dev/null +++ b/languages/cpp/templates/schemas/include/common/module.h @@ -0,0 +1,35 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include "error.h" +/* ${IMPORTS} */ + +${if.declarations}namespace Firebolt { +namespace ${info.Title} { +${if.enums} + +// Enums +/* ${ENUMS} */${end.if.enums} +${if.types} +// Types +/* ${TYPES} */${end.if.types} + +} //namespace ${info.Title} +}${end.if.declarations} diff --git a/languages/cpp/templates/schemas/src/jsondata_module.h b/languages/cpp/templates/schemas/src/jsondata_module.h new file mode 100644 index 00000000..916b8698 --- /dev/null +++ b/languages/cpp/templates/schemas/src/jsondata_module.h @@ -0,0 +1,30 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +/* ${IMPORTS} */ +#include "common/${info.title.lowercase}.h" + +${if.schemas}namespace Firebolt { +namespace ${info.Title} { + + // Types + /* ${SCHEMAS:json-types} */ +} +}${end.if.schemas} diff --git a/languages/cpp/templates/schemas/src/module_common.cpp b/languages/cpp/templates/schemas/src/module_common.cpp new file mode 100644 index 00000000..7d9d13af --- /dev/null +++ b/languages/cpp/templates/schemas/src/module_common.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "FireboltSDK.h" +/* ${IMPORTS} */ +#include "jsondata_${info.title.lowercase}.h" +${if.enums} + +namespace WPEFramework { + +/* ${ENUMS} */ +}${end.if.enums} \ No newline at end of file diff --git a/languages/cpp/templates/sdk/include/firebolt.h b/languages/cpp/templates/sdk/include/firebolt.h new file mode 100644 index 00000000..fbd99809 --- /dev/null +++ b/languages/cpp/templates/sdk/include/firebolt.h @@ -0,0 +1,133 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#pragma once + +#include +#include "types.h" +#include "error.h" +${module.includes} + +namespace Firebolt { + +struct IFireboltAccessor { + + virtual ~IFireboltAccessor() = default; + + /** + * @brief Get the FireboltAccessor singleton instance + * + * @return FireboltAccessor Instance + * */ + static IFireboltAccessor& Instance(); + + /** + * @brief Inititalize the Firebolt SDK. Sets up the Transport, WorkerPool and Logging Subsystems. + * + * @param configLine JSON String with configuration options. At a minimum the user is expected to pass in the Websocket URL. + * + * CONFIG Format: + * { + * "waitTime": 1000, + * "logLevel": "Info", + * "workerPool":{ + * "queueSize": 8, + * "threadCount": 3 + * }, + * "wsUrl": "ws://127.0.0.1:9998" + * } + * + * + * @return Firebolt::Error + * + */ + + virtual Firebolt::Error Initialize ( const std::string& configLine ) = 0; + + /** + * @brief Deinititlize the SDK. + * + * @return Firebolt::Error + * + */ + virtual Firebolt::Error Deinitialize ( ) = 0; + + + /** + * @brief Connection status listener callback + * + * @param connected Connection status: true - Connected, false - Disconnected + * @param error Reason, if any. + * + * @return None + */ + using OnConnectionChanged = std::function; + + /** + * @brief Attempt a connection to the endpoint. This method is asynchronous and the user is expected to wait for the + * OnConnectionChanged callback to report successful connection before calling SDK methods + * + * @param listener Connection status listener + * + * @return Firebolt::Error + */ + virtual Firebolt::Error Connect ( OnConnectionChanged listener ) = 0; + + /** + * @brief Disconnects from the Websocket endpoint. + * + * @return Firebolt::Error + */ + virtual Firebolt::Error Disconnect ( ) = 0; + + /** + * @brief Dispose the FireboltAccessor instance and all associated module instances. + * + * @return None + * + */ + static void Dispose(); + + /** + * @brief Error callback when a method fails. + * + * @param method Name of the method e.g, "Device.id" + * @param payload JSONRPC Payload of the method. + * @param error Failure reason. + * + * @return None + */ + using OnError = std::function; + + /** + * @brief Register for Error notifications. + * + * @param notification OnError Notification callback. Passing a nullptr will unregister. + * + * @return None + */ + virtual void ErrorListener(OnError notification) = 0; + + + // Module Instance methods goes here. + // Instances are owned by the FireboltAcccessor and linked with its lifecycle. + +${module.init} +}; + +} diff --git a/languages/cpp/templates/sdk/scripts/build.sh b/languages/cpp/templates/sdk/scripts/build.sh new file mode 100755 index 00000000..8f2142df --- /dev/null +++ b/languages/cpp/templates/sdk/scripts/build.sh @@ -0,0 +1,44 @@ +#!/bin/bash +usage() +{ + echo "options:" + echo " -p sdk path" + echo " -s sysroot path" + echo " -c clear build" + echo " -l enable static build" + echo " -t enable test" + echo " -h : help" + echo + echo "usage: " + echo " ./build.sh -p path -tc" +} + +SdkPath="." +EnableTest="OFF" +SysrootPath=${SYSROOT_PATH} +ClearBuild="N" +EnableStaticLib="OFF" +while getopts p:s:clth flag +do + case "${flag}" in + p) SdkPath="${OPTARG}";; + s) SysrootPath="${OPTARG}";; + c) ClearBuild="Y";; + l) EnableStaticLib="ON";; + t) EnableTest="ON";; + h) usage && exit 1;; + esac +done + +if [ "${ClearBuild}" == "Y" ]; +then + rm -rf ${SdkPath}/build +fi + +rm -rf ${SdkPath}/build/src/libFireboltSDK.so +cmake -B${SdkPath}/build -S${SdkPath} -DSYSROOT_PATH=${SysrootPath} -DENABLE_TESTS=${EnableTest} -DHIDE_NON_EXTERNAL_SYMBOLS=OFF -DFIREBOLT_ENABLE_STATIC_LIB=${EnableStaticLib} +cmake --build ${SdkPath}/build +if [ -f "${SdkPath}/build/src/libFireboltSDK.so" ]; +then + cmake --install ${SdkPath}/build --prefix ${SdkPath}/build/Firebolt/usr +fi diff --git a/languages/cpp/templates/sdk/scripts/install.sh b/languages/cpp/templates/sdk/scripts/install.sh new file mode 100755 index 00000000..b31556a2 --- /dev/null +++ b/languages/cpp/templates/sdk/scripts/install.sh @@ -0,0 +1,65 @@ +#!/bin/bash +usage() +{ + echo "options:" + echo " -i install path" + echo " -s sdk path" + echo " -m module name. i.e, core/manage" + echo + echo "usage: " + echo " ./install.sh -i path -s sdk path -m core" +} + +SdkPath=".." +InstallPath=".." +ModuleName="core" +while getopts i:s:m:h flag +do + case "${flag}" in + i) InstallPath="${OPTARG}";; + s) SdkPath="${OPTARG}";; + m) ModuleName="${OPTARG}";; + h) usage && exit 1;; + esac +done + +GetVersion() +{ + PackagePath=${SdkPath}/../../../../../../package-lock.json + InputKey="\"@firebolt-js/openrpc\":" + Line=$(grep -n "${InputKey}" ${PackagePath}) + if [[ "${Line}" == *"file:"* ]]; then + InputKey="name\": \"@firebolt-js/openrpc" + Line=$(grep -n "${InputKey}" ${PackagePath}) + LineNo="$(echo ${Line} | head -n 1 | cut -d: -f1)" + VersionLine=$((LineNo++)) + else + LineNo="$(echo ${Line} | head -n 1 | cut -d: -f1)" + fi + eval "array=(`sed -n "${LineNo}p" < ${PackagePath} | sed 's/\"/\n/g'`)" + Version=${array[2]} +} + +Version=0.0 +GetVersion +ReleaseName=firebolt-${ModuleName}-native-sdk-${Version} +ReleasePath=${InstallPath}/${ReleaseName} + +rm -rf ${ReleasePath} +mkdir -p ${ReleasePath} +cp -aR ${SdkPath}/src ${ReleasePath} +cp -aR ${SdkPath}/include ${ReleasePath} +cp -aR ${SdkPath}/cmake ${ReleasePath} +cp -aR ${SdkPath}/scripts/build.sh ${ReleasePath} +cp -aR ${SdkPath}/CMakeLists.txt ${ReleasePath} +cp -aR ${SdkPath}/cpptest ${ReleasePath}/test + +sed -i'' -e '/EnableTest="ON";;/d' ${ReleasePath}/build.sh +sed -i'' -e 's/getopts p:s:tch/getopts p:s:ch/g' ${ReleasePath}/build.sh +sed -i'' -e '/enable test/d' ${ReleasePath}/build.sh +sed -i'' -e '/EnableTest="OFF"/d' ${ReleasePath}/build.sh +sed -i'' -e 's/ -DENABLE_TESTS=${EnableTest}//g' ${ReleasePath}/build.sh + +cd ${ReleasePath}/../ +tar -cvzf ${ReleaseName}.tgz ${ReleaseName}/* +cd - diff --git a/languages/cpp/templates/sdk/src/firebolt.cpp b/languages/cpp/templates/sdk/src/firebolt.cpp new file mode 100644 index 00000000..a864c2c4 --- /dev/null +++ b/languages/cpp/templates/sdk/src/firebolt.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2023 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "FireboltSDK.h" +${module.includes.private} + +namespace Firebolt { + + class FireboltAccessorImpl : public IFireboltAccessor { + private: + using ModuleMap = std::unordered_map; + + private: + FireboltAccessorImpl() + : _accessor(nullptr) + { + ASSERT(_singleton == nullptr); + _singleton = this; + } + public: + FireboltAccessorImpl(const FireboltAccessorImpl&) = delete; + FireboltAccessorImpl& operator=(const FireboltAccessorImpl&) = delete; + + ~FireboltAccessorImpl() + { + if (_accessor != nullptr) { + _accessor->Dispose(); + _accessor = nullptr; + } + + ASSERT(_singleton != nullptr); + _singleton = nullptr; + } + + static FireboltAccessorImpl& Instance() + { + static FireboltAccessorImpl* instance = new FireboltAccessorImpl(); + ASSERT(instance != nullptr); + return *instance; + } + + static void Dispose() + { + ModuleMap::iterator module = _moduleMap.begin(); + while (module != _moduleMap.end()) { + delete module->second; + module = _moduleMap.erase(module); + } + + ASSERT(_singleton != nullptr); + if (_singleton != nullptr) { + delete _singleton; + } + } + + Firebolt::Error Initialize( const std::string& configLine ) override + { + _accessor = &(FireboltSDK::Accessor::Instance(configLine)); + return Error::None; + } + + Firebolt::Error Deinitialize() override + { + return Error::None; + } + + Firebolt::Error Connect( OnConnectionChanged listener ) override + { + return _accessor->Connect(listener); + } + + Firebolt::Error Disconnect() override + { + return _accessor->Disconnect(); + } + + void ErrorListener(OnError notification) override + { + } + +${module.init} + private: + FireboltSDK::Accessor* _accessor; + static FireboltAccessorImpl* _singleton; + static ModuleMap _moduleMap; + }; + + FireboltAccessorImpl::ModuleMap FireboltAccessorImpl::_moduleMap; + + FireboltAccessorImpl* FireboltAccessorImpl::_singleton = nullptr; + + /* static */ IFireboltAccessor& IFireboltAccessor::Instance() + { + return (FireboltAccessorImpl::Instance()); + } + /* static */ void IFireboltAccessor::Dispose() + { + FireboltAccessorImpl::Dispose(); + } +} diff --git a/languages/cpp/templates/sections/declarations.h b/languages/cpp/templates/sections/declarations.h new file mode 100644 index 00000000..b3ef974d --- /dev/null +++ b/languages/cpp/templates/sections/declarations.h @@ -0,0 +1 @@ +${declaration.list} diff --git a/languages/cpp/templates/sections/enums.h b/languages/cpp/templates/sections/enums.h new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/cpp/templates/sections/enums.h @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/cpp/templates/sections/events.h b/languages/cpp/templates/sections/events.h new file mode 100644 index 00000000..5be10409 --- /dev/null +++ b/languages/cpp/templates/sections/events.h @@ -0,0 +1 @@ +${event.list} diff --git a/languages/cpp/templates/sections/methods.h b/languages/cpp/templates/sections/methods.h new file mode 100644 index 00000000..e8200eb0 --- /dev/null +++ b/languages/cpp/templates/sections/methods.h @@ -0,0 +1 @@ +${method.list} diff --git a/languages/cpp/templates/sections/methods_types.h b/languages/cpp/templates/sections/methods_types.h new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/cpp/templates/sections/methods_types.h @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/cpp/templates/sections/provider-interfaces.cpp b/languages/cpp/templates/sections/provider-interfaces.cpp new file mode 100644 index 00000000..0198e308 --- /dev/null +++ b/languages/cpp/templates/sections/provider-interfaces.cpp @@ -0,0 +1 @@ +${providers.list} \ No newline at end of file diff --git a/languages/cpp/templates/sections/provider-interfaces.h b/languages/cpp/templates/sections/provider-interfaces.h new file mode 100644 index 00000000..88134258 --- /dev/null +++ b/languages/cpp/templates/sections/provider-interfaces.h @@ -0,0 +1,14 @@ +// Provider Interfaces +struct IProviderSession { + virtual ~IProviderSession() = default; + + virtual std::string correlationId() const = 0; +}; + +struct IFocussableProviderSession : virtual public IProviderSession { + virtual ~IFocussableProviderSession() override = default; + + virtual void focus( Firebolt::Error *err = nullptr ) = 0; +}; + +${providers.list} diff --git a/languages/cpp/templates/sections/provider-subscribe.cpp b/languages/cpp/templates/sections/provider-subscribe.cpp new file mode 100644 index 00000000..0198e308 --- /dev/null +++ b/languages/cpp/templates/sections/provider-subscribe.cpp @@ -0,0 +1 @@ +${providers.list} \ No newline at end of file diff --git a/languages/cpp/templates/sections/schemas.h b/languages/cpp/templates/sections/schemas.h new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/cpp/templates/sections/schemas.h @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/cpp/templates/sections/types.h b/languages/cpp/templates/sections/types.h new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/cpp/templates/sections/types.h @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/cpp/templates/sections/xuses-interfaces.h b/languages/cpp/templates/sections/xuses-interfaces.h new file mode 100644 index 00000000..b5b580c7 --- /dev/null +++ b/languages/cpp/templates/sections/xuses-interfaces.h @@ -0,0 +1,7 @@ +struct I${info.Title}AsyncResponse { + + virtual ~I${info.Title}AsyncResponse() = default; + + virtual void response(const std::string& result, Firebolt::Error *err) = 0; +}; + diff --git a/languages/cpp/templates/types/additionalProperties.h b/languages/cpp/templates/types/additionalProperties.h new file mode 100644 index 00000000..89a249cc --- /dev/null +++ b/languages/cpp/templates/types/additionalProperties.h @@ -0,0 +1 @@ +using ${title} = std::unordered_map<${namespace}${key}, ${type}>; \ No newline at end of file diff --git a/languages/cpp/templates/types/additionalPropertiesKey.h b/languages/cpp/templates/types/additionalPropertiesKey.h new file mode 100644 index 00000000..9c1a95b6 --- /dev/null +++ b/languages/cpp/templates/types/additionalPropertiesKey.h @@ -0,0 +1 @@ +std::string diff --git a/languages/cpp/templates/types/anyOf.h b/languages/cpp/templates/types/anyOf.h new file mode 100644 index 00000000..c6d1c815 --- /dev/null +++ b/languages/cpp/templates/types/anyOf.h @@ -0,0 +1 @@ +std::string \ No newline at end of file diff --git a/languages/cpp/templates/types/anyOfSchemaShape.h b/languages/cpp/templates/types/anyOfSchemaShape.h new file mode 100644 index 00000000..93a91ea4 --- /dev/null +++ b/languages/cpp/templates/types/anyOfSchemaShape.h @@ -0,0 +1 @@ +/* anyOf schema shape is not supported right now */ diff --git a/languages/cpp/templates/types/array.h b/languages/cpp/templates/types/array.h new file mode 100644 index 00000000..ed5a1d2b --- /dev/null +++ b/languages/cpp/templates/types/array.h @@ -0,0 +1 @@ +std::vector<${type}> \ No newline at end of file diff --git a/languages/cpp/templates/types/boolean.h b/languages/cpp/templates/types/boolean.h new file mode 100644 index 00000000..fc75f206 --- /dev/null +++ b/languages/cpp/templates/types/boolean.h @@ -0,0 +1 @@ +bool \ No newline at end of file diff --git a/languages/cpp/templates/types/const.h b/languages/cpp/templates/types/const.h new file mode 100644 index 00000000..c6d1c815 --- /dev/null +++ b/languages/cpp/templates/types/const.h @@ -0,0 +1 @@ +std::string \ No newline at end of file diff --git a/languages/cpp/templates/types/default.h b/languages/cpp/templates/types/default.h new file mode 100644 index 00000000..a034b220 --- /dev/null +++ b/languages/cpp/templates/types/default.h @@ -0,0 +1 @@ +${shape} diff --git a/languages/cpp/templates/types/enum.cpp b/languages/cpp/templates/types/enum.cpp new file mode 100644 index 00000000..e162ef43 --- /dev/null +++ b/languages/cpp/templates/types/enum.cpp @@ -0,0 +1,4 @@ + /* ${title} ${description} */ + ENUM_CONVERSION_BEGIN(Firebolt::${info.Title}::${name}) + { Firebolt::${info.Title}::${name}::${key}, _T("${value}") }, + ENUM_CONVERSION_END(Firebolt::${info.Title}::${name}) diff --git a/languages/cpp/templates/types/enum.h b/languages/cpp/templates/types/enum.h new file mode 100644 index 00000000..6ec88816 --- /dev/null +++ b/languages/cpp/templates/types/enum.h @@ -0,0 +1,4 @@ +/* ${title} ${description} */ +enum class ${name} { + ${key}${delimiter},${end.delimiter} +}; diff --git a/languages/cpp/templates/types/integer.h b/languages/cpp/templates/types/integer.h new file mode 100644 index 00000000..33028e0f --- /dev/null +++ b/languages/cpp/templates/types/integer.h @@ -0,0 +1 @@ +int32_t \ No newline at end of file diff --git a/languages/cpp/templates/types/items.h b/languages/cpp/templates/types/items.h new file mode 100644 index 00000000..52268157 --- /dev/null +++ b/languages/cpp/templates/types/items.h @@ -0,0 +1 @@ +${title}${delimiter}, ${end.delimiter} \ No newline at end of file diff --git a/languages/cpp/templates/types/namespace.h b/languages/cpp/templates/types/namespace.h new file mode 100644 index 00000000..b3afde8a --- /dev/null +++ b/languages/cpp/templates/types/namespace.h @@ -0,0 +1 @@ +${if.namespace.notsame}${parent.Title}::${end.if.namespace.notsame} \ No newline at end of file diff --git a/languages/cpp/templates/types/null.h b/languages/cpp/templates/types/null.h new file mode 100644 index 00000000..ab6cc8f6 --- /dev/null +++ b/languages/cpp/templates/types/null.h @@ -0,0 +1 @@ +void \ No newline at end of file diff --git a/languages/cpp/templates/types/number.h b/languages/cpp/templates/types/number.h new file mode 100644 index 00000000..05eeb48f --- /dev/null +++ b/languages/cpp/templates/types/number.h @@ -0,0 +1 @@ +float \ No newline at end of file diff --git a/languages/cpp/templates/types/object-empty-property.h b/languages/cpp/templates/types/object-empty-property.h new file mode 100644 index 00000000..a5aae7f4 --- /dev/null +++ b/languages/cpp/templates/types/object-empty-property.h @@ -0,0 +1,2 @@ +/* ${title} */ +using ${title} = std::string; \ No newline at end of file diff --git a/languages/cpp/templates/types/object.h b/languages/cpp/templates/types/object.h new file mode 100644 index 00000000..4bf36f8e --- /dev/null +++ b/languages/cpp/templates/types/object.h @@ -0,0 +1,3 @@ +struct ${title} { +${properties} +}; \ No newline at end of file diff --git a/languages/cpp/templates/types/primitive.h b/languages/cpp/templates/types/primitive.h new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/cpp/templates/types/primitive.h @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/cpp/templates/types/property.h b/languages/cpp/templates/types/property.h new file mode 100644 index 00000000..a0ea4ab1 --- /dev/null +++ b/languages/cpp/templates/types/property.h @@ -0,0 +1 @@ + ${if.optional}std::optional<${end.if.optional}${title}${if.optional}>${end.if.optional} ${property}; diff --git a/languages/cpp/templates/types/ref.h b/languages/cpp/templates/types/ref.h new file mode 100644 index 00000000..4da5b5d9 --- /dev/null +++ b/languages/cpp/templates/types/ref.h @@ -0,0 +1 @@ +${shape} \ No newline at end of file diff --git a/languages/cpp/templates/types/string.h b/languages/cpp/templates/types/string.h new file mode 100644 index 00000000..c6d1c815 --- /dev/null +++ b/languages/cpp/templates/types/string.h @@ -0,0 +1 @@ +std::string \ No newline at end of file diff --git a/languages/cpp/templates/types/title.h b/languages/cpp/templates/types/title.h new file mode 100644 index 00000000..2a420b4b --- /dev/null +++ b/languages/cpp/templates/types/title.h @@ -0,0 +1 @@ +${Title} \ No newline at end of file diff --git a/languages/cpp/templates/types/tuple.h b/languages/cpp/templates/types/tuple.h new file mode 100644 index 00000000..0ba9370f --- /dev/null +++ b/languages/cpp/templates/types/tuple.h @@ -0,0 +1,2 @@ +/* ${title} */ +using ${title} = std::pair<${items}>; \ No newline at end of file diff --git a/languages/cpp/templates/types/x-method.h b/languages/cpp/templates/types/x-method.h new file mode 100644 index 00000000..573c9f8e --- /dev/null +++ b/languages/cpp/templates/types/x-method.h @@ -0,0 +1 @@ +void* \ No newline at end of file diff --git a/languages/javascript/language.config.json b/languages/javascript/language.config.json index a2eff1ed..19b4659d 100644 --- a/languages/javascript/language.config.json +++ b/languages/javascript/language.config.json @@ -7,11 +7,21 @@ ], "createModuleDirectories": true, "copySchemasIntoModules": true, - "aggregateFile": "/index.d.ts", + "aggregateFiles": [ + "/index.d.ts" + ], "treeshakePattern": "(import|export).*?from\\s+['\"](.*?)['\"]", "treeshakeTypes": [".js", ".mjs"], "operators": { "or": " | ", "stringQuotation": "'" - } -} \ No newline at end of file + }, + "primitives": { + "boolean": "boolean", + "integer": "number", + "number": "number", + "string": "string", + "object": "object" + }, + "additionalMethodTemplates": [ "declarations" ] +} diff --git a/languages/javascript/templates/codeblocks/module.mjs b/languages/javascript/templates/codeblocks/module.mjs index cb5ca256..def593af 100644 --- a/languages/javascript/templates/codeblocks/module.mjs +++ b/languages/javascript/templates/codeblocks/module.mjs @@ -4,8 +4,8 @@ ${if.events} type Event = ${events} ${end.if.events} /* ${SCHEMAS} */ - /* ${DECLARATIONS} */ + /* ${METHODS:declarations} */ /* ${PROVIDERS} */ -} \ No newline at end of file +} diff --git a/languages/javascript/templates/declarations/default.js b/languages/javascript/templates/declarations/default.js index a9690ece..950ffac9 100644 --- a/languages/javascript/templates/declarations/default.js +++ b/languages/javascript/templates/declarations/default.js @@ -3,4 +3,4 @@ * ${method.params.annotations}${if.deprecated} * @deprecated ${method.deprecation} ${end.if.deprecated} */ - ${method.signature} + function ${method.name}(${method.signature.params}): Promise<${method.result.type}> diff --git a/languages/javascript/templates/declarations/interface.js b/languages/javascript/templates/declarations/interface.js new file mode 100644 index 00000000..fb48facb --- /dev/null +++ b/languages/javascript/templates/declarations/interface.js @@ -0,0 +1 @@ +${method.name}(${method.signature.params}): Promise<${method.result.type}> \ No newline at end of file diff --git a/languages/javascript/templates/declarations/polymorphic-reducer.js b/languages/javascript/templates/declarations/polymorphic-reducer.js index 24fff57d..6eb4d88d 100644 --- a/languages/javascript/templates/declarations/polymorphic-reducer.js +++ b/languages/javascript/templates/declarations/polymorphic-reducer.js @@ -3,6 +3,4 @@ * ${method.params.annotations}${if.deprecated} * @deprecated ${method.deprecation} ${end.if.deprecated} */ -${method.signature} - -// TODO: generate reducer signature \ No newline at end of file +function ${method.name}(${method.signature.params}): Promise<${method.result.type}> diff --git a/languages/javascript/templates/declarations/synchronous.js b/languages/javascript/templates/declarations/synchronous.js new file mode 100644 index 00000000..ca039b32 --- /dev/null +++ b/languages/javascript/templates/declarations/synchronous.js @@ -0,0 +1,6 @@ + /** + * ${method.summary} + * +${method.params.annotations}${if.deprecated} * @deprecated ${method.deprecation} +${end.if.deprecated} */ +function ${method.name}(${method.signature.params}): ${method.result.type} diff --git a/languages/javascript/templates/interfaces/default.mjs b/languages/javascript/templates/interfaces/default.mjs new file mode 100644 index 00000000..63f63093 --- /dev/null +++ b/languages/javascript/templates/interfaces/default.mjs @@ -0,0 +1 @@ + ${method.name}(${method.signature.params}, session: ProviderSession): Promise<${method.result.type}> diff --git a/languages/javascript/templates/interfaces/focusable.mjs b/languages/javascript/templates/interfaces/focusable.mjs new file mode 100644 index 00000000..b81737b1 --- /dev/null +++ b/languages/javascript/templates/interfaces/focusable.mjs @@ -0,0 +1 @@ + ${method.name}(${method.signature.params}, session: FocusableProviderSession): Promise<${method.result.type}> diff --git a/languages/javascript/templates/parameters/default.js b/languages/javascript/templates/parameters/default.js index 916a8adf..ab44d4b5 100644 --- a/languages/javascript/templates/parameters/default.js +++ b/languages/javascript/templates/parameters/default.js @@ -1 +1 @@ -${method.params.list} \ No newline at end of file +${method.param.name}: ${method.param.type} \ No newline at end of file diff --git a/languages/javascript/templates/parameters/optional.js b/languages/javascript/templates/parameters/optional.js new file mode 100644 index 00000000..20cf5889 --- /dev/null +++ b/languages/javascript/templates/parameters/optional.js @@ -0,0 +1 @@ +${method.param.name}?: ${method.param.type} \ No newline at end of file diff --git a/languages/javascript/templates/schemas/default.js b/languages/javascript/templates/schemas/default.js index bc9ab873..f00b1e37 100644 --- a/languages/javascript/templates/schemas/default.js +++ b/languages/javascript/templates/schemas/default.js @@ -1,5 +1,4 @@ /** * ${schema.description} */ - ${schema.shape} diff --git a/languages/javascript/templates/sections/enums.js b/languages/javascript/templates/sections/enums.js new file mode 100644 index 00000000..9295133c --- /dev/null +++ b/languages/javascript/templates/sections/enums.js @@ -0,0 +1 @@ +${schema.list} diff --git a/languages/javascript/templates/sections/provider-interfaces.js b/languages/javascript/templates/sections/provider-interfaces.js index 418d7abf..0b77c87d 100644 --- a/languages/javascript/templates/sections/provider-interfaces.js +++ b/languages/javascript/templates/sections/provider-interfaces.js @@ -1,11 +1,11 @@ - // Provider Interfaces +// Provider Interfaces - interface ProviderSession { - correlationId(): string // Returns the correlation id of the current provider session - } - - interface FocusableProviderSession extends ProviderSession { - focus(): Promise // Requests that the provider app be moved into focus to prevent a user experience - } - - ${providers.list} \ No newline at end of file +interface ProviderSession { + correlationId(): string // Returns the correlation id of the current provider session +} + +interface FocusableProviderSession extends ProviderSession { + focus(): Promise // Requests that the provider app be moved into focus to prevent a user experience +} + +${providers.list} \ No newline at end of file diff --git a/languages/javascript/templates/types/anyOf.mjs b/languages/javascript/templates/types/anyOf.mjs new file mode 100644 index 00000000..67f67236 --- /dev/null +++ b/languages/javascript/templates/types/anyOf.mjs @@ -0,0 +1 @@ +${type}${delimiter} | ${end.delimiter} \ No newline at end of file diff --git a/languages/javascript/templates/types/array.mjs b/languages/javascript/templates/types/array.mjs new file mode 100644 index 00000000..f6dc0dcf --- /dev/null +++ b/languages/javascript/templates/types/array.mjs @@ -0,0 +1 @@ +${title}[] \ No newline at end of file diff --git a/languages/javascript/templates/types/const.mjs b/languages/javascript/templates/types/const.mjs new file mode 100644 index 00000000..b711be8b --- /dev/null +++ b/languages/javascript/templates/types/const.mjs @@ -0,0 +1 @@ +${value} \ No newline at end of file diff --git a/languages/javascript/templates/types/default.mjs b/languages/javascript/templates/types/default.mjs new file mode 100644 index 00000000..3d9103b2 --- /dev/null +++ b/languages/javascript/templates/types/default.mjs @@ -0,0 +1 @@ +type ${title} = ${shape} \ No newline at end of file diff --git a/languages/javascript/templates/types/default.ts b/languages/javascript/templates/types/default.ts new file mode 100644 index 00000000..3d9103b2 --- /dev/null +++ b/languages/javascript/templates/types/default.ts @@ -0,0 +1 @@ +type ${title} = ${shape} \ No newline at end of file diff --git a/languages/javascript/templates/types/enum-empty-property.mjs b/languages/javascript/templates/types/enum-empty-property.mjs new file mode 100644 index 00000000..0db3279e --- /dev/null +++ b/languages/javascript/templates/types/enum-empty-property.mjs @@ -0,0 +1,3 @@ +{ + +} diff --git a/languages/javascript/templates/types/enum.mjs b/languages/javascript/templates/types/enum.mjs index 907fb37d..ae4a06b6 100644 --- a/languages/javascript/templates/types/enum.mjs +++ b/languages/javascript/templates/types/enum.mjs @@ -1,3 +1,3 @@ -${name}: { +${title}: { ${key}: '${value}', }, diff --git a/languages/javascript/templates/types/enum.ts b/languages/javascript/templates/types/enum.ts new file mode 100644 index 00000000..0743ba85 --- /dev/null +++ b/languages/javascript/templates/types/enum.ts @@ -0,0 +1,3 @@ +enum ${title} { + ${key} = '${value}', +} diff --git a/languages/javascript/templates/types/generic.mjs b/languages/javascript/templates/types/generic.mjs new file mode 100644 index 00000000..a60cb41f --- /dev/null +++ b/languages/javascript/templates/types/generic.mjs @@ -0,0 +1 @@ +${title} \ No newline at end of file diff --git a/languages/javascript/templates/types/items.mjs b/languages/javascript/templates/types/items.mjs new file mode 100644 index 00000000..9b964820 --- /dev/null +++ b/languages/javascript/templates/types/items.mjs @@ -0,0 +1 @@ +${title}${delimiter},${end.delimiter} // ${property} ${summary} item \ No newline at end of file diff --git a/languages/javascript/templates/types/null.mjs b/languages/javascript/templates/types/null.mjs new file mode 100644 index 00000000..ec747fa4 --- /dev/null +++ b/languages/javascript/templates/types/null.mjs @@ -0,0 +1 @@ +null \ No newline at end of file diff --git a/languages/javascript/templates/types/object.mjs b/languages/javascript/templates/types/object.mjs new file mode 100644 index 00000000..78965d52 --- /dev/null +++ b/languages/javascript/templates/types/object.mjs @@ -0,0 +1,3 @@ +{ +${properties} +} diff --git a/languages/javascript/templates/types/primitive.mjs b/languages/javascript/templates/types/primitive.mjs new file mode 100644 index 00000000..23bbace7 --- /dev/null +++ b/languages/javascript/templates/types/primitive.mjs @@ -0,0 +1 @@ +${type} \ No newline at end of file diff --git a/languages/javascript/templates/types/property.mjs b/languages/javascript/templates/types/property.mjs new file mode 100644 index 00000000..66bf6f73 --- /dev/null +++ b/languages/javascript/templates/types/property.mjs @@ -0,0 +1 @@ + ${property}${if.optional}?${end.if.optional}: ${title}${if.summary} // ${summary}${end.if.summary} diff --git a/languages/javascript/templates/types/ref.mjs b/languages/javascript/templates/types/ref.mjs new file mode 100644 index 00000000..a60cb41f --- /dev/null +++ b/languages/javascript/templates/types/ref.mjs @@ -0,0 +1 @@ +${title} \ No newline at end of file diff --git a/languages/javascript/templates/types/title.mjs b/languages/javascript/templates/types/title.mjs new file mode 100644 index 00000000..a60cb41f --- /dev/null +++ b/languages/javascript/templates/types/title.mjs @@ -0,0 +1 @@ +${title} \ No newline at end of file diff --git a/languages/javascript/templates/types/tuple-delimiter.mjs b/languages/javascript/templates/types/tuple-delimiter.mjs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/languages/javascript/templates/types/tuple-delimiter.mjs @@ -0,0 +1 @@ + diff --git a/languages/javascript/templates/types/tuple.mjs b/languages/javascript/templates/types/tuple.mjs new file mode 100644 index 00000000..2c803cd3 --- /dev/null +++ b/languages/javascript/templates/types/tuple.mjs @@ -0,0 +1,3 @@ +[ + ${items} +] \ No newline at end of file diff --git a/languages/javascript/templates/types/x-method.mjs b/languages/javascript/templates/types/x-method.mjs new file mode 100644 index 00000000..607228f3 --- /dev/null +++ b/languages/javascript/templates/types/x-method.mjs @@ -0,0 +1 @@ +(${params}) => Promise<${title}> \ No newline at end of file diff --git a/languages/markdown/templates/codeblocks/interface.md b/languages/markdown/templates/codeblocks/interface.md new file mode 100644 index 00000000..8c69cda0 --- /dev/null +++ b/languages/markdown/templates/codeblocks/interface.md @@ -0,0 +1,3 @@ +interface ${name} { + ${methods} +} \ No newline at end of file diff --git a/languages/markdown/templates/declarations/default.md b/languages/markdown/templates/declarations/default.md new file mode 100644 index 00000000..27a3c4a7 --- /dev/null +++ b/languages/markdown/templates/declarations/default.md @@ -0,0 +1 @@ +function ${method.name}(${method.params}): Promise<${method.result.type}> \ No newline at end of file diff --git a/languages/markdown/templates/interfaces/default.mjs b/languages/markdown/templates/interfaces/default.mjs new file mode 100644 index 00000000..63f63093 --- /dev/null +++ b/languages/markdown/templates/interfaces/default.mjs @@ -0,0 +1 @@ + ${method.name}(${method.signature.params}, session: ProviderSession): Promise<${method.result.type}> diff --git a/languages/markdown/templates/interfaces/focusable.mjs b/languages/markdown/templates/interfaces/focusable.mjs new file mode 100644 index 00000000..b81737b1 --- /dev/null +++ b/languages/markdown/templates/interfaces/focusable.mjs @@ -0,0 +1 @@ + ${method.name}(${method.signature.params}, session: FocusableProviderSession): Promise<${method.result.type}> diff --git a/languages/markdown/templates/types/default.md b/languages/markdown/templates/types/default.md deleted file mode 100644 index f2a4644e..00000000 --- a/languages/markdown/templates/types/default.md +++ /dev/null @@ -1,3 +0,0 @@ -```typescript -${type} -``` \ No newline at end of file diff --git a/languages/markdown/templates/types/enum.md b/languages/markdown/templates/types/enum.md deleted file mode 100644 index 59861d52..00000000 --- a/languages/markdown/templates/types/enum.md +++ /dev/null @@ -1,5 +0,0 @@ -${name} Enumeration: - -| key | value | -|-----|-------| -| ${key} | ${value} | diff --git a/languages/markdown/templates/types/object.md b/languages/markdown/templates/types/object.md deleted file mode 100644 index 2b15f271..00000000 --- a/languages/markdown/templates/types/object.md +++ /dev/null @@ -1,3 +0,0 @@ -| Property | Type | Description | -|----------|------|-------------| -| `${property}` | [${type}](${type.link}) | ${description} | diff --git a/package-lock.json b/package-lock.json index e2ff007f..de926ccc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@firebolt-js/openrpc", - "version": "2.3.0", + "version": "3.0.0-next.5", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@firebolt-js/openrpc", - "version": "2.3.0", + "version": "3.0.0-next.5", "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", @@ -2436,14 +2436,14 @@ } }, "node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.15.0.tgz", + "integrity": "sha512-15BTtQUOsSrmHCy+B4VnAiJAJxJ8IFgu6fcjFQF3jQYZ78nLSQthlFg4ehp+NLIyfvFgOlxNsjKIEhydtFPVHQ==", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.3.0", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -2451,9 +2451,9 @@ } }, "node_modules/ajv-formats": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz", - "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dependencies": { "ajv": "^8.0.0" }, @@ -3795,6 +3795,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-uri": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", + "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" + }, "node_modules/fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -10349,6 +10354,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, "engines": { "node": ">=6" } @@ -11521,14 +11527,6 @@ "node": ">= 4.0.0" } }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", @@ -13766,20 +13764,20 @@ } }, "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.15.0.tgz", + "integrity": "sha512-15BTtQUOsSrmHCy+B4VnAiJAJxJ8IFgu6fcjFQF3jQYZ78nLSQthlFg4ehp+NLIyfvFgOlxNsjKIEhydtFPVHQ==", "requires": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.3.0", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "ajv-formats": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.0.tgz", - "integrity": "sha512-USH2jBb+C/hIpwD2iRjp0pe0k+MvzG0mlSn/FIdCgQhUb9ALPRjt2KIQdfZDS9r0ZIeUAg7gOu9KL0PFqGqr5Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "requires": { "ajv": "^8.0.0" } @@ -14809,6 +14807,11 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-uri": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.3.0.tgz", + "integrity": "sha512-eel5UKGn369gGEWOqBShmFJWfq/xSJvsgDzgLYC845GneayWvXBf0lJCBn5qTABfewy1ZDPoaR5OZCP+kssfuw==" + }, "fastq": { "version": "1.13.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", @@ -19519,7 +19522,8 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "q": { "version": "1.5.1", @@ -20400,14 +20404,6 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, "url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", diff --git a/package.json b/package.json index 40f50b13..cc528173 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@firebolt-js/openrpc", - "version": "2.3.0", + "version": "3.0.0-next.5", "description": "The Firebolt SDK Code & Doc Generator", "main": "languages/javascript/src/sdk.mjs", "type": "module", @@ -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", diff --git a/src/cli.mjs b/src/cli.mjs index 8e09f6c7..9cb60368 100755 --- a/src/cli.mjs +++ b/src/cli.mjs @@ -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 = { diff --git a/src/docs/index.mjs b/src/docs/index.mjs index 66dd4136..fdf71acd 100755 --- a/src/docs/index.mjs +++ b/src/docs/index.mjs @@ -59,6 +59,7 @@ const run = async ({ examples: examples, templatesPerModule: config.templatesPerModule, templatesPerSchema: config.templatesPerSchema, + operators: config.operators, libraryName: libraryName, hidePrivate: false }) diff --git a/src/firebolt-openrpc.json b/src/firebolt-openrpc.json index 67cf5d8a..e2c2abc8 100644 --- a/src/firebolt-openrpc.json +++ b/src/firebolt-openrpc.json @@ -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"] diff --git a/src/macrofier/engine.mjs b/src/macrofier/engine.mjs index 3f7596ec..9a7823fc 100644 --- a/src/macrofier/engine.mjs +++ b/src/macrofier/engine.mjs @@ -29,9 +29,9 @@ 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, createPolymorphicMethods } from '../shared/modules.mjs' +import { isRPCOnlyMethod, isProviderInterfaceMethod, getProviderInterface, getPayloadFromEvent, providerHasNoParameters, isTemporalSetMethod, hasMethodAttributes, getMethodAttributes, isEventMethodWithContext, getSemanticVersion, getSetterFor, getProvidedCapabilities, isPolymorphicPullMethod, hasPublicAPIs, isAllowFocusMethod, hasAllowFocusMethods, createPolymorphicMethods, isExcludedMethod, isCallsMetricsMethod } from '../shared/modules.mjs' import isEmpty from 'crocks/core/isEmpty.js' -import { getLinkedSchemaPaths, getSchemaConstraints, isSchema, localizeDependencies, isDefinitionReferencedBySchema, getSafeEnumKeyName } from '../shared/json-schema.mjs' +import { getPath as getJsonPath, getLinkedSchemaPaths, getSchemaConstraints, isSchema, localizeDependencies, isDefinitionReferencedBySchema, mergeAnyOf, mergeOneOf, getSafeEnumKeyName } from '../shared/json-schema.mjs' // util for visually debugging crocks ADTs const _inspector = obj => { @@ -42,42 +42,53 @@ const _inspector = obj => { } } -// getMethodSignature(method, module, options = { destination: 'file.txt' }) -// getMethodSignatureParams(method, module, options = { destination: 'file.txt' }) // getSchemaType(schema, module, options = { destination: 'file.txt', title: true }) // getSchemaShape(schema, module, options = { name: 'Foo', destination: 'file.txt' }) // getJsonType(schema, module, options = { name: 'Foo', prefix: '', descriptions: false, level: 0 }) // getSchemaInstantiation(schema, module, options = {type: 'params' | 'result' | 'callback.params'| 'callback.result' | 'callback.response'}) let types = { - getMethodSignature: () => null, - getMethodSignatureParams: () => null, getSchemaShape: () => null, - getSchemaType: () => null, - getJsonType: () => null, - getSchemaInstantiation: () => null + getSchemaType: () => null } let config = { copySchemasIntoModules: false, extractSubSchemas: false, - excludeDeclarations: false + unwrapResultObjects: false, + excludeDeclarations: false, + extractProviderSchema: false, } const state = { destination: undefined, + typeTemplateDir: 'types', section: undefined } const capitalize = str => str[0].toUpperCase() + str.substr(1) -const indent = (str, padding) => { +const indent = (str, paddingStr, repeat = 1, endRepeat = 0) => { let first = true - return str.split('\n').map(line => { + let padding = '' + for (let i = 0; i < repeat; i++) { + padding += paddingStr + } + + let length = str.split('\n').length - 1 + let endPadding = '' + for (let i = 0; length && i < endRepeat; i++) { + endPadding += paddingStr + } + + return str.split('\n').map((line, index) => { if (first) { first = false return line } + else if (index === length && endPadding) { + return endPadding + line + } else { return padding + line } @@ -97,17 +108,17 @@ const getTemplate = (name, templates) => { } const getTemplateTypeForMethod = (method, type, templates) => { - const name = method.tags && method.tags.map(tag => tag.name.split(":").shift()).find(tag => Object.keys(templates).find(name => name.startsWith(`/${type}/${tag}.`))) || 'default' + const name = method.tags ? (isAllowFocusMethod(method) && Object.keys(templates).find(name => name.startsWith(`/${type}/allowsFocus.`))) ? 'allowsFocus' : (method.tags.map(tag => tag.name.split(":").shift()).find(tag => Object.keys(templates).find(name => name.startsWith(`/${type}/${tag}.`)))) || 'default' : 'default' const path = `/${type}/${name}` return getTemplate(path, templates) } -const getTemplateForMethod = (method, templates) => { - return getTemplateTypeForMethod(method, 'methods', templates) +const getTemplateForMethod = (method, templates, templateDir) => { + return getTemplateTypeForMethod(method, templateDir, templates) } -const getTemplateForDeclaration = (method, templates) => { - return getTemplateTypeForMethod(method, 'declarations', templates) +const getTemplateForDeclaration = (method, templates, templateDir) => { + return getTemplateTypeForMethod(method, templateDir, templates) } const getTemplateForExample = (method, templates) => { @@ -119,11 +130,11 @@ const getTemplateForExampleResult = (method, templates) => { return template || JSON.stringify(method.examples[0].result.value, null, '\t') } -const getLinkForSchema = (schema, json, { name = '' } = {}) => { +const getLinkForSchema = (schema, json) => { const dirs = config.createModuleDirectories const copySchemasIntoModules = config.copySchemasIntoModules - const type = types.getSchemaType(schema, json, { name: name, destination: state.destination, section: state.section }) + const type = types.getSchemaType(schema, json, { templateDir: state.typeTemplateDir, destination: state.destination, section: state.section }) // local - insert a bogus link, that we'll update later based on final table-of-contents if (json.components.schemas[type]) { @@ -254,6 +265,14 @@ const isPropertyMethod = (m) => { return hasTag(m, 'property') || hasTag(m, 'property:immutable') || hasTag(m, 'property:readonly') } +const eventHasOptionalParam = (event) => { + return event.params.length && event.params.find(param => !(param.required && param.required === true)) +} + +const isOptionalParam = (param) => { + return (!(param.required && param.required === true)) +} + // Pick methods that call RCP out of the methods array const rpcMethodsOrEmptyArray = compose( option([]), @@ -344,9 +363,22 @@ const makeProviderMethod = x => x.name["onRequest".length].toLowerCase() + x.nam //import { default as platform } from '../Platform/defaults' const generateAggregateMacros = (openrpc, modules, templates, library) => Object.values(modules) .reduce((acc, module) => { - acc.exports += insertMacros(getTemplate('/codeblocks/export', templates) + '\n', generateMacros(module, templates)) - acc.mockImports += insertMacros(getTemplate('/codeblocks/mock-import', templates) + '\n', generateMacros(module, templates)) - acc.mockObjects += insertMacros(getTemplate('/codeblocks/mock-parameter', templates) + '\n', generateMacros(module, templates)) + + let template = getTemplate('/codeblocks/export', templates) + if (template) { + acc.exports += insertMacros(template + '\n', generateMacros(module, templates)) + } + + template = getTemplate('/codeblocks/mock-import', templates) + if (template) { + acc.mockImports += insertMacros(template + '\n', generateMacros(module, templates)) + } + + template = getTemplate('/codeblocks/mock-parameter', templates) + if (template) { + acc.mockObjects += insertMacros(template + '\n', generateMacros(module, templates)) + } + return acc }, { exports: '', @@ -357,70 +389,93 @@ const generateAggregateMacros = (openrpc, modules, templates, library) => Object }) const addContentDescriptorSubSchema = (descriptor, prefix, obj) => { - let title = '' - if (descriptor.schema.type === 'array' && descriptor.schema.items && descriptor.schema.items['$ref']) { - let refName = descriptor.schema.items['$ref'].split('/').pop() - title = refName.charAt(0).toUpperCase() + refName.substring(1) + '_ArrayType' - if (obj.components.schemas[title]) { - descriptor.schema = { - $ref: "#/components/schemas/" + title - } - return - } - } - else { - let descriptorName = capitalize(descriptor.name) - let prefixName = capitalize(prefix) - title = (prefixName !== descriptorName) ? prefixName + '_' +descriptorName : descriptorName - if (obj.components.schemas[title]) { - throw 'Generated name `' + title + '` already exists...' - } - } - obj.components.schemas[title] = descriptor.schema - obj.components.schemas[title].title = title - descriptor.schema = { - $ref: "#/components/schemas/" + title + const title = getPromotionNameFromContentDescriptor(descriptor, prefix) + promoteSchema(descriptor, 'schema', title, obj, "#/components/schemas") +} + +const getPromotionNameFromContentDescriptor = (descriptor, prefix) => { + const subtitle = descriptor.schema.title || descriptor.name.charAt(0).toUpperCase() + descriptor.name.substring(1) + return (prefix ? prefix.charAt(0).toUpperCase() + prefix.substring(1) : '') + subtitle +} + +const promoteSchema = (location, property, title, document, destinationPath) => { + const destination = getJsonPath(destinationPath, document) + destination[title] = location[property] + destination[title].title = title + location[property] = { + $ref: `${destinationPath}/${title}` } } -// only consider sub-objects, sub-array and sub-enums to be sub-schemas -const isSubSchema = (schema) => schema.type === 'object' || (schema.type === 'string' && schema.enum) || (schema.type === 'array' && schema.items) +// only consider sub-objects and sub enums to be sub-schemas +const isSubSchema = (schema) => schema.type === 'object' || (schema.type === 'string' && schema.enum) + +// check schema is sub enum of array +const isSubEnumOfArraySchema = (schema) => (schema.type === 'array' && schema.items.enum) const promoteAndNameSubSchemas = (obj) => { // make a copy so we don't polute our inputs obj = JSON.parse(JSON.stringify(obj)) // find anonymous method param or result schemas and name/promote them obj.methods && obj.methods.forEach(method => { - if (!isExcludedMethod(method)) { - method.params && method.params.forEach(param => { - if (isSubSchema(param.schema)) { - addContentDescriptorSubSchema(param, method.name, obj) + method.params && method.params.forEach(param => { + if (isSubSchema(param.schema)) { + addContentDescriptorSubSchema(param, '', obj) + } + }) + if (isSubSchema(method.result.schema)) { + addContentDescriptorSubSchema(method.result, '', obj) + } + else if (isEventMethod(method) && isSubSchema(getPayloadFromEvent(method))) { + // TODO: the `1` below is brittle... should find the index of the non-ListenResponse schema + promoteSchema(method.result.schema.anyOf, 1, getPromotionNameFromContentDescriptor(method.result, ''), obj, '#/components/schemas') + } + if (method.tags.find(t => t['x-error'])) { + method.tags.forEach(tag => { + if (tag['x-error']) { + const descriptor = { + name: obj.info.title + 'Error', + schema: tag['x-error'] + } + addContentDescriptorSubSchema(descriptor, '', obj) } }) - if (isSubSchema(method.result.schema)) { - addContentDescriptorSubSchema(method.result, method.name, obj) - } } }) - // find non-primative sub-schemas of components.schemas and name/promote them + // find non-primitive sub-schemas of components.schemas and name/promote them if (obj.components && obj.components.schemas) { let more = true while (more) { more = false Object.entries(obj.components.schemas).forEach(([key, schema]) => { - if ((schema.type === "object") && schema.properties) { - Object.entries(schema.properties).forEach(([name, propSchema]) => { - if (isSubSchema(propSchema)) { - more = true - const descriptor = { - name: name, - schema: propSchema + let componentSchemaProperties = schema.allOf ? schema.allOf : [schema] + componentSchemaProperties.forEach((componentSchema) => { + if ((componentSchema.type === "object") && componentSchema.properties) { + Object.entries(componentSchema.properties).forEach(([name, propSchema]) => { + if (isSubSchema(propSchema)) { + more = true + const descriptor = { + name: name, + schema: propSchema + } + addContentDescriptorSubSchema(descriptor, key, obj) + componentSchema.properties[name] = descriptor.schema } - addContentDescriptorSubSchema(descriptor, key, obj) - schema.properties[name] = descriptor.schema - } - }) + if (isSubEnumOfArraySchema(propSchema)) { + const descriptor = { + name: name, + schema: propSchema.items + } + addContentDescriptorSubSchema(descriptor, key, obj) + componentSchema.properties[name].items = descriptor.schema + } + }) + } + }) + + if (!schema.title) { + schema.title = capitalize(key) } }) } @@ -430,10 +485,6 @@ const promoteAndNameSubSchemas = (obj) => { } const generateMacros = (obj, templates, languages, options = {}) => { - // for languages that don't support nested schemas, let's promote them to first-class schemas w/ titles - if (config.extractSubSchemas) { - obj = promoteAndNameSubSchemas(obj) - } if (options.createPolymorphicMethods) { let methods = [] obj.methods && obj.methods.forEach(method => { @@ -449,64 +500,101 @@ const generateMacros = (obj, templates, languages, options = {}) => { }) obj.methods = methods } + // for languages that don't support nested schemas, let's promote them to first-class schemas w/ titles + if (config.extractSubSchemas) { + obj = promoteAndNameSubSchemas(obj) + } // grab the options so we don't have to pass them from method to method Object.assign(state, options) + const macros = { + schemas: {}, + types: {}, + enums: {}, + methods: {}, + events: {}, + methodList: '', + eventList: '' + } + + Array.from(new Set(['types'].concat(config.additionalSchemaTemplates))).filter(dir => dir).forEach(dir => { + state.typeTemplateDir = dir + const schemasArray = generateSchemas(obj, templates, { baseUrl: '', section: 'schemas' }).filter(s => (options.copySchemasIntoModules || !s.uri)) + macros.schemas[dir] = getTemplate('/sections/schemas', templates).replace(/\$\{schema.list\}/g, schemasArray.map(s => s.body).filter(body => body).join('\n')) + macros.types[dir] = getTemplate('/sections/types', templates).replace(/\$\{schema.list\}/g, schemasArray.filter(x => !x.enum).map(s => s.body).filter(body => body).join('\n')) + macros.enums[dir] = getTemplate('/sections/enums', templates).replace(/\$\{schema.list\}/g, schemasArray.filter(x => x.enum).map(s => s.body).filter(body => body).join('\n')) + }) + + state.typeTemplateDir = 'types' const imports = generateImports(obj, templates, { destination: (options.destination ? options.destination : '') }) const initialization = generateInitialization(obj, templates) - const enums = generateEnums(obj, templates, { destination: (options.destination ? options.destination : '') }) const eventsEnum = generateEvents(obj, templates) + const examples = generateExamples(obj, templates, languages) + const allMethodsArray = generateMethods(obj, examples, templates, languages, options.type) + + Array.from(new Set(['methods'].concat(config.additionalMethodTemplates))).filter(dir => dir).forEach(dir => { - const allMethodsArray = generateMethods(obj, examples, templates) - const methodsArray = allMethodsArray.filter(m => m.body && !m.event && (!options.hideExcluded || !m.excluded)) - const eventsArray = allMethodsArray.filter(m => m.body && m.event && (!options.hideExcluded || !m.excluded)) - const declarationsArray = allMethodsArray.filter(m => m.declaration && (!config.excludeDeclarations || (!options.hideExcluded || !m.excluded))) + if (dir.includes('declarations')) { + const declarationsArray = allMethodsArray.filter(m => m.declaration[dir] && (!config.excludeDeclarations || (!options.hideExcluded || !m.excluded))) + macros.methods[dir] = declarationsArray.length ? getTemplate('/sections/declarations', templates).replace(/\$\{declaration\.list\}/g, declarationsArray.map(m => m.declaration[dir]).join('\n')) : '' + } + else if (dir.includes('methods')) { + const methodsArray = allMethodsArray.filter(m => m.body[dir] && !m.event && (!options.hideExcluded || !m.excluded)) + macros.methods[dir] = methodsArray.length ? getTemplate('/sections/methods', templates).replace(/\$\{method.list\}/g, methodsArray.map(m => m.body[dir]).join('\n')) : '' - const declarations = declarationsArray.length ? getTemplate('/sections/declarations', templates).replace(/\$\{declaration\.list\}/g, declarationsArray.map(m => m.declaration).join('\n')) : '' - const methods = methodsArray.length ? getTemplate('/sections/methods', templates).replace(/\$\{method.list\}/g, methodsArray.map(m => m.body).join('\n')) : '' - const methodList = methodsArray.filter(m => m.body).map(m => m.name) + const eventsArray = allMethodsArray.filter(m => m.body[dir] && m.event && (!options.hideExcluded || !m.excluded)) + macros.events[dir] = eventsArray.length ? getTemplate('/sections/events', templates).replace(/\$\{event.list\}/g, eventsArray.map(m => m.body[dir]).join('\n')) : '' + if (dir === 'methods') { + macros.methodList = methodsArray.filter(m => m.body).map(m => m.name) + macros.eventList = eventsArray.map(m => makeEventName(m)) + } + } + }) + + const xusesInterfaces = generateXUsesInterfaces(obj, templates) + const providerSubscribe = generateProviderSubscribe(obj, templates) const providerInterfaces = generateProviderInterfaces(obj, templates) - const events = eventsArray.length ? getTemplate('/sections/events', templates).replace(/\$\{event.list\}/g, eventsArray.map(m => m.body).join('\n')) : '' - const eventList = eventsArray.map(m => makeEventName(m)) const defaults = generateDefaults(obj, templates) - const schemasArray = generateSchemas(obj, templates, { baseUrl: '', section: 'schemas' }).filter(s => (options.copySchemasIntoModules || !s.uri)) - const accessorsArray = generateSchemas(obj, templates, { baseUrl: '', section: 'accessors' }).filter(s => (options.copySchemasIntoModules || !s.uri)) - const schemas = schemasArray.length ? getTemplate('/sections/schemas', templates).replace(/\$\{schema.list\}/g, schemasArray.map(s => s.body).filter(body => body).join('\n')) : '' - const typesArray = schemasArray.length ? schemasArray.filter(x => !x.enum) : [] - const types = (typesArray.length ? getTemplate('/sections/types', templates).replace(/\$\{schema.list\}/g, typesArray.map(s => s.body).filter(body => body).join('\n')) : '') - const accessors = (accessorsArray.length ? getTemplate('/sections/accessors', templates).replace(/\$\{schema.list\}/g, accessorsArray.map(s => s.body).filter(body => body).join('\n')) : '') + const suffix = options.destination ? options.destination.split('.').pop().trim() : '' const module = getTemplate('/codeblocks/module', templates) + const moduleInclude = getTemplate(suffix ? `/codeblocks/module-include.${suffix}` : '/codeblocks/module-include', templates) + const moduleIncludePrivate = getTemplate(suffix ? `/codeblocks/module-include-private.${suffix}` : '/codeblocks/module-include-private', templates) + const moduleInit = getTemplate(suffix ? `/codeblocks/module-init.${suffix}` : '/codeblocks/module-init', templates) - const macros = { + Object.assign(macros, { imports, initialization, - enums, - events, - eventList, eventsEnum, - methods, - methodList, - accessors, - declarations, defaults, examples, - schemas, - types, + xusesInterfaces, providerInterfaces, + providerSubscribe, version: getSemanticVersion(obj), title: obj.info.title, description: obj.info.description, module: module, + moduleInclude: moduleInclude, + moduleIncludePrivate: moduleIncludePrivate, + moduleInit: moduleInit, public: hasPublicAPIs(obj) - } + }) return macros } +const clearMacros = (fContents = '') => { + fContents = fContents.replace(/\$\{module\.includes\}/g, "") + fContents = fContents.replace(/\$\{module\.includes\.private\}/g, "") + fContents = fContents.replace(/\$\{module\.init\}/g, "") + + return fContents +} + const insertAggregateMacros = (fContents = '', aggregateMacros = {}) => { fContents = fContents.replace(/[ \t]*\/\* \$\{EXPORTS\} \*\/[ \t]*\n/, aggregateMacros.exports) fContents = fContents.replace(/[ \t]*\/\* \$\{MOCK_IMPORTS\} \*\/[ \t]*\n/, aggregateMacros.mockImports) @@ -518,30 +606,69 @@ const insertAggregateMacros = (fContents = '', aggregateMacros = {}) => { } const insertMacros = (fContents = '', macros = {}) => { - if (macros.append) { + if (macros.append && macros.module) { fContents += '\n' + macros.module } const quote = config.operators ? config.operators.stringQuotation : '"' const or = config.operators ? config.operators.or : ' | ' - fContents = fContents.replace(/\$\{if\.types\}(.*?)\$\{end\.if\.types\}/gms, macros.types.trim() ? '$1' : '') - fContents = fContents.replace(/\$\{if\.schemas\}(.*?)\$\{end\.if\.schemas\}/gms, macros.schemas.trim() ? '$1' : '') - fContents = fContents.replace(/\$\{if\.declarations\}(.*?)\$\{end\.if\.declarations\}/gms, (macros.accessors.trim() || macros.declarations.trim() || macros.enums.trim()) ? '$1' : '') - fContents = fContents.replace(/\$\{if\.definitions\}(.*?)\$\{end\.if\.definitions\}/gms, (macros.accessors.trim() || macros.methods.trim() || macros.events.trim()) ? '$1' : '') + fContents = fContents.replace(/\$\{if\.types\}(.*?)\$\{end\.if\.types\}/gms, macros.types.types.trim() ? '$1' : '') + fContents = fContents.replace(/\$\{if\.schemas\}(.*?)\$\{end\.if\.schemas\}/gms, macros.schemas.types.trim() ? '$1' : '') + fContents = fContents.replace(/\$\{if\.enums\}(.*?)\$\{end\.if\.enums\}/gms, macros.enums.types.trim() ? '$1' : '') + fContents = fContents.replace(/\$\{if\.declarations\}(.*?)\$\{end\.if\.declarations\}/gms, (macros.methods.declarations && macros.methods.declarations.trim() || macros.enums.types.trim()) || macros.types.types.trim()? '$1' : '') + + fContents = fContents.replace(/\$\{module\.list\}/g, macros.module) + fContents = fContents.replace(/\$\{module\.includes\}/g, macros.moduleInclude) + fContents = fContents.replace(/\$\{module\.includes\.private\}/g, macros.moduleIncludePrivate) + fContents = fContents.replace(/\$\{module\.init\}/g, macros.moduleInit) + + let methods = '' + Array.from(new Set(['methods'].concat(config.additionalMethodTemplates))).filter(dir => dir).every(dir => { + if (macros.methods[dir]) { + methods = macros.methods[dir] + return false + } + return true + }) + fContents = fContents.replace(/\$\{if\.methods\}(.*?)\$\{end\.if\.methods\}/gms, methods.trim() || macros.events.methods.trim() ? '$1' : '') + fContents = fContents.replace(/\$\{if\.implementations\}(.*?)\$\{end\.if\.implementations\}/gms, (methods.trim() || macros.events.methods.trim() || macros.schemas.types.trim()) ? '$1' : '') + fContents = fContents.replace(/\$\{if\.modules\}(.*?)\$\{end\.if\.modules\}/gms, (methods.trim() || macros.events.methods.trim()) ? '$1' : '') + + fContents = fContents.replace(/\$\{if\.xuses\}(.*?)\$\{end\.if\.xuses\}/gms, macros.xusesInterfaces.trim() ? '$1' : '') + fContents = fContents.replace(/\$\{if\.providers\}(.*?)\$\{end\.if\.providers\}/gms, macros.providerInterfaces.trim() ? '$1' : '') - fContents = fContents.replace(/\$\{module.list\}/g, macros.module) - fContents = fContents.replace(/[ \t]*\/\* \$\{METHODS\} \*\/[ \t]*\n/, macros.methods) - fContents = fContents.replace(/[ \t]*\/\* \$\{ACCESSORS\} \*\/[ \t]*\n/, macros.accessors.trimStart('\n')) - fContents = fContents.replace(/[ \t]*\/\* \$\{DECLARATIONS\} \*\/[ \t]*\n/, macros.declarations) + // Output the originally supported non-configurable methods & events macros + fContents = fContents.replace(/[ \t]*\/\* \$\{METHODS\} \*\/[ \t]*\n/, macros.methods.methods) fContents = fContents.replace(/[ \t]*\/\* \$\{METHOD_LIST\} \*\/[ \t]*\n/, macros.methodList.join(',\n')) - fContents = fContents.replace(/[ \t]*\/\* \$\{EVENTS\} \*\/[ \t]*\n/, macros.events) + fContents = fContents.replace(/[ \t]*\/\* \$\{EVENTS\} \*\/[ \t]*\n/, macros.events.methods) fContents = fContents.replace(/[ \t]*\/\* \$\{EVENT_LIST\} \*\/[ \t]*\n/, macros.eventList.join(',')) fContents = fContents.replace(/[ \t]*\/\* \$\{EVENTS_ENUM\} \*\/[ \t]*\n/, macros.eventsEnum) - fContents = fContents.replace(/[ \t]*\/\* \$\{SCHEMAS\} \*\/[ \t]*\n/, macros.schemas) - fContents = fContents.replace(/[ \t]*\/\* \$\{TYPES\} \*\/[ \t]*\n/, macros.types) + + // Output all declarations, methods & events with all dynamically configured templates + Array.from(new Set(['methods'].concat(config.additionalMethodTemplates))).filter(dir => dir).forEach(dir => { + ['METHODS', 'EVENTS'].forEach(type => { + const regex = new RegExp('[ \\t]*\\/\\* \\$\\{' + type + '\\:' + dir + '\\} \\*\\/[ \\t]*\\n', 'g') + fContents = fContents.replace(regex, macros[type.toLowerCase()][dir]) + }) + }) + + // Output the originally supported non-configurable schema macros + fContents = fContents.replace(/[ \t]*\/\* \$\{SCHEMAS\} \*\/[ \t]*\n/, macros.schemas.types) + fContents = fContents.replace(/[ \t]*\/\* \$\{TYPES\} \*\/[ \t]*\n/, macros.types.types) + fContents = fContents.replace(/[ \t]*\/\* \$\{ENUMS\} \*\/[ \t]*\n/, macros.enums.types) + + // Output all schemas with all dynamically configured templates + Array.from(new Set(['types'].concat(config.additionalSchemaTemplates))).filter(dir => dir).forEach(dir => { + ['SCHEMAS', 'TYPES', 'ENUMS'].forEach(type => { + const regex = new RegExp('[ \\t]*\\/\\* \\$\\{' + type + '\\:' + dir + '\\} \\*\\/[ \\t]*\\n', 'g') + fContents = fContents.replace(regex, macros[type.toLowerCase()][dir]) + }) + }) + fContents = fContents.replace(/[ \t]*\/\* \$\{PROVIDERS\} \*\/[ \t]*\n/, macros.providerInterfaces) - fContents = fContents.replace(/[ \t]*\/\* \$\{ENUMS\} \*\/[ \t]*\n/, macros.enums) + fContents = fContents.replace(/[ \t]*\/\* \$\{XUSES\} \*\/[ \t]*\n/, macros.xusesInterfaces) + fContents = fContents.replace(/[ \t]*\/\* \$\{PROVIDERS_SUBSCRIBE\} \*\/[ \t]*\n/, macros.providerSubscribe) fContents = fContents.replace(/[ \t]*\/\* \$\{IMPORTS\} \*\/[ \t]*\n/, macros.imports) fContents = fContents.replace(/[ \t]*\/\* \$\{INITIALIZATION\} \*\/[ \t]*\n/, macros.initialization) fContents = fContents.replace(/[ \t]*\/\* \$\{DEFAULTS\} \*\/[ \t]*\n/, macros.defaults) @@ -633,14 +760,12 @@ const convertEnumTemplate = (schema, templateName, templates) => { const template = getTemplate(templateName, templates).split('\n') for (var i = 0; i < template.length; i++) { if (template[i].indexOf('${key}') >= 0) { - template[i] = enumSchema.enum.map(value => { + template[i] = enumSchema.enum.map((value, id) => { const safeName = getSafeEnumKeyName(value) return template[i].replace(/\$\{key\}/g, safeName) .replace(/\$\{value\}/g, value) + .replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/g, id === enumSchema.enum.length - 1 ? '' : '$1') }).join('\n') - if (!templateName.includes(".cpp")) { - template[i] = template[i].replace(/,*$/, ''); - } } } return template.join('\n') @@ -754,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 } @@ -773,7 +898,7 @@ function generateSchemas(json, templates, options) { else { content = content.replace(/\$\{if\.description\}(.*?)\{end\.if\.description\}/gms, '$1') } - const schemaShape = types.getSchemaShape(schema, json, { name, prefix, destination: state.destination, section: options.section }) + const schemaShape = types.getSchemaShape(schema, json, { templateDir: state.typeTemplateDir, destination: state.destination, section: options.section, primitive: config.primitives ? Object.keys(config.primitives).length > 0 : false }) content = content .replace(/\$\{schema.title\}/, (schema.title || name)) @@ -791,9 +916,9 @@ function generateSchemas(json, templates, options) { else { content = content.replace(/.*\$\{schema.seeAlso\}/, '') } - content = content.trim().length ? content.trimEnd() : content.trim() + content = content.trim().length ? content : content.trim() - const isEnum = x => x.type === 'string' && Array.isArray(x.enum) && x.title + const isEnum = x => x.type && Array.isArray(x.enum) && x.title && ((x.type === 'string') || (x.type[0] === 'string')) const result = uri ? { uri: uri, @@ -837,13 +962,27 @@ function getRelatedSchemaLinks(schema = {}, json = {}, templates = {}, options = .map(path => path.substring(2).split('/')) .map(path => getPathOr(null, path, json)) .filter(schema => schema.title) - .map(schema => '[' + types.getSchemaType(schema, json, { name: schema.title, destination: state.destination, section: state.section }) + '](' + getLinkForSchema(schema, json, { name: schema.title }) + ')') // need full module here, not just the schema + .map(schema => '[' + types.getSchemaType(schema, json, { templateDir: state.typeTemplateDir, destination: state.destination, section: state.section }) + '](' + getLinkForSchema(schema, json) + ')') // need full module here, not just the schema .filter(link => link) .join('\n') return links } +function getTemplateFromDestination(destination, templateName, templates) { + const destinationArray = destination.split('/').pop().split(/[_.]+/) + + let template = '' + destinationArray.filter(value => value).every((suffix) => { + template = getTemplate(templateName +`.${suffix}`, templates) + return template ? false: true + }) + if (!template) { + template = getTemplate(templateName, templates) + } + return template +} + const generateImports = (json, templates, options = { destination: '' }) => { let imports = '' @@ -874,18 +1013,12 @@ const generateImports = (json, templates, options = { destination: '' }) => { if (methodsWithXMethodsInResult(json).length) { imports += getTemplate('/imports/x-method', templates) } - const suffix = options.destination.split('.').pop() - const prefix = options.destination.split('/').pop().split('_')[0].toLowerCase() if (callsMetrics(json).length) { - imports += getTemplate(suffix ? `/imports/calls-metrics.${suffix}` : '/imports/calls-metrics', templates) - } - - let template = prefix ? getTemplate(`/imports/default.${prefix}`, templates) : '' - if (!template) { - template = getTemplate(suffix ? `/imports/default.${suffix}` : '/imports/default', templates) + imports += getTemplateFromDestination(options.destination, '/imports/calls-metrics', templates) } + let template = getTemplateFromDestination(options.destination, '/imports/default', templates) if (json['x-schemas'] && Object.keys(json['x-schemas']).length > 0 && !json.info['x-uri-titles']) { imports += Object.keys(json['x-schemas']).map(shared => template.replace(/\$\{info.title.lowercase\}/g, shared.toLowerCase())).join('') } @@ -991,7 +1124,28 @@ function generateExamples(json = {}, mainTemplates = {}, languages = {}) { return examples } -function generateMethods(json = {}, examples = {}, templates = {}) { +function generateMethodResult(type, templates) { + const result = { + name: type, + body: {}, + declaration: {}, + } + + Array.from(new Set(['methods'].concat(config.additionalMethodTemplates))).filter(dir => dir).forEach(dir => { + const template = getTemplate(('/' + dir + '/' + type), templates) + if (template) { + if (dir.includes('declarations')) { + result.declaration[dir] = template + } + else if (dir.includes('methods')) { + result.body[dir] = template + } + } + }) + return result +} + +function generateMethods(json = {}, examples = {}, templates = {}, languages = [], type = '') { const methods = compose( option([]), getMethods @@ -1001,25 +1155,29 @@ function generateMethods(json = {}, examples = {}, templates = {}) { const results = reduce((acc, methodObj, i, arr) => { const result = { name: methodObj.name, - body: '', - declaration: '', + body: {}, + declaration: {}, excluded: methodObj.tags.find(t => t.name === 'exclude-from-sdk'), event: isEventMethod(methodObj) } - let template = getTemplateForMethod(methodObj, templates); - - if (template && template.length) { - let javascript = insertMethodMacros(template, methodObj, json, templates, examples) - result.body = javascript - } - - template = getTemplateForDeclaration(methodObj, templates) + const suffix = state.destination && config.templateExtensionMap ? state.destination.split(state.destination.includes('_') ? '_' : '.').pop() : '' - if (template && template.length) { - let javascript = insertMethodMacros(template, methodObj, json, templates, examples) - result.declaration = javascript - } + // Generate implementation of methods/events for both dynamic and static configured templates + Array.from(new Set(['methods'].concat(config.additionalMethodTemplates))).filter(dir => dir).forEach(dir => { + if (dir.includes('declarations') && (suffix && config.templateExtensionMap[dir] ? config.templateExtensionMap[dir].includes(suffix) : true)) { + const template = getTemplateForDeclaration(methodObj, templates, dir) + if (template && template.length) { + result.declaration[dir] = insertMethodMacros(template, methodObj, json, templates, '', examples) + } + } + 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, languages) + } + } + }) acc.push(result) @@ -1028,41 +1186,22 @@ function generateMethods(json = {}, examples = {}, templates = {}) { // TODO: might be useful to pass in local macro for an array with all capability & provider interface names if (json.methods && json.methods.find(isProviderInterfaceMethod)) { - results.push({ - name: "provide", - body: getTemplate('/methods/provide', templates), - declaration: getTemplate('/declarations/provide', templates), - }) + results.push(generateMethodResult('provide', templates)) } // TODO: might be useful to pass in local macro for an array with all event names if (json.methods && json.methods.find(isPublicEventMethod)) { - results.push({ - name: "listen", - body: getTemplate('/methods/listen', templates), - declaration: getTemplate('/declarations/listen', templates) - }) - - results.push({ - name: "once", - body: getTemplate('/methods/once', templates), - declaration: getTemplate('/declarations/once', templates) - }) - - results.push({ - name: "clear", - body: getTemplate('/methods/clear', templates), - declaration: getTemplate('/declarations/clear', templates) + ['listen', 'once', 'clear'].forEach(type => { + results.push(generateMethodResult(type, templates)) }) } results.sort((a, b) => a.name.localeCompare(b.name)) - return results } // 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, type = '', examples = {}, languages = {}) { const moduleName = getModuleName(json) const info = { @@ -1126,33 +1265,63 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) // grab some related methdos in case they are output together in a single template file const puller = json.methods.find(method => method.tags.find(tag => tag['x-pulls-for'] === methodObj.name)) const pullsFor = methodObj.tags.find(t => t['x-pulls-for']) && json.methods.find(method => method.name === methodObj.tags.find(t => t['x-pulls-for'])['x-pulls-for']) - const pullerTemplate = (puller ? insertMethodMacros(getTemplate('/codeblocks/puller', templates), puller, json, templates, examples) : '') + const pullerTemplate = (puller ? insertMethodMacros(getTemplate('/codeblocks/puller', templates), puller, json, templates, type, examples) : '') const setter = getSetterFor(methodObj.name, json) - const setterTemplate = (setter ? insertMethodMacros(getTemplate('/codeblocks/setter', templates), setter, json, templates, examples) : '') + const setterTemplate = (setter ? insertMethodMacros(getTemplate('/codeblocks/setter', templates), setter, json, templates, type, examples) : '') 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, examples) : '') + 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 && types.getSchemaShape(pullsResult, json, { destination: state.destination, section: state.section }) - const pullsForType = pullsResult && types.getSchemaType(pullsResult, json, { destination: state.destination, section: state.section }) - const pullsForJsonType = pullsResult ? types.getJsonType(pullsResult, json, { name: pullsResult.name }) : '' - const pullsParamsType = pullsParams ? types.getSchemaShape(pullsParams, json, { destination: state.destination, section: state.section }) : '' - const pullsForParamType = pullsParams ? types.getSchemaType(pullsParams, json, { destination: state.destination, section: state.section }) : '' - const pullsForParamJsonType = pullsParams ? types.getJsonType(pullsParams, json, { name: pullsParams.title }) : '' + const pullsResultType = (pullsResult && (type === 'methods')) ? types.getSchemaShape(pullsResult, json, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section }) : '' + const pullsForType = pullsResult && types.getSchemaType(pullsResult, json, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section }) + const pullsParamsType = (pullsParams && (type === 'methods')) ? types.getSchemaShape(pullsParams, json, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section }) : '' + const pullsForParamTitle = pullsParams ? pullsParams.title.charAt(0).toLowerCase() + pullsParams.title.substring(1) : '' + const pullsForResultTitle = pullsResult ? pullsResult.title.charAt(0).toLowerCase() + pullsResult.title.substring(1) : '' + const pullsResponseInit = (pullsParams && (type === 'methods')) ? types.getSchemaShape(pullsParams, json, { templateDir: 'result-initialization', property: pullsForParamTitle, required: pullsParams.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + const pullsResponseInst = (pullsParams && (type === 'methods')) ? types.getSchemaShape(pullsParams, json, { templateDir: 'result-instantiation', property: pullsForParamTitle, required: pullsParams.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + const pullsResultSerialize = (pullsResult && (type === 'methods')) ? types.getSchemaShape(pullsResult, json, { templateDir: 'parameter-serialization/sub-property', property: pullsForResultTitle, required: pullsResult.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + + const serializedParams = (type === 'methods') ? flattenedMethod.params.map(param => types.getSchemaShape(param.schema, json, { templateDir: 'parameter-serialization', property: param.name, required: param.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true })).join('\n') : '' + const resultInst = (type === 'methods') ? types.getSchemaShape(flattenedMethod.result.schema, json, { templateDir: 'result-instantiation', property: flattenedMethod.result.name, required: flattenedMethod.result.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' // w/out primitive: true, getSchemaShape skips anonymous types, like primitives + const resultInit = (type === 'methods') ? types.getSchemaShape(flattenedMethod.result.schema, json, { templateDir: 'result-initialization', property: flattenedMethod.result.name, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' // w/out primitive: true, getSchemaShape skips anonymous types, like primitives + const serializedEventParams = event && (type === 'methods') ? flattenedMethod.params.filter(p => p.name !== 'listen').map(param => types.getSchemaShape(param.schema, json, {templateDir: 'parameter-serialization', property: param.name, required: param.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true })).join('\n') : '' + // this was wrong... check when we merge if it was fixed + const callbackSerializedList = event && (type === 'methods') ? types.getSchemaShape(event.result.schema, json, { templateDir: eventHasOptionalParam(event) && !event.tags.find(t => t.name === 'provider') ? 'callback-serialization' : 'callback-result-serialization', property: result.name, required: event.result.schema.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + const callbackInitialization = event && (type === 'methods') ? (eventHasOptionalParam(event) && !event.tags.find(t => t.name === 'provider') ? (event.params.map(param => isOptionalParam(param) ? types.getSchemaShape(param.schema, json, { templateDir: 'callback-initialization-optional', property: param.name, required: param.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '').filter(param => param).join('\n') + '\n') : '' ) + (types.getSchemaShape(event.result.schema, json, { templateDir: 'callback-initialization', property: result.name, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true })) : '' + let callbackInstantiation = '' + if (event) { + if (eventHasOptionalParam(event) && !event.tags.find(t => t.name === 'provider')) { + callbackInstantiation = (type === 'methods') ? types.getSchemaShape(event.result.schema, json, { templateDir: 'callback-instantiation', property: result.name, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + let paramInstantiation = (type === 'methods') ? event.params.map(param => isOptionalParam(param) ? types.getSchemaShape(param.schema, json, { templateDir: 'callback-context-instantiation', property: param.name, required: param.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '').filter(param => param).join('\n') : '' + let resultInitialization = (type === 'methods') ? types.getSchemaShape(event.result.schema, json, { templateDir: 'callback-value-initialization', property: result.name, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + let resultInstantiation = (type === 'methods') ? types.getSchemaShape(event.result.schema, json, { templateDir: 'callback-value-instantiation', property: result.name, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + callbackInstantiation = callbackInstantiation + .replace(/\$\{callback\.param\.instantiation\.with\.indent\}/g, indent(paramInstantiation, ' ', 3)) + .replace(/\$\{callback\.result\.initialization\.with\.indent\}/g, indent(resultInitialization, ' ', 1)) + .replace(/\$\{callback\.result\.instantiation\}/g, resultInstantiation) + } + else { + callbackInstantiation = (type === 'methods') ? types.getSchemaShape(event.result.schema, json, { templateDir: 'callback-result-instantiation', property: result.name, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '' + } + } + // hmm... how is this different from callbackSerializedList? i guess they get merged? + const callbackResponseInst = event && (type === 'methods') ? (eventHasOptionalParam(event) ? (event.params.map(param => isOptionalParam(param) ? types.getSchemaShape(param.schema, json, { templateDir: 'callback-response-instantiation', property: param.name, required: param.required, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) : '').filter(param => param).join(', ') + ', ') : '' ) + (types.getSchemaShape(event.result.schema, json, { templateDir: 'callback-response-instantiation', property: result.name, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true })) : '' + const resultType = result.schema ? types.getSchemaType(result.schema, json, { templateDir: state.typeTemplateDir }) : '' + const resultSchemaType = result.schema.type + const resultJsonType = result.schema ? types.getSchemaType(result.schema, json, { templateDir: 'json-types' }) : '' + const resultParams = generateResultParams(result.schema, json, templates, { name: result.name}) + + // todo: what does prefix do in Types.mjs? need to account for it somehow + const callbackResultJsonType = event && result.schema ? types.getSchemaType(result.schema, json, { templateDir: 'json-types' }) : '' + + const pullsForParamType = pullsParams ? types.getSchemaType(pullsParams, json, { destination: state.destination, section: state.section }) : '' + const pullsForJsonType = pullsResult ? types.getSchemaType(pullsResult, json, { templateDir: 'json-types' }) : '' + const pullsForParamJsonType = pullsParams ? types.getSchemaType(pullsParams, json, { templateDir: 'json-types' }) : '' + const pullsEventParamName = event ? types.getSchemaInstantiation(event.result, json, event.name, { instantiationType: 'pull.param.name' }) : '' - const serializedParams = types.getSchemaInstantiation(methodObj, json, methodObj.name, { instantiationType: 'params' }) - const resultInst = types.getSchemaInstantiation(result.schema, json, result.name, { instantiationType: 'result' } ) - const serializedEventParams = event ? indent(types.getSchemaInstantiation(event, json, event.name, { instantiationType: 'params' }), ' ') : '' - const callbackSerializedParams = event ? types.getSchemaInstantiation(event, json, event.name, { instantiationType: 'callback.params', prefix: method.alternative }) : '' - const callbackResultInst = event ? types.getSchemaInstantiation(event, json, event.name, { instantiationType: 'callback.result', prefix: method.alternative }) : '' - const callbackResponseInst = event ? types.getSchemaInstantiation(event, json, event.name, { instantiationType: 'callback.response', prefix: method.alternative }) : '' - const callbackResultJsonType = event && result.schema ? types.getJsonType(result.schema, json, { name: result.name, prefix: method.alternative }) : '' - const resultType = result.schema ? types.getSchemaType(result.schema, json, { name: result.name }) : '' - const resultJsonType = result.schema ? types.getJsonType(result.schema, json, { name: result.name }) : '' - let seeAlso = '' if (isPolymorphicPullMethod(methodObj) && pullsForType) { seeAlso = `See also: [${pullsForType}](#${pullsForType.toLowerCase()}-1)` // this assumes the schema will be after the method... @@ -1165,13 +1334,29 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) if (isTemporalSetMethod(methodObj)) { itemName = result.schema.items.title || 'item' itemName = itemName.charAt(0).toLowerCase() + itemName.substring(1) - itemType = types.getSchemaType(result.schema.items, json, { destination: state.destination, section: state.section }) + itemType = types.getSchemaType(result.schema.items, json, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section }) } - template = insertExampleMacros(template, examples[methodObj.name] || [], methodObj, json, templates) + 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.title || methodObj.name) + .replace(/\$\{method\.rpc\.name\}/g, methodObj.rpc_name || methodObj.name) .replace(/\$\{method\.summary\}/g, methodObj.summary) .replace(/\$\{method\.description\}/g, methodObj.description || methodObj.summary) @@ -1185,14 +1370,18 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) .replace(/\$\{method\.params\.count}/g, methodObj.params ? methodObj.params.length : 0) .replace(/\$\{if\.params\}(.*?)\$\{end\.if\.params\}/gms, method.params.length ? '$1' : '') .replace(/\$\{if\.result\}(.*?)\$\{end\.if\.result\}/gms, resultType ? '$1' : '') + .replace(/\$\{if\.result.nonvoid\}(.*?)\$\{end\.if\.result.nonvoid\}/gms, resultType && resultType !== 'void' ? '$1' : '') + .replace(/\$\{if\.result.nonboolean\}(.*?)\$\{end\.if\.result.nonboolean\}/gms, resultSchemaType && resultSchemaType !== 'boolean' ? '$1' : '') + .replace(/\$\{if\.result\.properties\}(.*?)\$\{end\.if\.result\.properties\}/gms, resultParams ? '$1' : '') .replace(/\$\{if\.params\.empty\}(.*?)\$\{end\.if\.params\.empty\}/gms, method.params.length === 0 ? '$1' : '') .replace(/\$\{if\.signature\.empty\}(.*?)\$\{end\.if\.signature\.empty\}/gms, (method.params.length === 0 && resultType === '') ? '$1' : '') .replace(/\$\{if\.context\}(.*?)\$\{end\.if\.context\}/gms, event && event.params.length ? '$1' : '') .replace(/\$\{method\.params\.serialization\}/g, serializedParams) .replace(/\$\{method\.params\.serialization\.with\.indent\}/g, indent(serializedParams, ' ')) // Typed signature stuff - .replace(/\$\{method\.signature\}/g, types.getMethodSignature(methodObj, json, { isInterface: false, destination: state.destination, section: state.section })) + .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(', ')) .replace(/\$\{method\.context\.array\}/g, JSON.stringify(method.context)) .replace(/\$\{method\.context\.count}/g, method.context ? method.context.length : 0) @@ -1202,11 +1391,13 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) .replace(/\$\{event\.params\}/g, eventParams) .replace(/\$\{event\.params\.table\.rows\}/g, eventParamsRows) .replace(/\$\{if\.event\.params\}(.*?)\$\{end\.if\.event\.params\}/gms, event && event.params.length ? '$1' : '') + .replace(/\$\{if\.event\.callback\.params\}(.*?)\$\{end\.if\.event\.callback\.params\}/gms, event && eventHasOptionalParam(event) ? '$1' : '') .replace(/\$\{event\.signature\.params\}/g, event ? types.getMethodSignatureParams(event, json, { destination: state.destination, section: state.section }) : '') .replace(/\$\{event\.signature\.callback\.params\}/g, event ? types.getMethodSignatureParams(event, json, { destination: state.destination, section: state.section, callback: true }) : '') .replace(/\$\{event\.params\.serialization\}/g, serializedEventParams) - .replace(/\$\{event\.callback\.params\.serialization\}/g, callbackSerializedParams) - .replace(/\$\{event\.callback\.result\.instantiation\}/g, callbackResultInst) + .replace(/\$\{event\.callback\.serialization\}/g, callbackSerializedList) + .replace(/\$\{event\.callback\.initialization\}/g, callbackInitialization) + .replace(/\$\{event\.callback\.instantiation\}/g, callbackInstantiation) .replace(/\$\{event\.callback\.response\.instantiation\}/g, callbackResponseInst) .replace(/\$\{info\.title\.lowercase\}/g, info.title.toLowerCase()) .replace(/\$\{info\.title\}/g, info.title) @@ -1223,15 +1414,19 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) .replace(/\$\{method\.capabilities\}/g, capabilities) .replace(/\$\{method\.result\.name\}/g, result.name) .replace(/\$\{method\.result\.summary\}/g, result.summary) - .replace(/\$\{method\.result\.link\}/g, getLinkForSchema(result.schema, json, { name: result.name })) //, baseUrl: options.baseUrl - .replace(/\$\{method\.result\.type\}/g, types.getSchemaType(result.schema, json, { name: result.name, title: true, asPath: false, destination: state.destination, resultSchema: true })) //, baseUrl: options.baseUrl - .replace(/\$\{method\.result\.json\}/, types.getJsonType(result.schema, json, { name: result.name, destination: state.destination, section: state.section, code: false, link: false, title: true, asPath: false, expandEnums: false })) - .replace(/\$\{event\.result\.type\}/, isEventMethod(methodObj) ? types.getSchemaType(result.schema, json, { name: result.name, prefix: method.alternative, destination: state.destination, event: true, description: methodObj.result.summary, asPath: false }) : '') + .replace(/\$\{method\.result\.link\}/g, getLinkForSchema(result.schema, json)) //, baseUrl: options.baseUrl + .replace(/\$\{method\.result\.type\}/g, types.getSchemaType(result.schema, json, { templateDir: state.typeTemplateDir, title: true, asPath: false, destination: state.destination, result: true })) //, baseUrl: options.baseUrl + .replace(/\$\{method\.result\.json\}/g, types.getSchemaType(result.schema, json, { templateDir: 'json-types', destination: state.destination, section: state.section, title: true, code: false, link: false, asPath: false, expandEnums: false, namespace: true })) + // todo: what does prefix do? + .replace(/\$\{event\.result\.type\}/g, isEventMethod(methodObj) ? types.getMethodSignatureResult(event, json, { destination: state.destination, section: state.section, callback: true }) : '') + .replace(/\$\{event\.result\.json\.type\}/g, resultJsonType) .replace(/\$\{event\.result\.json\.type\}/g, callbackResultJsonType) .replace(/\$\{event\.pulls\.param\.name\}/g, pullsEventParamName) .replace(/\$\{method\.result\}/g, generateResult(result.schema, json, templates, { name: result.name })) .replace(/\$\{method\.result\.json\.type\}/g, resultJsonType) .replace(/\$\{method\.result\.instantiation\}/g, resultInst) + .replace(/\$\{method\.result\.initialization\}/g, resultInit) + .replace(/\$\{method\.result\.properties\}/g, resultParams) .replace(/\$\{method\.result\.instantiation\.with\.indent\}/g, indent(resultInst, ' ')) .replace(/\$\{method\.example\.value\}/g, JSON.stringify(methodObj.examples[0].result.value)) .replace(/\$\{method\.alternative\}/g, method.alternative) @@ -1240,10 +1435,15 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) .replace(/\$\{method\.pulls\.type\}/g, pullsForType) .replace(/\$\{method\.pulls\.json\.type\}/g, pullsForJsonType) .replace(/\$\{method\.pulls\.result\}/g, pullsResultType) + .replace(/\$\{method\.pulls\.result\.title\}/g, pullsForResultTitle) .replace(/\$\{method\.pulls\.params.type\}/g, pullsParams ? pullsParams.title : '') .replace(/\$\{method\.pulls\.params\}/g, pullsParamsType) .replace(/\$\{method\.pulls\.param\.type\}/g, pullsForParamType) - .replace(/\$\{method\.pulls\.param\.json.type\}/g, pullsForParamJsonType) + .replace(/\$\{method\.pulls\.param\.title\}/g, pullsForParamTitle) + .replace(/\$\{method\.pulls\.param\.json\.type\}/g, pullsForParamJsonType) + .replace(/\$\{method\.pulls\.response\.initialization\}/g, pullsResponseInit) + .replace(/\$\{method\.pulls\.response\.instantiation}/g, pullsResponseInst) + .replace(/\$\{method\.pulls\.result\.serialization\.with\.indent\}/g, indent(pullsResultSerialize, ' ', 3, 2)) .replace(/\$\{method\.setter\.for\}/g, setterFor) .replace(/\$\{method\.puller\}/g, pullerTemplate) // must be last!! .replace(/\$\{method\.setter\}/g, setterTemplate) // must be last!! @@ -1261,7 +1461,7 @@ function insertMethodMacros(template, methodObj, json, templates, examples = {}) const matches = [...template.matchAll(/\$\{method\.params\[([0-9]+)\]\.type\}/g)] matches.forEach(match => { const index = parseInt(match[1]) - template = template.replace(/\$\{method\.params\[([0-9]+)\]\.type\}/g, types.getSchemaType(methodObj.params[index].schema, json, { destination: state.destination })) + template = template.replace(/\$\{method\.params\[([0-9]+)\]\.type\}/g, types.getSchemaType(methodObj.params[index].schema, json, { destination: state.destination, templateDir: state.typeTemplateDir })) template = template.replace(/\$\{method\.params\[([0-9]+)\]\.name\}/g, methodObj.params[index].name) }) @@ -1366,7 +1566,7 @@ function insertExampleMacros(template, examples, method, json, templates) { function generateResult(result, json, templates, { name = '' } = {}) { - const type = types.getSchemaType(result, json, { name: name, destination: state.destination, section: state.section }) + const type = types.getSchemaType(result, json, { templateDir: state.typeTemplateDir, destination: state.destination, section: state.section }) if (result.type === 'object' && result.properties) { let content = getTemplate('/types/object', templates).split('\n') @@ -1383,17 +1583,17 @@ function generateResult(result, json, templates, { name = '' } = {}) { return insertSchemaMacros(getTemplate('/types/enum', templates), name, result, json) } else if (result.$ref) { - const link = getLinkForSchema(result, json, { name: name }) + const link = getLinkForSchema(result, json) // if we get a real link use it if (link !== '#') { - return `[${types.getSchemaType(result, json, { destination: state.destination, section: state.section })}](${link})` + return `[${types.getSchemaType(result, json, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section })}](${link})` } // otherwise this was a schema with no title, and we'll just copy it here else { const schema = localizeDependencies(result, json) return getTemplate('/types/default', templates) - .replace(/\$\{type\}/, types.getSchemaShape(schema, json, { name: result.$ref.split("/").pop() })) + .replace(/\$\{type\}/, types.getSchemaShape(schema, json, { templateDir: state.typeTemplateDir })) } } else { @@ -1401,10 +1601,56 @@ function generateResult(result, json, templates, { name = '' } = {}) { } } +function generateResultParams(result, json, templates, { name = '' } = {}) { + let moduleTitle = json.info.title + + while (result.$ref) { + if (result.$ref.includes("/x-schemas/")) { + moduleTitle = result.$ref.split("/")[2] + } + result = getJsonPath(result.$ref, json) + } + + // const results are almost certainly `"const": "null"` so there's no need to include it in the method signature + if (result.hasOwnProperty('const')) { + return '' + } + // Objects with no titles get unwrapped + else if (config.unwrapResultObjects && result.type && !result.title && result.type === 'object' && result.properties) { + const template = getTemplate('/parameters/result', templates) + return Object.entries(result.properties).map( ([name, type]) => template + .replace(/\$\{method\.param\.name\}/g, name) + .replace(/\$\{method\.param\.type\}/g, types.getSchemaType(type, json, { moduleTitle: moduleTitle, result: true, namespace: true})) + ).join(', ') // most languages separate params w/ a comma, so leaving this here for now + } + // tuples get unwrapped + else if (config.unwrapResultObjects && result.type && result.type === 'array' && Array.isArray(result.items)) { + // TODO: this is hard coded to C + const template = getTemplate('/parameters/result', templates) + return result.items.map( (type) => template + .replace(/\$\{method\.param\.name\}/g, type['x-property']) + .replace(/\$\{method\.param\.type\}/g, types.getSchemaType(type, json, { moduleTitle: moduleTitle, result: true, namespace: true})) + ).join(', ') + } + // everything else is just output as-is + else { + + const template = getTemplate('/parameters/result', templates) + const type = types.getSchemaType(result, json, { moduleTitle: moduleTitle, result: true, namespace: true}) + if (type === 'undefined') { + console.log(`Warning: undefined type for ${name}`) + } + + return template + .replace(/\$\{method\.param\.name\}/g, `${name}`) + .replace(/\$\{method\.param\.type\}/g, type) + } +} + function insertSchemaMacros(template, title, schema, module) { return template.replace(/\$\{property\}/g, title) - .replace(/\$\{type\}/g, types.getSchemaType(schema, module, { name: title, destination: state.destination, section: state.section, code: false })) - .replace(/\$\{type.link\}/g, getLinkForSchema(schema, module, { name: title })) + .replace(/\$\{type\}/g, types.getSchemaType(schema, module, { templateDir: state.typeTemplateDir, destination: state.destination, section: state.section, code: false })) + .replace(/\$\{type.link\}/g, getLinkForSchema(schema, module)) .replace(/\$\{description\}/g, schema.description || '') .replace(/\$\{name\}/g, title || '') } @@ -1414,24 +1660,27 @@ function insertParameterMacros(template, param, method, module) { //| `${method.param.name}` | ${method.param.type} | ${method.param.required} | ${method.param.summary} ${method.param.constraints} | let constraints = getSchemaConstraints(param, module) - let type = types.getSchemaType(param.schema, module, { name: param.name, destination: state.destination, section: state.section, code: false, link: false, title: true, asPath: false, expandEnums: false }) //baseUrl: options.baseUrl - let typeLink = getLinkForSchema(param.schema, module, { name: param.name }) - let jsonType = types.getJsonType(param.schema, module, { name: param.name, destination: state.destination, section: state.section, code: false, link: false, title: true, asPath: false, expandEnums: false }) + let type = types.getSchemaType(param.schema, module, { templateDir: state.typeTemplateDir, destination: state.destination, section: state.section, code: false, link: false, asPath: false, expandEnums: false }) //baseUrl: options.baseUrl + let typeLink = getLinkForSchema(param.schema, module) + let jsonType = types.getSchemaType(param.schema, module, { templateDir: 'json-types', destination: state.destination, section: state.section, code: false, link: false, asPath: false, expandEnums: false }) if (constraints && type) { constraints = '
' + 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 || '') .replace(/\$\{method.param.required\}/g, param.required || 'false') .replace(/\$\{method.param.type\}/g, type) .replace(/\$\{json.param.type\}/g, jsonType) - .replace(/\$\{method.param.link\}/g, getLinkForSchema(param.schema, module, { name: param.name })) //getType(param)) - .replace(/\$\{method.param.constraints\}/g, constraints) //getType(param)) -} + .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 = [] @@ -1452,33 +1701,135 @@ function insertCapabilityMacros(template, capabilities, method, module) { return content.join() } +function generateXUsesInterfaces(json, templates) { + let template = '' + if (hasAllowFocusMethods(json)) { + const suffix = state.destination ? state.destination.split('.').pop() : '' + template = getTemplate(suffix ? `/sections/xuses-interfaces.${suffix}` : '/sections/xuses-interfaces', templates) + if (!template) { + template = getTemplate('/sections/xuses-interfaces', templates) + } + } + return template +} + +function generateProviderSubscribe(json, templates) { + const interfaces = getProvidedCapabilities(json) + const suffix = state.destination ? state.destination.split('.').pop() : '' + let template = getTemplate(suffix ? `/sections/provider-subscribe.${suffix}` : '/sections/provider-subscribe', templates) + const providers = reduce((acc, capability) => { + const template = insertProviderSubscribeMacros(getTemplate(suffix ? `/codeblocks/provider-subscribe.${suffix}` : '/codeblocks/provider-subscribe', templates), capability, json, templates) + return acc + template + }, '', interfaces) + + return interfaces.length ? template.replace(/\$\{providers\.list\}/g, providers) : '' +} + function generateProviderInterfaces(json, templates) { const interfaces = getProvidedCapabilities(json) - let template = getTemplate('/sections/provider-interfaces', templates) + const suffix = state.destination ? state.destination.split('.').pop() : '' + + let template + if (suffix) { + template = getTemplate(`/sections/provider-interfaces.${suffix}`, templates) + } + if (!template) { + template = getTemplate('/sections/provider-interfaces', templates) + } + const providers = reduce((acc, capability) => { - const template = insertProviderInterfaceMacros(getTemplate('/codeblocks/provider', templates), capability, json, templates) + let providerTemplate = getTemplate(suffix ? `/codeblocks/provider.${suffix}` : '/codeblocks/provider', templates) + if (!providerTemplate) { + providerTemplate = getTemplate('/codeblocks/provider', templates) + } + const template = insertProviderInterfaceMacros(providerTemplate, capability, json, templates) return acc + template }, '', interfaces) return interfaces.length ? template.replace(/\$\{providers\.list\}/g, providers) : '' } -function insertProviderInterfaceMacros(template, capability, moduleJson = {}, templates) { - const iface = getProviderInterface(capability, moduleJson, { destination: state.destination, section: state.section })//.map(method => { method.name = method.name.charAt(9).toLowerCase() + method.name.substr(10); return method } ) - - const uglyName = capability.split(":").slice(-2).map(capitalize).reverse().join('') + "Provider" +function getProviderInterfaceName(iface, capability, moduleJson = {}) { + 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']) { name = moduleJson.info['x-interface-names'][capability] || name } + return name +} + +function getProviderXValues(method) { + let xValues = [] + if (method.tags.find(t => t['x-error']) || method.tags.find(t => t['x-response'])) { + method.tags.forEach(tag => { + if (tag['x-response']) { + xValues['x-response'] = tag['x-response'] + } + if (tag['x-error']) { + xValues['x-error'] = tag['x-error'] + } + }) + } + return xValues +} + +function insertProviderXValues(template, moduleJson, xValues) { + if (xValues['x-response']) { + const xResponseInst = types.getSchemaShape(xValues['x-response'], moduleJson, { templateDir: 'parameter-serialization', property: 'result', required: true, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) + template = template.replace(/\$\{provider\.xresponse\.serialization\}/gms, xResponseInst) + .replace(/\$\{provider\.xresponse\.name\}/gms, xValues['x-response'].title) + } + if (xValues['x-error']) { + const xErrorInst = types.getSchemaShape(xValues['x-error'], moduleJson, { templateDir: 'parameter-serialization', property: 'result', required: true, destination: state.destination, section: state.section, primitive: true, skipTitleOnce: true }) + template = template.replace(/\$\{provider\.xerror\.serialization\}/gms, xErrorInst) + .replace(/\$\{provider\.xerror\.name\}/gms, xValues['x-error'].title) + } + return template +} + +function insertProviderSubscribeMacros(template, capability, moduleJson = {}, templates) { + const iface = getProviderInterface(capability, moduleJson, config.extractProviderSchema) + let name = getProviderInterfaceName(iface, capability, moduleJson) - let interfaceShape = getTemplate('/codeblocks/interface', templates) + const suffix = state.destination ? state.destination.split('.').pop() : '' + template = template.replace(/\$\{subscribe\}/gms, iface.map(method => { + return insertMethodMacros(getTemplate(suffix ? `/codeblocks/subscribe.${suffix}` : '/codeblocks/subscribe', templates), method, moduleJson, templates) + }).join('\n') + '\n') + return template +} + +function insertProviderInterfaceMacros(template, capability, moduleJson = {}, templates) { + const iface = getProviderInterface(capability, moduleJson, config.extractProviderSchema) + let name = getProviderInterfaceName(iface, capability, moduleJson) + let xValues + const suffix = state.destination ? state.destination.split('.').pop() : '' + let interfaceShape = getTemplate(suffix ? `/codeblocks/interface.${suffix}` : '/codeblocks/interface', templates) + if (!interfaceShape) { + interfaceShape = getTemplate('/codeblocks/interface', templates) + } interfaceShape = interfaceShape.replace(/\$\{name\}/g, name) .replace(/\$\{capability\}/g, capability) - .replace(/[ \t]*\$\{methods\}[ \t]*\n/g, iface.map(method => `\t${types.getMethodSignature(method, moduleJson, { destination: state.destination, section: state.section, isInterface: true })}`).join('\n') + '\n') + .replace(/[ \t]*\$\{methods\}[ \t]*\n/g, iface.map(method => { + const focusable = method.tags.find(t => t['x-allow-focus']) + let interfaceDeclaration; + const interfaceTemplate = '/interfaces/' + (focusable ? 'focusable' : 'default') + if (suffix) { + interfaceDeclaration = getTemplate(`${interfaceTemplate}.${suffix}`, templates) + } + if (!interfaceDeclaration) { + interfaceDeclaration = getTemplate(interfaceTemplate, templates) + } + xValues = getProviderXValues(method) + method.tags.unshift({ + name: 'provider' + }) + + let type = config.templateExtensionMap && config.templateExtensionMap['methods'] && config.templateExtensionMap['methods'].includes(suffix) ? 'methods' : 'declarations' + return insertMethodMacros(interfaceDeclaration, method, moduleJson, templates, type) + }).join('') + '\n') if (iface.length === 0) { template = template.replace(/\$\{provider\.methods\}/gms, '') @@ -1497,7 +1848,7 @@ function insertProviderInterfaceMacros(template, capability, moduleJson = {}, te name: 'provider' }) const parametersSchema = method.params[0].schema - const parametersShape = types.getSchemaShape(parametersSchema, moduleJson, { destination: state.destination, section: state.section }) + const parametersShape = types.getSchemaShape(parametersSchema, moduleJson, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section }) let methodBlock = insertMethodMacros(getTemplateForMethod(method, templates), method, moduleJson, templates) methodBlock = methodBlock.replace(/\${parameters\.shape\}/g, parametersShape) const hasProviderParameters = parametersSchema && parametersSchema.properties && Object.keys(parametersSchema.properties).length > 0 @@ -1512,8 +1863,6 @@ function insertProviderInterfaceMacros(template, capability, moduleJson = {}, te } } methodBlock = lines.join('\n') - methodBlock = methodBlock.replace(/\$\{if\.provider\.params\}/gms, '') - methodBlock = methodBlock.replace(/\$\{end\.if\.provider\.params\}/gms, '') } else { methodBlock = methodBlock.replace(/\$\{if\.provider\.params\}.*?\$\{end\.if\.provider\.params\}/gms, '') @@ -1564,6 +1913,7 @@ function insertProviderInterfaceMacros(template, capability, moduleJson = {}, te template = template.replace(/\$\{provider\}/g, name) template = template.replace(/\$\{interface\}/g, interfaceShape) template = template.replace(/\$\{capability\}/g, capability) + template = insertProviderXValues(template, moduleJson, xValues) return template } @@ -1578,7 +1928,7 @@ function insertProviderParameterMacros(data = '', parameters, module = {}, optio Object.entries(parameters.properties).forEach(([name, param]) => { let constraints = getSchemaConstraints(param, module) - let type = types.getSchemaType(param, module, { destination: state.destination, section: state.section, code: true, link: true, title: true, asPath: options.asPath, baseUrl: options.baseUrl }) + let type = types.getSchemaType(param, module, { destination: state.destination, templateDir: state.typeTemplateDir, section: state.section, code: true, link: true, asPath: options.asPath, baseUrl: options.baseUrl }) if (constraints && type) { constraints = '
' + constraints @@ -1597,6 +1947,7 @@ function insertProviderParameterMacros(data = '', parameters, module = {}, optio export { generateMacros, + clearMacros, insertMacros, generateAggregateMacros, insertAggregateMacros @@ -1604,6 +1955,7 @@ export { export default { generateMacros, + clearMacros, insertMacros, generateAggregateMacros, insertAggregateMacros, diff --git a/src/macrofier/index.mjs b/src/macrofier/index.mjs index ce8d3d8d..298241f6 100644 --- a/src/macrofier/index.mjs +++ b/src/macrofier/index.mjs @@ -48,9 +48,17 @@ const macrofy = async ( createModuleDirectories, copySchemasIntoModules, extractSubSchemas, + unwrapResultObjects, + allocatedPrimitiveProxies, + convertTuplesToArraysOrObjects, + additionalSchemaTemplates, + additionalMethodTemplates, + templateExtensionMap, excludeDeclarations, - aggregateFile, + extractProviderSchema, + aggregateFiles, operators, + primitives, hidePrivate = true, hideExcluded = false, staticModuleNames = [], @@ -71,19 +79,28 @@ const macrofy = async ( let typer try { - const typerModule = await import(path.join(sharedTemplates, '..', 'Types.mjs')) - typer = typerModule.default +// const typerModule = await import(path.join(sharedTemplates, '..', 'Types.mjs')) +// typer = typerModule.default } catch (_) { - typer = (await import('../shared/typescript.mjs')).default +// typer = (await import('../shared/typescript.mjs')).default } + typer = (await import('./types.mjs')).default + engine.setTyper(typer) engine.setConfig({ copySchemasIntoModules, createModuleDirectories, extractSubSchemas, + unwrapResultObjects, + primitives, + allocatedPrimitiveProxies, + additionalSchemaTemplates, + additionalMethodTemplates, + templateExtensionMap, excludeDeclarations, + extractProviderSchema, operators }) @@ -92,6 +109,7 @@ const macrofy = async ( const sharedTemplateList = await readDir(sharedTemplates, { recursive: true }) const templates = Object.assign(await readFiles(sharedTemplateList, sharedTemplates), await readFiles(sdkTemplateList, template)) // sdkTemplates are second so they win ties + let templatesPermission = {} if (persistPermission) { templatesPermission = Object.assign(await readFilesPermissions(sharedTemplateList, sharedTemplates), @@ -110,16 +128,31 @@ const macrofy = async ( exampleTemplates[config.name]['__config'] = config } + // check if this is a "real" language or just documentation broiler-plate, e.g. markdown + if (Object.keys(templates).find(key => key.startsWith('/types/primitive'))) { + typer.setTemplates && typer.setTemplates(templates) + typer.setPrimitives(primitives) + } + else { + const lang = Object.entries(exampleTemplates)[0][1] + const prims = Object.entries(exampleTemplates)[0][1]['__config'].primitives + // add the templates from the first example language and the wrapper langauage + typer.setTemplates && typer.setTemplates(lang) + typer.setTemplates && typer.setTemplates(templates) + typer.setPrimitives(prims) + } + typer.setAllocatedPrimitiveProxies(allocatedPrimitiveProxies) + typer.setConvertTuples(convertTuplesToArraysOrObjects) + const staticCodeList = staticContent ? await readDir(staticContent, { recursive: true }) : [] const staticModules = staticModuleNames.map(name => ( { info: { title: name } } )) - + let modules - if (hidePrivate) { - modules = moduleList.map(name => getModule(name, openrpc, copySchemasIntoModules)).filter(hasPublicAPIs) + modules = moduleList.map(name => getModule(name, openrpc, copySchemasIntoModules, extractSubSchemas)).filter(hasPublicAPIs) } else { - modules = moduleList.map(name => getModule(name, openrpc, copySchemasIntoModules)) + modules = moduleList.map(name => getModule(name, openrpc, copySchemasIntoModules, extractSubSchemas)) } const aggregateMacros = engine.generateAggregateMacros(openrpc, modules.concat(staticModules), templates, libraryName) @@ -127,21 +160,20 @@ const macrofy = async ( const outputFiles = Object.fromEntries(Object.entries(await readFiles( staticCodeList, staticContent)) .map( ([n, v]) => [path.join(output, n), v])) - let primaryOutput + let primaryOutput = [] Object.keys(templates).forEach(file => { if (file.startsWith(path.sep + outputDirectory + path.sep) || outputDirectory === '') { // Note: '/foo/bar/file.js'.split('/') => ['', 'foo', 'bar', 'file.js'] so we need to drop one more that you might suspect, hence slice(2) below... const dirsToDrop = outputDirectory === '' ? 1 : 2 let outputFile = path.sep + file.split(path.sep).slice(dirsToDrop).join(path.sep) - const isPrimary = outputFile === aggregateFile - + const isPrimary = (aggregateFiles && aggregateFiles.includes(outputFile)) if (rename[outputFile]) { outputFile = outputFile.split(path.sep).slice(0, -1).concat([rename[outputFile]]).join(path.sep) } if (isPrimary) { - primaryOutput = path.join(output, outputFile) + primaryOutput.push(path.join(output, outputFile)) } const content = engine.insertAggregateMacros(templates[file], aggregateMacros) @@ -162,7 +194,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, createPolymorphicMethods: createPolymorphicMethods, destination: t}) + const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: t, type: 'methods'}) 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 @@ -176,15 +208,18 @@ const macrofy = async ( logSuccess(`Generated macros for module ${path.relative(output, location)}`) }) - if (primaryOutput) { - const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: primaryOutput}) + primaryOutput.forEach(output => { + const macros = engine.generateMacros(module, templates, exampleTemplates, {hideExcluded: hideExcluded, copySchemasIntoModules: copySchemasIntoModules, createPolymorphicMethods: createPolymorphicMethods, destination: output}) macros.append = append - outputFiles[primaryOutput] = engine.insertMacros(outputFiles[primaryOutput], macros) - } + outputFiles[output] = engine.insertMacros(outputFiles[output], macros) + }) append = true }) - + primaryOutput.forEach(output => { + outputFiles[output] = engine.clearMacros(outputFiles[output]); + }) + if (treeshakePattern && treeshakeEntry) { const importedFiles = (code, base) => Array.from(new Set([...code.matchAll(treeshakePattern)].map(arr => arr[2]))).map(i => path.join(output, base, i)) diff --git a/src/macrofier/types.mjs b/src/macrofier/types.mjs new file mode 100644 index 00000000..6e854d15 --- /dev/null +++ b/src/macrofier/types.mjs @@ -0,0 +1,933 @@ +/* + * Copyright 2021 Comcast Cable Communications Management, LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +import deepmerge from 'deepmerge' +import { getPath, localizeDependencies, getSafeEnumKeyName } from '../shared/json-schema.mjs' +import path from "path" + +let convertTuplesToArraysOrObjects = false +const templates = {} +const state = {} +let primitives = { + "integer": "number", + "number": "number", + "boolean": "boolean", + "string": "string" +} +const stdPrimitives = [ "integer", "number", "boolean", "string" ] + +const isVoid = type => (type === 'void') ? true : false +const isPrimitiveType = type => stdPrimitives.includes(type) ? true : false +const allocatedPrimitiveProxies = {} +const isObject = schema => (schema.type === 'object') || (Array.isArray(schema.type) && schema.type.includes("object")) +function setTemplates(t) { + Object.assign(templates, t) +} + +function setPrimitives(p) { + if (p) { + primitives = p + } +} + +function setConvertTuples(t) { + convertTuplesToArraysOrObjects = t +} + +function setAllocatedPrimitiveProxies(m) { + Object.assign(allocatedPrimitiveProxies, m) +} + +const capitalize = str => str ? str[0].toUpperCase() + str.substr(1) : str +const indent = (str, padding) => { + let first = true + if (str) { + return str.split('\n').map(line => { + if (first) { + first = false + return line + } + else { + return padding + line + } + }).join('\n') + } +} + +// TODO: This is what's left of getMethodSignatureParams. We need to figure out / handle C's `FireboltTypes_StringHandle` +function getMethodSignatureParams(method, module, { destination, callback }) { + const paramOptional = getTemplate('/parameters/optional') + let polymorphicPull = method.tags.find(t => t.name === 'polymorphic-pull') + return method.params.map(param => { + if (polymorphicPull && (param.name === 'correlationId')) { + return + } + let type = getSchemaType(param.schema, module, { destination, namespace : true }) + if (callback && allocatedPrimitiveProxies[type]) { + type = allocatedPrimitiveProxies[type] + } + + let paramRequired = '' + let jsonType = getJsonType(param.schema, module, { destination }) + if (!isPrimitiveType(jsonType) && getTemplate('/parameters/nonprimitive')) { + paramRequired = getTemplate('/parameters/nonprimitive') + } + else if ((jsonType === 'string') && getTemplate('/parameters/string')) { + paramRequired = getTemplate('/parameters/string') + } + else { + paramRequired = getTemplate('/parameters/default') + } + + if (callback) { + return (!(param.required && param.required === true) ? paramOptional.replace(/\$\{method\.param\.name\}/g, param.name).replace(/\$\{method\.param\.type\}/g, type) : '') + } + else { + return (param.required ? paramRequired : paramOptional).replace(/\$\{method\.param\.name\}/g, param.name).replace(/\$\{method\.param\.type\}/g, type) + } + }).filter(param => param).join(', ') +} + +function getMethodSignatureResult(method, module, { destination, callback }) { + let type = getSchemaType(method.result.schema, module, { destination, namespace : true }) + let result = '' + + if (callback) { + let jsonType = getJsonType(method.result.schema, module, { destination }) + + if (!isVoid(type) && !isPrimitiveType(jsonType) && getTemplate('/result-callback/nonprimitive')) { + result = getTemplate('/result-callback/nonprimitive') + } + else if ((jsonType === 'string') && getTemplate('/result-callback/string')) { + result = getTemplate('/result-callback/string') + } + else { + result = getTemplate('/result-callback/default') + } + } + else { + result = getTemplate('/result/default') + } + return result.replace(/\$\{method\.result\.type\}/g, type) +} + +const getTemplate = (name) => { + if (name[0] !== '/') { + name = '/' + name + } + return templates[Object.keys(templates).find(k => k === name)] || templates[Object.keys(templates).find(k => k.startsWith(name.split('.').shift() + '.'))] || '' +} + +const getXSchemaGroupFromProperties = (schema, title, properties, group) => { + if (properties) { + Object.entries(properties).forEach(([name, prop]) => { + if ((schema.title === prop.title) || (prop.items && prop.items.title === schema.title)) { + group = title + } + else { + group = getXSchemaGroupFromProperties(schema, title, prop.properties, group) + } + }) + } + return group +} + +// TODO: this assumes the same title doesn't exist in multiple x-schema groups! +const getXSchemaGroup = (schema, module) => { + let group = module.info.title + + if (schema.title && module['x-schemas']) { + Object.entries(module['x-schemas']).forEach(([title, module]) => { + Object.values(module).forEach(moduleSchema => { + let schemas = moduleSchema.allOf ? moduleSchema.allOf : [moduleSchema] + schemas.forEach((s) => { + if (schema.title === s.title || schema.title === moduleSchema.title) { + group = title + } else { + group = getXSchemaGroupFromProperties(schema, title, s.properties, group) + } + }) + }) + }) + } + return group +} + +function getSchemaDescription(schema, module) { + let description = schema.description || '' + if (schema.type === 'array' && schema.items) { + schema = schema.items + } + if (schema['$ref'] && (schema['$ref'][0] === '#')) { + const refSchema = getPath(schema['$ref'], module) + description = (refSchema && refSchema.description) || description + } + return description +} + +function insertSchemaMacros(content, schema, module, { name = '', parent = '', property = '', required = false, recursive = true, templateDir = 'types'}) { + const title = name || schema.title || '' + const moduleTitle = getXSchemaGroup(schema, module) + const description = getSchemaDescription(schema, module) + + content = content + .replace(/\$\{title\}/g, title) + .replace(/\$\{Title\}/g, capitalize(title)) + .replace(/\$\{TITLE\}/g, title.toUpperCase()) + .replace(/\$\{property\}/g, property) + .replace(/\$\{Property\}/g, capitalize(property)) + .replace(/\$\{if\.namespace\.notsame}(.*?)\$\{end\.if\.namespace\.notsame\}/g, (module.info.title !== (parent || moduleTitle)) ? '$1' : '') + .replace(/\$\{parent\.title\}/g, parent || moduleTitle) + .replace(/\$\{parent\.Title\}/g, capitalize(parent || moduleTitle)) + .replace(/\$\{description\}/g, description) + .replace(/\$\{if\.optional\}(.*?)\$\{end\.if\.optional\}/gms, (Array.isArray(required) ? required.includes(property) : required) ? '' : '$1') + .replace(/\$\{if\.non.optional\}(.*?)\$\{end\.if\.non.optional\}/gms, (Array.isArray(required) ? required.includes(property) : required) ? '$1' : '') + .replace(/\$\{if\.summary\}(.*?)\$\{end\.if\.summary\}/gms, description ? '$1' : '') + .replace(/\$\{summary\}/g, description ? description.split('\n')[0] : '') + .replace(/\$\{name\}/g, title) + .replace(/\$\{NAME\}/g, title.toUpperCase()) + .replace(/\$\{info.title\}/g, moduleTitle) + .replace(/\$\{info.Title\}/g, capitalize(moduleTitle)) + .replace(/\$\{info.TITLE\}/g, moduleTitle.toUpperCase()) + + if (recursive) { + content = content.replace(/\$\{type\}/g, getSchemaType(schema, module, { templateDir: templateDir, destination: state.destination, section: state.section, code: false, namespace: true })) + } + return content +} + +// TODO using JSON.stringify probably won't work for many languages... +const insertConstMacros = (content, schema, module, name) => { + content = content.replace(/\$\{value\}/g, (typeof schema.const === 'string' ? `'${schema.const}'` : schema.const)) + return content +} + +const insertEnumMacros = (content, schema, module, name, suffix, templateDir = "types") => { + const template = content.split('\n') + + for (var i = 0; i < template.length; i++) { + if (template[i].indexOf('${key}') >= 0) { + let values = [] + schema.enum.map(value => { + if (!value) { + value = getTemplate(path.join(templateDir, 'unset' + suffix)) + } + value ? values.push(template[i].replace(/\$\{key\}/g, getSafeEnumKeyName(value)) + .replace(/\$\{value\}/g, value)) : '' + }) + template[i] = values.map((value, id) => { + return value.replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/g, id === values.length - 1 ? '' : '$1') + }).join('\n') + } + } + + return template.join('\n') +} + +const insertObjectAdditionalPropertiesMacros = (content, schema, module, title, options) => { + const options2 = options ? JSON.parse(JSON.stringify(options)) : {} + options2.parent = title + options2.level = options.level + 1 + options2.required = options.required + const shape = getSchemaShape(schema.additionalProperties, module, options2) + let type = getSchemaType(schema.additionalProperties, module, options2).trimEnd() + const propertyNames = localizeDependencies(schema, module).propertyNames + + let jsonType = getJsonType(schema.additionalProperties, module) + if (!isPrimitiveType(jsonType)) { + jsonType = 'string' + } + + const additionalType = getPrimitiveType(jsonType, 'additional-types') + + let namespace = '' + let defaultKey = true + let key = getTemplate(path.join('types', 'additionalPropertiesKey')).trimEnd() + if (propertyNames && propertyNames.title) { + let parent = getXSchemaGroup(propertyNames, module) + key = propertyNames.title + namespace = getTemplate(path.join(options.templateDir, 'namespace')) + .replace(/\$\{if\.namespace\.notsame}(.*?)\$\{end\.if\.namespace\.notsame\}/g, (module.info.title !== (parent || moduleTitle)) ? '$1' : '') + .replace(/\$\{parent\.Title\}/g, (parent && module.info.title !== parent) ? parent : '') + defaultKey = false + } + content = content + .replace(/\$\{shape\}/g, shape) + .replace(/\$\{if\.default\}(.*?)\$\{end\.if\.default\}/g, defaultKey ? '$1' : '') + .replace(/\$\{if\.not.default\}(.*?)\$\{end\.if\.not.default\}/gms, defaultKey ? '' : '$1') + .replace(/\$\{parent\.title\}/g, title) + .replace(/\$\{title\}/g, title) + .replace(/\$\{type\}/g, type) + .replace(/\$\{additional\.type\}/g, additionalType) + .replace(/\$\{key\}/g, key) + .replace(/\$\{namespace\}/g, namespace) + .replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/g, '') + .replace(/\$\{if\.optional\}(.*?)\$\{end\.if\.optional\}/g, '') + .replace(/\$\{if\.impl.optional\}(.*?)\$\{end\.if\.impl.optional\}/g, options.required ? '' : '$1') + .replace(/\$\{if\.impl.non.optional\}(.*?)\$\{end\.if\.impl.non.optional\}/g, options.required ? '$1' : '') + + return content +} + +const insertObjectPatternPropertiesMacros = (content, schema, module, title, options) => { + const options2 = options ? JSON.parse(JSON.stringify(options)) : {} + options2.parent = title + options2.level = options.level + 1 + options2.required = options.required + + let patternSchema; + Object.entries(schema.patternProperties).forEach(([pattern, sch]) => { + patternSchema = sch + }) + + if (patternSchema) { + const shape = getSchemaShape(patternSchema, module, options2) + let type = getSchemaType(patternSchema, module, options2).trimEnd() + const propertyNames = localizeDependencies(schema, module).propertyNames + + content = content + .replace(/\$\{shape\}/g, shape) + .replace(/\$\{parent\.title\}/g, title) + .replace(/\$\{title\}/g, title) + .replace(/\$\{type\}/g, type) + .replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/g, '') + .replace(/\$\{if\.optional\}(.*?)\$\{end\.if\.optional\}/g, '') + .replace(/\$\{if\.impl.optional\}(.*?)\$\{end\.if\.impl.optional\}/g, options.required ? '' : '$1') + .replace(/\$\{if\.impl.non.optional\}(.*?)\$\{end\.if\.impl.non.optional\}/g, options.required ? '$1' : '') + } + + return content +} + +const getIndents = level => level ? ' ' : '' +const insertObjectMacros = (content, schema, module, title, property, options) => { + const options2 = options ? JSON.parse(JSON.stringify(options)) : {} + options2.parent = title + options2.parentLevel = options.parentLevel + options2.level = options.level + 1 + options2.templateDir = options.templateDir + ;(['properties', 'properties.register', 'properties.assign']).forEach(macro => { + const indent = getIndents(options.parentLevel || (options.level ? 1 : 0)) + const templateType = macro.split('.').slice(1).join('') + const template = getTemplate(path.join(options.templateDir, 'property' + (templateType ? `-${templateType}` : ''))).replace(/\n/gms, '\n' + indent) + const properties = [] + if (schema.properties) { + Object.entries(schema.properties).forEach(([name, prop], i) => { + let localizedProp = localizeDependencies(prop, module) + const subProperty = getTemplate(path.join(options2.templateDir, 'sub-property/object')) + options2.templateDir += subProperty ? '/sub-property' : '' + const objSeparator = getTemplate(path.join(options2.templateDir, 'object-separator')) + if (localizedProp.type === 'array' || localizedProp.anyOf || localizedProp.oneOf || (typeof localizedProp.const === 'string')) { + options2.property = name + options2.required = schema.required + } else { + options2.property = options.property + options2.required = schema.required && schema.required.includes(name) + } + const schemaShape = indent + getSchemaShape(localizedProp, module, options2).replace(/\n/gms, '\n' + indent) + const type = getSchemaType(localizedProp, module, options2) + // don't push properties w/ unsupported types + if (type) { + const description = getSchemaDescription(prop, module) + let replacedTemplate = template + .replace(/(^\s+)/g, '$1'.repeat(options2.level)) + .replace(/\$\{property\}/g, name) + .replace(/\$\{Property\}/g, capitalize(name)) + .replace(/\$\{parent\.title\}/g, title) + .replace(/\$\{title\}/g, type) + .replace(/\$\{shape\}/g, schemaShape) + .replace(/\$\{description\}/g, description) + .replace(/\$\{if\.summary\}(.*?)\$\{end\.if\.summary\}/gms, description ? '$1' : '') + .replace(/\$\{summary\}/g, description ? description.split('\n')[0] : '') + .replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/gms, i === schema.properties.length - 1 ? '' : '$1') + .replace(/\$\{if\.optional\}(.*?)\$\{end\.if\.optional\}/gms, ((schema.required && schema.required.includes(name)) || (localizedProp.required && localizedProp.required === true)) ? '' : '$1') + .replace(/\$\{if\.non.optional\}(.*?)\$\{end\.if\.non.optional\}/gms, ((schema.required && schema.required.includes(name)) || (localizedProp.required && localizedProp.required === true)) ? '$1' : '') + .replace(/\$\{if\.base\.optional\}(.*?)\$\{end\.if\.base\.optional\}/gms, options.required ? '' : '$1') + .replace(/\$\{if\.non\.object\}(.*?)\$\{end\.if\.non\.object\}/gms, isObject(localizedProp) ? '' : '$1') + .replace(/\$\{if\.non\.array\}(.*?)\$\{end\.if\.non\.array\}/gms, (localizedProp.type === 'array') ? '' : '$1') + .replace(/\$\{if\.non\.anyOf\}(.*?)\$\{end\.if\.non\.anyOf\}/gms, (localizedProp.anyOf || localizedProp.anyOneOf) ? '' : '$1') + .replace(/\$\{if\.non\.const\}(.*?)\$\{end\.if\.non\.const\}/gms, (typeof localizedProp.const === 'string') ? '' : '$1') + let baseTitle = options.property + + if (isObject(localizedProp)) { + replacedTemplate = replacedTemplate + .replace(/\$\{if\.impl.optional\}(.*?)\$\{end\.if\.impl.optional\}/gms, ((schema.required && schema.required.includes(name)) || (localizedProp.required && localizedProp.required === true)) ? '' : '$1') + .replace(/\$\{if\.impl.non.optional\}(.*?)\$\{end\.if\.impl.non.optional\}/gms, ((schema.required && schema.required.includes(name)) || (localizedProp.required && localizedProp.required === true)) ? '$1' : '') + .replace(/\$\{property.dependency\}/g, ((options.level > 0) ? '${property.dependency}${if.impl.optional}.value()${end.if.impl.optional}' : '') + objSeparator + name) + .replace(/\$\{Property.dependency\}/g, ((options.level > 0) ? '${Property.dependency}' : '') + (objSeparator) + capitalize(name)) + } + else { + if (options2.level <= 1) { + replacedTemplate = replacedTemplate + .replace(/\$\{property.dependency\}/g, '') + .replace(/\$\{Property.dependency\}/g, '') + .replace(/\$\{if\.impl.optional\}(.*?)\$\{end\.if\.impl.optional\}/gms, '') + } + } + replacedTemplate = replacedTemplate + .replace(/\$\{obj\.separator}/g, objSeparator) + .replace(/\$\{base.title\}/g, (baseTitle ? (baseTitle)[0].toLowerCase() + (baseTitle).substr(1) : '')).trimEnd() + .replace(/\$\{base.Title\}/g, (baseTitle ? (baseTitle)[0].toUpperCase() + (baseTitle).substr(1) : '')).trimEnd() + properties.push(replacedTemplate) + } + }) + } + + if (schema.propertyNames) { + const { propertyNames } = localizeDependencies(schema, module) + if (propertyNames.enum) { + propertyNames.enum.forEach((prop, i) => { + if (schema.properties && schema.properties[prop]) { + // skip properties that were already defined above + return + } + // TODO: add language config feature for 'unknown' type + let type; // = { type: "null" } + + if (schema.additionalProperties && (typeof schema.additionalProperties === 'object')) { + type = schema.additionalProperties + } + + if (schema.patternProperties) { + Object.entries(schema.patternProperties).forEach(([pattern, schema]) => { + let regex = new RegExp(pattern) + if (prop.match(regex)) { + type = schema + } + }) + } + + if (type) { + options2.property = prop + const schemaShape = getSchemaShape(type, module, options2) + const description = getSchemaDescription(prop, module) + properties.push(template + .replace(/\$\{property\}/g, getSafeEnumKeyName(prop)) + .replace(/\$\{Property\}/g, capitalize(getSafeEnumKeyName(prop))) + .replace(/\$\{parent\.title\}/g, title) + .replace(/\$\{title\}/g, getSchemaType(type, module, options2)) + .replace(/\$\{shape\}/g, schemaShape) + .replace(/\$\{description\}/g, description) + .replace(/\$\{if\.summary\}(.*?)\$\{end\.if\.summary\}/gms, description ? '$1' : '') + .replace(/\$\{summary\}/g, description ? description.split('\n')[0] : '') + .replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/gms, i === propertyNames.enum.length - 1 ? '' : '$1') + .replace(/\$\{if\.optional\}(.*?)\$\{end\.if\.optional\}/gms, schema.required && schema.required.includes(prop) ? '' : '$1') + .replace(/\$\{if\.non.optional\}(.*?)\$\{end\.if\.non.optional\}/gms, schema.required && schema.required.includes(prop) ? '$1' : '')) + } + }) + } + } + + const regex = new RegExp("\\$\\{" + macro + "\\}", "g") + content = content.replace(regex, properties.join('\n')).replace(/\$\{level}/g, options.parentLevel > 0 ? options.parentLevel : '') + if (!schema.properties && !schema.additionalProperties) { + if (schema.propertyNames && schema.propertyNames.enum) { + content = getTemplate(path.join(options.templateDir, 'enum-empty-property')) + } + else { + content = getTemplate(path.join(options.templateDir, 'object-empty-property')) + } + } + }) + return content +} + +const insertArrayMacros = (content, schema, module, level = 0, items, required = false) => { + content = content + .replace(/\$\{json\.type\}/g, getSchemaType(schema.items, module, { templateDir: 'json-types', destination: state.destination, section: state.section, code: false, namespace: true })) + .replace(/\$\{items\}/g, items) + .replace(/\$\{items\.with\.indent\}/g, required ? indent(items, ' ') : indent(items, ' ')) + .replace(/\$\{if\.impl.array.optional\}(.*?)\$\{end\.if\.impl.array.optional\}/gms, required ? '' : '$1') + .replace(/\$\{if\.impl.array.non.optional\}(.*?)\$\{end\.if\.impl.array.non.optional\}/gms, required ? '$1' : '') + + return content +} + +const insertTupleMacros = (content, schema, module, title, options) => { + options.level = options.level + 1 + options.name = '' + + const propTemplate = getTemplate(path.join(options.templateDir, 'property')) + const itemsTemplate = getTemplate(path.join(options.templateDir, 'items')) + const propIndent = (content.split('\n').find(line => line.includes("${properties}")) || '').match(/^\s+/) || [''][0] + const itemsIndent = (content.split('\n').find(line => line.includes("${items}")) || '').match(/^\s+/) || [''][0] + const tupleDelimiter = getTemplate(path.join(options.templateDir, 'tuple-delimiter')) + + const doMacroWork = (str, prop, i, indent) => { + const schemaShape = getSchemaShape(prop, module, options) + const description = getSchemaDescription(prop, module) + + return (i !== 0 ? indent : '') + str + .replace(/\$\{property\}/g, prop['x-property']) + .replace(/\$\{Property\}/g, capitalize(prop['x-property'])) + .replace(/\$\{parent\.title\}/g, title) + .replace(/\$\{shape\}/g, schemaShape) + .replace(/\$\{title\}/g, getSchemaType(prop, module, options)) + .replace(/\$\{description\}/g, description) + .replace(/\$\{if\.summary\}(.*?)\$\{end\.if\.summary\}/gms, description ? '$1' : '') + .replace(/\$\{summary\}/g, description ? description.split('\n')[0] : '') + .replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/g, i === schema.items.length - 1 ? '' : '$1') + .replace(/\$\{if\.optional\}(.*?)\$\{end\.if\.optional\}/gms, '') + .replace(/\$\{if\.impl.optional\}(.*?)\$\{end\.if\.impl.optional\}/gms, '') + } + + content = content.replace(/\$\{properties\}/g, schema.items.map((prop, i) => doMacroWork(propTemplate, prop, i, propIndent)).join(tupleDelimiter)) + content = content.replace(/\$\{items\}/g, schema.items.map((prop, i) => doMacroWork(itemsTemplate, prop, i, itemsIndent)).join(tupleDelimiter)) + content = content.replace(/\$\{json\.type\}/g, getSchemaType(schema.items[0], module, { templateDir: 'json-types', destination: state.destination, section: state.section, code: false, namespace: true })) + return content +} + +const getPrimitiveType = (type, templateDir = 'types', title = false) => { + const template = getTemplate(path.join(templateDir, type)) || getTemplate(path.join(templateDir, 'generic')) + return (title ? template : primitives[type] || template) +} + +const pickBestType = types => Array.isArray(types) ? types.find(t => t !== 'null') : types + +const insertPrimitiveMacros = (content, schema, module, name, templateDir) => { + content = content.replace(/\$\{type\}/g, getPrimitiveType(pickBestType(schema.type), templateDir)) + return content +} + +const insertAnyOfMacros = (content, schema, module, name) => { + const itemTemplate = content + if (content.split('\n').find(line => line.includes("${type}"))) { + content = schema.anyOf.map((item, i) => itemTemplate + .replace(/\$\{type\}/g, getSchemaType(item, module)) + .replace(/\$\{delimiter\}(.*?)\$\{end.delimiter\}/g, i === schema.anyOf.length - 1 ? '' : '$1') + ).join('') + } + + return content +} + +const sanitize = (schema) => { + const result = JSON.parse(JSON.stringify(schema)) + + if (result.oneOf && result.oneOf.length === 2 && result.oneOf.find(s => s.const === null)) { + Object.assign(result, result.oneOf.find(s => s.const !== null)) + delete result.oneOf + } + + if (result.anyOf && result.anyOf.length === 2 && result.anyOf.find(s => s.const === null)) { + Object.assign(result, result.anyOf.find(s => s.const !== null)) + delete result.anyOf + } + + return result +} + +function getSchemaShape(schema = {}, module = {}, { templateDir = 'types', parent = '', property = '', required = false, parentLevel = 0, level = 0, summary, descriptions = true, destination, section, enums = true, skipTitleOnce = false, array = false, primitive = false, type = false } = {}) { + schema = sanitize(schema) + state.destination = destination + state.section = section + if (level === 0 && !schema.title && !primitive) { + return '' + } + + const suffix = destination && ('.' + destination.split('.').pop()) || '' + const theTitle = insertSchemaMacros(getTemplate(path.join(templateDir, 'title' + suffix)), schema, module, { name: schema.title, parent, property, required, recursive: false }) + + let result = getTemplate(path.join(templateDir, 'default' + suffix)) || '${shape}' + + let genericTemplate = getTemplate(path.join(templateDir, 'generic' + suffix)) + if (enums && level === 0 && Array.isArray(schema.enum) && ((schema.type === "string") || (schema.type[0] === "string"))) { + result = getTemplate(path.join(templateDir, 'enum' + suffix)) || genericTemplate + return insertSchemaMacros(insertEnumMacros(result, schema, module, theTitle, suffix, templateDir), schema, module, { name: theTitle, parent, property, required }) + } + + if (schema['$ref']) { + const someJson = getPath(schema['$ref'], module) + if (someJson) { + return getSchemaShape(someJson, module, { templateDir, parent, property, required, parentLevel, level, summary, descriptions, destination, enums, array, primitive }) + } + throw "Unresolvable $ref: " + schema['ref'] + ", in " + module.info.title + } + else if (schema.hasOwnProperty('const')) { + const shape = insertConstMacros(getTemplate(path.join(templateDir, 'const' + suffix)) || genericTemplate, schema, module, theTitle) + return insertSchemaMacros(result.replace(/\$\{shape\}/g, shape), schema, module, { name: theTitle, parent, property, required }) + } + else if (!skipTitleOnce && (level > 0) && schema.title) { + let enumType = (schema.type === 'string' && Array.isArray(schema.enum)) + // TODO: allow the 'ref' template to actually insert the shape using getSchemaShape + const innerShape = getSchemaShape(schema, module, { skipTitleOnce: true, templateDir, parent, property, required, parentLevel, level, summary, descriptions, destination, enums: enumType, array, primitive }) + + const shape = getTemplate(path.join(templateDir, 'ref' + suffix)) + .replace(/\$\{shape\}/g, innerShape) + + result = result.replace(/\$\{shape\}/g, shape) + return insertSchemaMacros(result, schema, module, { name: theTitle, parent, property, required }) + } + else if (isObject(schema)) { + let shape + const additionalPropertiesTemplate = getTemplate(path.join(templateDir, 'additionalProperties')) + if (additionalPropertiesTemplate && schema.additionalProperties && (typeof schema.additionalProperties === 'object')) { + shape = insertObjectAdditionalPropertiesMacros(additionalPropertiesTemplate, schema, module, theTitle, { level, parent, templateDir, namespace: true, required }) + } + else { + const patternPropertiesTemplate = getTemplate(path.join(templateDir, 'patternProperties')) + if (patternPropertiesTemplate && schema.patternProperties) { + shape = insertObjectPatternPropertiesMacros(patternPropertiesTemplate, schema, module, theTitle, { level, parent, templateDir, namespace: true, required }) + } else { + let objectLevel = array ? 0 : level + shape = insertObjectMacros(getTemplate(path.join(templateDir, 'object' + (array ? '-array' : '') + suffix)) || genericTemplate, schema, module, theTitle, property, { parentLevel, level: objectLevel, parent, property, required, templateDir, descriptions, destination, section, enums, namespace: true, primitive }) + } + } + result = result.replace(/\$\{shape\}/g, shape) + if (level === 0) { + result = result.replace(/\$\{if\.impl.optional\}(.*?)\$\{end\.if\.impl.optional\}/gms, (Array.isArray(required) ? required.includes(property) : required) ? '' : '$1') + result = result.replace(/\$\{if\.impl.non.optional\}(.*?)\$\{end\.if\.impl.non.optional\}/gms, (Array.isArray(required) ? required.includes(property) : required) ? '$1' : '') + } + return insertSchemaMacros(result, schema, module, { name: theTitle, parent, property, required, templateDir }) + } + else if (schema.anyOf || schema.oneOf) { + const template = getTemplate(path.join(templateDir, 'anyOfSchemaShape' + suffix)) + let shape + if (template) { + shape = insertAnyOfMacros(template, schema, module, theTitle) + } + else { + // borrow anyOf logic, note that schema is a copy, so we're not breaking it. + if (!schema.anyOf) { + schema.anyOf = schema.oneOf + } + shape = insertAnyOfMacros(getTemplate(path.join(templateDir, 'anyOf' + suffix)) || genericTemplate, schema, module, theTitle) + } + if (shape) { + result = result.replace(/\$\{shape\}/g, shape) + return insertSchemaMacros(result, schema, module, { name: theTitle, parent, property, required }) + } + else { + return '' + } + } + else if (schema.allOf) { + const merger = (key) => function (a, b) { + if (a.const) { + return JSON.parse(JSON.stringify(a)) + } + else if (b.const) { + return JSON.parse(JSON.stringify(b)) + } + else { + return deepmerge(a, b, { customMerge: merger }) + } + } + + let union = deepmerge.all([...schema.allOf.map(x => x['$ref'] ? getPath(x['$ref'], module) || x : x).reverse()], { + customMerge: merger + }) + + if (schema.title) { + union.title = schema.title + } + delete union['$ref'] + + return getSchemaShape(union, module, { templateDir, parent, property, required, parentLevel, level, summary, descriptions, destination, enums: false, array, primitive }) + } + else if (schema.type === "array" && schema.items && isSupportedTuple(schema)) { + // tuple + const shape = insertTupleMacros(getTemplate(path.join(templateDir, 'tuple' + suffix)) || genericTemplate, schema, module, theTitle, { level, templateDir, descriptions, destination, section, enums }) + result = result.replace(/\$\{shape\}/g, shape) + return insertSchemaMacros(result, schema, module, { name: theTitle, parent, property, required, templateDir }) + } + else if (schema.type === "array" && schema.items && !Array.isArray(schema.items)) { + // array + const items = getSchemaShape(schema.items, module, { templateDir, parent, property, required, parentLevel: parentLevel + 1, level, summary, descriptions, destination, enums: false, array: true, primitive }) + const shape = insertArrayMacros(getTemplate(path.join(templateDir, 'array' + suffix)) || genericTemplate, schema, module, level, items, Array.isArray(required) ? required.includes(property) : required) + result = result.replace(/\$\{shape\}/g, shape) + .replace(/\$\{if\.object\}(.*?)\$\{end\.if\.object\}/gms, isObject(schema.items) ? '$1' : '') + .replace(/\$\{if\.non\.object\}(.*?)\$\{end\.if\.non\.object\}/gms, (schema.items.type !== 'object') ? '$1' : '') + return insertSchemaMacros(result, schema, module, { name: items, parent, property, required, templateDir }) + } + else if (schema.type) { + const shape = insertPrimitiveMacros(getTemplate(path.join(templateDir, 'primitive' + suffix)), schema, module, theTitle, templateDir) + result = result.replace(/\$\{shape\}/g, shape) + if (level > 0 || primitive) { + return insertSchemaMacros(result, schema, module, { name: theTitle, parent, property, required, templateDir }) + } + } + + return '' +} + +const isHomogenous = schema => { + if (schema.items && Array.isArray(schema.items)) { + // all items have a type and they are all the same + if (schema.items.length === 0) { + return true + } + else if (schema.items.every(item => item.type) && schema.items.every(item => item.type === schema.items[0].type)) { + return true + } + else if (schema.items.every(item => item.$ref) && schema.items.every(item => item.$ref === schema.items[0].$ref)) { + return true + } + else { + return false + } + } + return true +} + +const isTuple = schema => schema.items && Array.isArray(schema.items) + +const isSupportedTuple = schema => { + + if (schema.items && Array.isArray(schema.items)) { + // if the convert flag isn't set, then all tuples are supported + if (!convertTuplesToArraysOrObjects) { + return true + } + else { + // if every item has an `x-property` extension, then this tuple is supported (tuple template can use ${property}) + if (schema.items.every(item => item['x-property'])) { + return true + } + // For homogenous tuples just treat them as arrays (i.e. not tuples) + else if (isHomogenous(schema)) { + console.log(`Treating homogenous tuple as array ${schema.items.map(item => item.type||item.$ref).join(' | ')}: ${convertTuplesToArraysOrObjects}`) + return false + } + else { + console.log(`Warning: non-homogenous tuples not supported (schema: ${schema.title})`) + } + } + } + else { + return false + } +} + +function getSchemaType(schema, module, { destination, templateDir = 'types', link = false, code = false, asPath = false, event = false, result = false, expandEnums = true, baseUrl = '', namespace = false } = {}) { + const wrap = (str, wrapper) => wrapper + str + wrapper + + schema = sanitize(schema) + + const suffix = destination && ('.' + destination.split('.').pop()) || '' + const namespaceStr = namespace ? getTemplate(path.join(templateDir, 'namespace' + suffix)) : '' + const theTitle = insertSchemaMacros(namespaceStr + getTemplate(path.join(templateDir, 'title' + suffix)), schema, module, { name: schema.title, parent: getXSchemaGroup(schema, module), recursive: false }) + const allocatedProxy = event || result + + const title = schema.type === "object" || schema.anyOf || schema.oneOf || Array.isArray(schema.type) && schema.type.includes("object") || schema.enum ? true : false + + if (schema['$ref']) { + if (schema['$ref'][0] === '#') { + const refSchema = getPath(schema['$ref'], module) + const includeNamespace = (module.info.title !== getXSchemaGroup(refSchema, module)) + return getSchemaType(refSchema, module, {destination, templateDir, link, code, asPath, event, result, expandEnums, baseUrl, namespace:includeNamespace })// { link: link, code: code, destination }) + } + else { + // TODO: This never happens... but might be worth keeping in case we link to an opaque external schema at some point? + + if (link) { + return '[' + wrap(theTitle, code ? '`' : '') + '](' + schema['$ref'] + ')' + } + else { + return wrap(theTitle, code ? '`' : '') + } + } + } + else if (title && schema.title) { + if (link) { + return '[' + wrap(theTitle, code ? '`' : '') + '](#' + schema.title.toLowerCase() + ')' + } + else { + return wrap(theTitle, code ? '`' : '') + } + } + else if (schema.const) { + return insertConstMacros(getTemplate(path.join(templateDir, 'const' + suffix)), schema, module) + } + else if (schema['x-method']) { + const target = JSON.parse(JSON.stringify(module.methods.find(m => m.name === schema['x-method'].split('.').pop()))) + + // transform the method copy params to be in the order of the x-additional-params array (and leave out any we don't want) + if (schema['x-additional-params']) { + const params = [] + schema['x-additional-params'].forEach(key => { + params.push(target.params.find(p => p.name === key)) + }) + target.params = params + } + else { + target.params = [] + } + + const params = getMethodSignatureParams(target, module, { destination }) + const template = getTemplate(path.join(templateDir, 'x-method')) + return insertSchemaMacros(template.replace(/\$\{params\}/g, params), target.result.schema, module, { name: theTitle, recursive: false }) + } + else if (schema.type === 'string' && schema.enum) { + let type = expandEnums ? schema.enum.map(e => wrap(e, '\'')).join(' | ') : schema.type + if (code) { + type = wrap(type, '`') + } + return type + } + // else if (schema.type === 'array' && Array.isArray(schema.items)) { + // // tuple + // } + else if ((schema.type === 'object' || (schema.type === 'array')) && schema.title) { + const maybeGetPath = (path, json) => { + try { + return getPath(path, json) + } + catch (e) { + return null + } + } + + const def = maybeGetPath('#/definitions/' + schema.title, module) || maybeGetPath('#/components/schemas/' + schema.title, module) + + if (def && link) { + return '[' + wrap(theTitle, code ? '`' : '') + '](./' + '#' + schema.title.toLowerCase() + ')' + } + else { + return wrap(theTitle, code ? '`' : '') + } + } + else if (schema.type === 'array' && schema.items) { + let firstItem + if (Array.isArray(schema.items)) { + if (!isHomogenous(schema.items)) { + console.log(`Non-homogenous tuples not supported: ${schema.items} in ${module.info.title}, ${theTitle}`) + return '' + } + firstItem = schema.items[0] + + // let type = '[' + schema.items.map(x => getSchemaType(x, module, { destination })).join(', ') + ']' // no links, no code + } + + let template + // Tuple -> Array + if (convertTuplesToArraysOrObjects && isTuple(schema) && isHomogenous(schema)) { + template = insertArrayMacros(getTemplate(path.join(templateDir, 'array')), schema, module) + template = insertSchemaMacros(template, firstItem, module, { name: getSchemaType(firstItem, module, {destination, templateDir, link, title, code, asPath, event, result, expandEnums, baseUrl, namespace }), recursive: false }) + } + // Normal Array + else if (!isTuple(schema)) { + const baseDir = (templateDir !== 'json-types' ? 'types': templateDir) + template = insertArrayMacros(getTemplate(path.join(baseDir, 'array')), schema, module) + template = insertSchemaMacros(template, schema.items, module, { name: getSchemaType(schema.items, module, {destination, templateDir, link, title, code, asPath, event, result, expandEnums, baseUrl, namespace })}) + } + else { + template = insertTupleMacros(getTemplate(path.join(templateDir, 'tuple')), schema, module, '', { templateDir }) + template = insertSchemaMacros(template, firstItem, module, { recursive: false }) + } + + if (code) { + template = wrap(template, '`') + } + // TODO need to support link: true + return template + } + else if (schema.allOf) { + let union = deepmerge.all([...schema.allOf.map(x => x['$ref'] ? getPath(x['$ref'], module) || x : x)]) + if (schema.title) { + union.title = schema.title + } + return getSchemaType(union, module, { templateDir, destination, link, title, code, asPath, baseUrl, namespace }) + } + else if (schema.oneOf || schema.anyOf) { + if (!schema.anyOf) { + schema.anyOf = schema.oneOf + } + // todo... we probably shouldn't allow untitled anyOfs, at least not w/out a feature flag + const shape = insertAnyOfMacros(getTemplate(path.join(templateDir, 'anyOf' + suffix)), schema, module, theTitle) + return insertSchemaMacros(shape, schema, module, { name: theTitle, recursive: false }) + + + // if (event) { + // return getSchemaType((schema.oneOf || schema.anyOf)[0], module, { destination, link, title, code, asPath, baseUrl }) + // } + // else { + // const newOptions = JSON.parse(JSON.stringify({ destination, link, title, code, asPath, baseUrl })) + // newOptions.code = false + // const result = (schema.oneOf || schema.anyOf).map(s => getSchemaType(s, module, newOptions)).join(' | ') + + // return code ? wrap(result, '`') : result + // } + } + else if (schema.type) { + let template = getTemplate(path.join(templateDir, 'additionalProperties')) + if (schema.additionalProperties && template ) { + return insertSchemaMacros(getTemplate(path.join(templateDir, 'Title')), schema, module, { name: theTitle, recursive: false }) + } + else { + template = getTemplate(path.join(templateDir, 'patternProperties')) + if (schema.paternProperties && template ) { + return insertSchemaMacros(getTemplate(path.join(templateDir, 'Title')), schema, module, { name: theTitle, recursive: false }) + } + else { + // TODO: this assumes that when type is an array of types, that it's one other primative & 'null', which isn't necessarily true. + const schemaType = !Array.isArray(schema.type) ? schema.type : schema.type.find(t => t !== 'null') + const baseDir = (templateDir !== 'json-types' ? 'types': templateDir) + let primitive = getPrimitiveType(schemaType, baseDir, schema.title ? true: false) + primitive = primitive ? primitive.replace(/\$\{title\}/g, schema.title) : primitive + const type = allocatedProxy ? allocatedPrimitiveProxies[schemaType] || primitive : primitive + + return wrap(type, code ? '`' : '') + } + } + } + else { + let type + if (schema.title) { + const baseDir = (templateDir !== 'json-types' ? 'types': templateDir) + type = getPrimitiveType('string', baseDir) + } + const template = type || getTemplate(path.join(templateDir, 'void')) || 'void' + // TODO this is TypeScript specific + return wrap(template, code ? '`' : '') + } +} + +function getJsonType(schema, module, { destination, link = false, title = false, code = false, asPath = false, event = false, expandEnums = true, baseUrl = '' } = {}) { + + schema = sanitize(schema) + let type + if (schema['$ref']) { + if (schema['$ref'][0] === '#') { + //Ref points to local schema + //Get Path to ref in this module and getSchemaType + let definition = getPath(schema['$ref'], module) + type = getJsonType(definition, schema, {destination}) + } + } + else { + type = !Array.isArray(schema.type) ? schema.type : schema.type.find(t => t !== 'null') + } + return type +} + +function getSchemaInstantiation(schema, module, { instantiationType }) { + return '' +} + +export default { + setTemplates, + setPrimitives, + setConvertTuples, + setAllocatedPrimitiveProxies, + getMethodSignatureParams, + getMethodSignatureResult, + getSchemaShape, + getSchemaType, + getSchemaInstantiation +} diff --git a/src/openrpc/index.mjs b/src/openrpc/index.mjs index e0819b7f..2931d21c 100644 --- a/src/openrpc/index.mjs +++ b/src/openrpc/index.mjs @@ -17,7 +17,7 @@ */ import { readJson, readFiles, readDir, writeJson } from "../shared/filesystem.mjs" -import { addExternalMarkdown, addExternalSchemas, fireboltize } from "../shared/modules.mjs" +import { addExternalMarkdown, addExternalSchemas, fireboltize, fireboltizeMerged } from "../shared/modules.mjs" import path from "path" import { logHeader, logSuccess } from "../shared/io.mjs" @@ -65,7 +65,7 @@ const run = async ({ json = addExternalMarkdown(json, markdown) // put module name in front of each method - json.methods.forEach(method => method.name = json.info.title + '.' + method.name) + json.methods.forEach(method => method.name = method.name.includes('\.') ? method.name : json.info.title + '.' + method.name) // merge any info['x-'] extension values (maps & arrays only..) Object.keys(json.info).filter(key => key.startsWith('x-')).forEach(extension => { @@ -101,6 +101,21 @@ const run = async ({ logSuccess(`Generated the ${json.info.title} module.`) }) + // make sure all provided-by APIs point to a real provider method + const appProvided = openrpc.methods.filter(m => m.tags.find(t=>t['x-provided-by'])) || [] + appProvided.forEach(m => { + const providedBy = m.tags.find(t=>t['x-provided-by'])['x-provided-by'] + const provider = openrpc.methods.find(m => m.name === providedBy) + if (!provider) { + throw `Method ${m.name} is provided by an undefined method (${providedBy})` + } + else { + console.log(`Method ${m.name} is provided by ${providedBy}`) + } + }) + + openrpc = fireboltizeMerged(openrpc) + await writeJson(output, openrpc) console.log() @@ -109,4 +124,4 @@ const run = async ({ return Promise.resolve() } -export default run \ No newline at end of file +export default run diff --git a/src/sdk/index.mjs b/src/sdk/index.mjs index 5d7effa2..f6fd0f5e 100755 --- a/src/sdk/index.mjs +++ b/src/sdk/index.mjs @@ -60,13 +60,21 @@ const run = async ({ persistPermission: config.persistPermission, createPolymorphicMethods: config.createPolymorphicMethods, operators: config.operators, + primitives: config.primitives, createModuleDirectories: config.createModuleDirectories, copySchemasIntoModules: config.copySchemasIntoModules, extractSubSchemas: config.extractSubSchemas, + convertTuplesToArraysOrObjects: config.convertTuplesToArraysOrObjects, + unwrapResultObjects: config.unwrapResultObjects, + allocatedPrimitiveProxies: config.allocatedPrimitiveProxies, + additionalSchemaTemplates: config.additionalSchemaTemplates, + additionalMethodTemplates: config.additionalMethodTemplates, + templateExtensionMap: config.templateExtensionMap, excludeDeclarations: config.excludeDeclarations, + extractProviderSchema: config.extractProviderSchema, staticModuleNames: staticModuleNames, hideExcluded: true, - aggregateFile: config.aggregateFile, + aggregateFiles: config.aggregateFiles, rename: mainFilename ? { '/index.mjs': mainFilename, '/index.d.ts': declarationsFilename } : {}, treeshakePattern: config.treeshakePattern ? new RegExp(config.treeshakePattern, "g") : undefined, treeshakeTypes: config.treeshakeTypes, diff --git a/src/shared/json-schema.mjs b/src/shared/json-schema.mjs index 76a5739f..98fdeaba 100644 --- a/src/shared/json-schema.mjs +++ b/src/shared/json-schema.mjs @@ -142,6 +142,74 @@ const getPath = (uri = '', moduleJson = {}) => { } } +const getPropertySchema = (json, dotPath, document) => { + const path = dotPath.split('.') + let node = json + + for (var i=0; i j >= i ).join('.') + if (node.$ref) { + node = getPropertySchema(getPath(node.$ref, document), remainingPath, document) + } + else if (property === '') { + return node + } + else if (node.type === 'object' || (node.type && node.type.includes && node.type.includes('object'))) { + if (node.properties && node.properties[property]) { + node = node.properties[property] + } + // todo: need to escape the regex? + else if (node.patternProperties && property.match(node.patternProperties)) { + node = node.patternProperties[property] + } + else if (node.additionalProperties && typeof node.additionalProperties === 'object') { + node = node.additionalProperties + } + } + else if (Array.isArray(node.allOf)) { + node = node.allOf.find(s => { + let schema + try { + schema = getPropertySchema(s, remainingPath, document) + } + catch (error) { + + } + return schema + }) + } + else { + throw `Cannot get property '${dotPath}' of non-object.` + } + } + + return node +} + +const getPropertiesInSchema = (json, document) => { + let node = json + + while (node.$ref) { + node = getPath(node.$ref, document) + } + + if (node.type === 'object') { + const props = [] + if (node.properties) { + props.push(...Object.keys(node.properties)) + } + + if (node.propertyNames) { + props.push(...node.propertyNames) + } + + return props + } + + return null +} + function getSchemaConstraints(schema, module, options = { delimiter: '\n' }) { if (schema.schema) { schema = schema.schema @@ -233,7 +301,6 @@ const localizeDependencies = (json, document, schemas = {}, options = defaultLoc path.pop() // drop ref if (refToPath(ref).length > 1) { let resolvedSchema = JSON.parse(JSON.stringify(getPathOr(null, refToPath(ref), document))) - if (schemaReferencesItself(resolvedSchema, refToPath(ref))) { resolvedSchema = null } @@ -355,6 +422,78 @@ const isDefinitionReferencedBySchema = (name = '', moduleJson = {}) => { return (refs.length > 0) } +function union(schemas) { + + const result = {}; + for (const schema of schemas) { + for (const [key, value] of Object.entries(schema)) { + if (!result.hasOwnProperty(key)) { + // If the key does not already exist in the result schema, add it + if (value && value.anyOf) { + result[key] = union(value.anyOf) + } else if (key === 'title' || key === 'description' || key === 'required') { + //console.warn(`Ignoring "${key}"`) + } else { + result[key] = value; + } + } else if (key === '$ref') { + if (result[key].endsWith("/ListenResponse")) { + + } + // If the key is '$ref' make sure it's the same + else if(result[key] === value) { + //console.warn(`Ignoring "${key}" that is already present and same`) + } else { + console.warn(`ERROR "${key}" is not same -${JSON.stringify(result, null, 4)} ${key} ${result[key]} - ${value}`); + throw "ERROR: $ref is not same" + } + } else if (key === 'type') { + // If the key is 'type', merge the types of the two schemas + if(result[key] === value) { + //console.warn(`Ignoring "${key}" that is already present and same`) + } else { + console.warn(`ERROR "${key}" is not same -${JSON.stringify(result, null, 4)} ${key} ${result[key]} - ${value}`); + throw "ERROR: type is not same" + } + } else { + //If the Key is a const then merge them into an enum + if(value && value.const) { + if(result[key].enum) { + result[key].enum = Array.from(new Set([...result[key].enum, value.const])) + } + else { + result[key].enum = Array.from(new Set([result[key].const, value.const])) + delete result[key].const + } + } + // If the key exists in both schemas and is not 'type', merge the values + else if (Array.isArray(result[key])) { + // If the value is an array, concatenate the arrays and remove duplicates + result[key] = Array.from(new Set([...result[key], ...value])) + } else if (result[key] && result[key].enum && value && value.enum) { + //If the value is an enum, merge the enums together and remove duplicates + result[key].enum = Array.from(new Set([...result[key].enum, ...value.enum])) + } else if (typeof result[key] === 'object' && typeof value === 'object') { + // If the value is an object, recursively merge the objects + result[key] = union([result[key], value]); + } else if (result[key] !== value) { + // If the value is a primitive and is not the same in both schemas, ignore it + //console.warn(`Ignoring conflicting value for key "${key}"`) + } + } + } + } + return result; +} + +function mergeAnyOf(schema) { + return union(schema.anyOf) +} + +function mergeOneOf(schema) { + return union(schema.oneOf) +} + const getSafeEnumKeyName = (value) => value.split(':').pop() // use last portion of urn:style:values .replace(/[\.\-]/g, '_') // replace dots and dashes .replace(/\+/g, '_plus') // change + to _plus @@ -370,11 +509,15 @@ export { getLocalSchemaPaths, getLinkedSchemaPaths, getPath, + getPropertySchema, + getPropertiesInSchema, isDefinitionReferencedBySchema, isNull, isSchema, localizeDependencies, replaceUri, replaceRef, - removeIgnoredAdditionalItems -} \ No newline at end of file + removeIgnoredAdditionalItems, + mergeAnyOf, + mergeOneOf +} diff --git a/src/shared/modules.mjs b/src/shared/modules.mjs index 095acd87..5fa64c6a 100644 --- a/src/shared/modules.mjs +++ b/src/shared/modules.mjs @@ -28,10 +28,14 @@ import isEmpty from 'crocks/core/isEmpty.js' 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 { getExternalSchemaPaths, isDefinitionReferencedBySchema, isNull, localizeDependencies, isSchema, getLocalSchemaPaths, replaceRef, getPropertySchema } from './json-schema.mjs' import { getPath as getRefDefinition } from './json-schema.mjs' const { isObject, isArray, propEq, pathSatisfies, hasProp, propSatisfies } = predicates +// TODO remove these when major/rpc branch is merged +const name = method => method.name.split('.').pop() +const rename = (method, renamer) => method.name.split('.').map((x, i, arr) => i === (arr.length-1) ? renamer(x) : x).join('.') + // util for visually debugging crocks ADTs const inspector = obj => { if (obj.inspect) { @@ -85,79 +89,75 @@ const getProviderInterfaceMethods = (capability, json) => { } -function getProviderInterface(capability, module) { +function getProviderInterface(capability, module, extractProviderSchema = false) { module = JSON.parse(JSON.stringify(module)) - const iface = getProviderInterfaceMethods(capability, module).map(method => localizeDependencies(method, module, null, { mergeAllOfs: true })) + const iface = getProviderInterfaceMethods(capability, module)//.map(method => localizeDependencies(method, module, null, { mergeAllOfs: true })) iface.forEach(method => { - const payload = localizeDependencies(getPayloadFromEvent(method), module) + const payload = getPayloadFromEvent(method) const focusable = method.tags.find(t => t['x-allow-focus']) // remove `onRequest` method.name = method.name.charAt(9).toLowerCase() + method.name.substr(10) - + + const schema = getPropertySchema(payload, 'properties.parameters', module) + method.params = [ { "name": "parameters", - "schema": payload.properties.parameters - }, - { - "name": "session", - "schema": { - "type": focusable ? "FocusableProviderSession" : "ProviderSession" - } + "required": true, + "schema": schema } ] - let exampleResult = null - - if (method.tags.find(tag => tag['x-response'])) { - const result = method.tags.find(tag => tag['x-response'])['x-response'] + if (!extractProviderSchema) { + let exampleResult = null + + if (method.tags.find(tag => tag['x-response'])) { + const result = method.tags.find(tag => tag['x-response'])['x-response'] - method.result = { - "name": "result", - "schema": result - } + method.result = { + "name": "result", + "schema": result + } - if (result.examples && result.examples[0]) { - exampleResult = result.examples[0] + if (result.examples && result.examples[0]) { + exampleResult = result.examples[0] + } } - } - else { - method.result = { - "name": "result", - "schema": { - "const": null + else { + method.result = { + "name": "result", + "schema": { + "const": null + } } } - } - method.examples = method.examples.map( example => ( - { - params: [ - { - name: "parameters", - value: example.result.value.parameters - }, - { - name: "session", - value: { - correlationId: example.result.value.correlationId + method.examples = method.examples.map( example => ( + { + params: [ + { + name: "parameters", + value: example.result.value.parameters + }, + { + name: "correlationId", + value: example.result.value.correlationId } + ], + result: { + name: "result", + value: exampleResult } - ], - result: { - name: "result", - value: exampleResult } - } - )) + )) - // remove event tag - method.tags = method.tags.filter(tag => tag.name !== 'event') + // remove event tag + method.tags = method.tags.filter(tag => tag.name !== 'event') + } }) - return iface } @@ -295,6 +295,16 @@ const isPolymorphicReducer = compose( getPath(['tags']) ) +const isAllowFocusMethod = compose( + option(false), + map(_ => true), + chain(find(and( + hasProp('x-uses'), + propSatisfies('x-allow-focus', focus => (focus === true)) + ))), + getPath(['tags']) +) + const hasTitle = compose( option(false), map(isString), @@ -365,6 +375,8 @@ const getPublicEvents = compose( const hasPublicInterfaces = json => json.methods && json.methods.filter(m => m.tags && m.tags.find(t=>t['x-provides'])).length > 0 const hasPublicAPIs = json => hasPublicInterfaces(json) || (json.methods && json.methods.filter( method => !method.tags.find(tag => tag.name === 'rpc-only')).length > 0) +const hasAllowFocusMethods = json => json.methods && json.methods.filter(m => isAllowFocusMethod(m)).length > 0 + const eventDefaults = event => { event.tags = [ @@ -376,13 +388,17 @@ const eventDefaults = event => { return event } -const createEventResultSchemaFromProperty = property => { +const createEventResultSchemaFromProperty = (property, type='') => { const subscriberType = property.tags.map(t => t['x-subscriber-type']).find(t => typeof t === 'string') || 'context' - if (property.tags.find(t => (t.name == 'property' || t.name.startsWith('property:')) && (subscriberType === 'global'))) { + const caps = property.tags.find(t => t.name === 'capabilities') + let name = caps['x-provided-by'] ? caps['x-provided-by'].split('.').pop().replace('onRequest', '') : property.name + name = name.charAt(0).toUpperCase() + name.substring(1) + + if ( subscriberType === 'global') { // wrap the existing result and the params in a new result object const schema = { - title: property.name.charAt(0).toUpperCase() + property.name.substring(1) + 'ChangedInfo', + title: name + type + 'Info', type: "object", properties: { @@ -400,27 +416,32 @@ const createEventResultSchemaFromProperty = property => { schema.properties[property.result.name] = property.result.schema !schema.required.includes(property.result.name) && schema.required.push(property.result.name) - return schema } } -const createEventFromProperty = property => { +const createEventFromProperty = (property, type='', alternative, json) => { + const provider = (property.tags.find(t => t['x-provided-by']) || {})['x-provided-by'] + const pusher = provider ? provider.replace('onRequest', '').split('.').map((x, i, arr) => (i === arr.length-1) ? x.charAt(0).toLowerCase() + x.substr(1) : x).join('.') : undefined const event = eventDefaults(JSON.parse(JSON.stringify(property))) - event.name = 'on' + event.name.charAt(0).toUpperCase() + event.name.substr(1) + 'Changed' - const old_tags = property.tags.concat() +// event.name = (module ? module + '.' : '') + 'on' + event.name.charAt(0).toUpperCase() + event.name.substr(1) + type + event.name = provider ? provider.split('.').pop().replace('onRequest', '') : event.name.charAt(0).toUpperCase() + event.name.substr(1) + type + event.name = event.name.split('.').map((x, i, arr) => (i === arr.length-1) ? 'on' + x.charAt(0).toUpperCase() + x.substr(1) : x).join('.') + const subscriberFor = pusher || (json.info.title + '.' + property.name) + + const old_tags = JSON.parse(JSON.stringify(property.tags)) - event.tags[0]['x-alternative'] = property.name + alternative && (event.tags[0]['x-alternative'] = alternative) - event.tags.unshift({ + !provider && event.tags.unshift({ name: "subscriber", - 'x-subscriber-for': property.name + 'x-subscriber-for': subscriberFor }) const subscriberType = property.tags.map(t => t['x-subscriber-type']).find(t => typeof t === 'string') || 'context' // if the subscriber type is global, zap all of the parameters and change the result type to the schema that includes them - if (old_tags.find(t => (t.name == 'property' || t.name.startsWith('property:')) && (subscriberType === 'global'))) { + if (subscriberType === 'global') { // wrap the existing result and the params in a new result object const result = { @@ -448,20 +469,59 @@ const createEventFromProperty = property => { } old_tags.forEach(t => { - if (t.name !== 'property' && !t.name.startsWith('property:')) + if (t.name !== 'property' && !t.name.startsWith('property:') && t.name !== 'push-pull') { event.tags.push(t) } }) + provider && (event.tags.find(t => t.name === 'capabilities')['x-provided-by'] = subscriberFor) + return event } +// create foo() notifier from onFoo() event +const createNotifierFromEvent = (event, json) => { + const push = JSON.parse(JSON.stringify(event)) + const caps = push.tags.find(t => t.name === 'capabilities') + push.name = caps['x-provided-by'] + delete caps['x-provided-by'] + + caps['x-provides'] = caps['x-uses'].pop() + delete caps['x-uses'] + + push.tags = push.tags.filter(t => t.name !== 'event') + + push.result.required = true + push.params.push(push.result) + + push.result = { + "name": "result", + "schema": { + "type": "null" + } + } + + push.examples.forEach(example => { + example.params.push(example.result) + example.result = { + "name": "result", + "value": null + } + }) + + return push +} + +const createPushEvent = (requestor, json) => { + return createEventFromProperty(requestor, '', undefined, json) +} + const createPullEventFromPush = (pusher, json) => { const event = eventDefaults(JSON.parse(JSON.stringify(pusher))) event.params = [] event.name = 'onPull' + event.name.charAt(0).toUpperCase() + event.name.substr(1) - const old_tags = pusher.tags.concat() + const old_tags = JSON.parse(JSON.stringify(pusher.tags)) event.tags[0]['x-pulls-for'] = pusher.name event.tags.unshift({ @@ -487,7 +547,7 @@ const createPullEventFromPush = (pusher, json) => { }) old_tags.forEach(t => { - if (t.name !== 'polymorphic-pull') + if (t.name !== 'polymorphic-pull' && t.name) { event.tags.push(t) } @@ -496,6 +556,119 @@ const createPullEventFromPush = (pusher, json) => { return event } +const createPullProvider = (requestor, params) => { + const event = eventDefaults(JSON.parse(JSON.stringify(requestor))) + event.name = requestor.tags.find(t => t['x-provided-by'])['x-provided-by'] + const old_tags = JSON.parse(JSON.stringify(requestor.tags)) + + const value = event.result + + event.tags[0]['x-response'] = value.schema + event.tags[0]['x-response'].examples = event.examples.map(e => e.result.value) + + event.result = { + "name": "request", + "schema": { + "type": "object", + "required": ["correlationId", "parameters"], + "properties":{ + "correlationId": { + "type": "string", + }, + "parameters": { + "$ref": "#/components/schemas/" + params + } + }, + "additionalProperties": false + } + } + + event.params = [] + + event.examples = event.examples.map(example => { + example.result = { + "name": "request", + "value": { + "correlationId": "xyz", + "parameters": {} + } + } + example.params.forEach(p => { + example.result.value.parameters[p.name] = p.value + }) + example.params = [] + return example + }) + + old_tags.forEach(t => { + if (t.name !== 'push-pull') + { + event.tags.push(t) + } + }) + + const caps = event.tags.find(t => t.name === 'capabilities') + caps['x-provides'] = caps['x-uses'].pop() || caps['x-manages'].pop() + caps['x-requestor'] = requestor.name + delete caps['x-uses'] + delete caps['x-manages'] + delete caps['x-provided-by'] + + return event +} + +const createPullProviderParams = (requestor) => { + const copy = JSON.parse(JSON.stringify(requestor)) + + // grab onRequest and turn into + const name = copy.tags.find(t => t['x-provided-by'])['x-provided-by'].split('.').pop().substring(9) + const paramsSchema = { + "title": name.charAt(0).toUpperCase() + name.substr(1) + "ProviderParameters", + "type": "object", + "required": [], + "properties": { + }, + "additionalProperties": false + } + + copy.params.forEach(p => { + paramsSchema.properties[p.name] = p.schema + if (p.required) { + paramsSchema.required.push(p.name) + } + }) + + return paramsSchema +} + +const createPullRequestor = (pusher, json) => { + const module = pusher.tags.find(t => t.name === 'push-pull')['x-requesting-interface'] + const requestor = JSON.parse(JSON.stringify(pusher)) + requestor.name = (module ? module + '.' : '') + 'request' + requestor.name.charAt(0).toUpperCase() + requestor.name.substr(1) + + const value = requestor.params.pop() + delete value.required + + requestor.tags = requestor.tags.filter(t => t.name !== 'push-pull') + requestor.tags.unshift({ + "name": "requestor", + "x-requestor-for": json.info.title + '.' + pusher.name + }) + const caps = requestor.tags.find(t => t.name === 'capabilities') + caps['x-provided-by'] = json.info.title + '.' + pusher.name + caps['x-uses'] = [ caps['x-provides'] ] + delete caps['x-provides'] + + requestor.tags.find(t => t.name === 'capabilities')['x-provided-by'] = json.info.title + '.' + pusher.name + requestor.result = value + + requestor.examples.forEach(example => { + example.result = example.params.pop() + }) + + return requestor +} + const createTemporalEventMethod = (method, json, name) => { const event = createEventFromMethod(method, json, name, 'x-temporal-for', ['temporal-set']) @@ -526,7 +699,7 @@ const createTemporalEventMethod = (method, json, name) => { const createEventFromMethod = (method, json, name, correlationExtension, tagsToRemove = []) => { const event = eventDefaults(JSON.parse(JSON.stringify(method))) event.name = 'on' + name - const old_tags = method.tags.concat() + const old_tags = JSON.parse(JSON.stringify(method.tags)) event.tags[0][correlationExtension] = method.name event.tags.unshift({ @@ -633,13 +806,13 @@ const createSetterFromProperty = property => { const createFocusFromProvider = provider => { - if (!provider.name.startsWith('onRequest')) { + if (!name(provider).startsWith('onRequest')) { throw "Methods with the `x-provider` tag extension MUST start with 'onRequest'." } 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.` + ready.name = rename(ready, n => n.charAt(9).toLowerCase() + n.substr(10) + 'Focus') + ready.summary = `Internal API for ${name(provider).substr(9)} Provider to request focus for UX purposes.` ready.tags = ready.tags.filter(t => t.name !== 'event') ready.tags.find(t => t.name === 'capabilities')['x-allow-focus-for'] = provider.name @@ -668,12 +841,12 @@ const createFocusFromProvider = provider => { // type = Response | Error const createResponseFromProvider = (provider, type, json) => { - if (!provider.name.startsWith('onRequest')) { + if (!name(provider).startsWith('onRequest')) { throw "Methods with the `x-provider` tag extension MUST start with 'onRequest'." } const response = JSON.parse(JSON.stringify(provider)) - response.name = response.name.charAt(9).toLowerCase() + response.name.substr(10) + type + response.name = rename(response, n => n.charAt(9).toLowerCase() + n.substr(10) + type) response.summary = `Internal API for ${provider.name.substr(9)} Provider to send back ${type.toLowerCase()}.` response.tags = response.tags.filter(t => t.name !== 'event') @@ -805,20 +978,32 @@ const createResponseFromProvider = (provider, type, json) => { return response } +const copyAllowFocusTags = (json) => { + // for each allow focus provider method, set the value on any `use` methods that share the same capability + json.methods.filter(m => m.tags.find(t => t['x-allow-focus'] && t['x-provides'])).forEach(method => { + const cap = method.tags.find(t => t.name === "capabilities")['x-provides'] + json.methods.filter(m => m.tags.find(t => t['x-uses'] && t['x-uses'].includes(cap))).forEach(useMethod => { + useMethod.tags.find(t => t.name === "capabilities")['x-allow-focus'] = true + }) + }) + + return json +} + const generatePropertyEvents = json => { const properties = json.methods.filter( m => m.tags && m.tags.find( t => t.name == 'property')) || [] const readonlies = json.methods.filter( m => m.tags && m.tags.find( t => t.name == 'property:readonly')) || [] properties.forEach(property => { - json.methods.push(createEventFromProperty(property)) - const schema = createEventResultSchemaFromProperty(property) + json.methods.push(createEventFromProperty(property, 'Changed', property.name, json)) + const schema = createEventResultSchemaFromProperty(property, 'Changed') if (schema) { json.components.schemas[schema.title] = schema } }) readonlies.forEach(property => { - json.methods.push(createEventFromProperty(property)) - const schema = createEventResultSchemaFromProperty(property) + json.methods.push(createEventFromProperty(property, 'Changed', property.name, json)) + const schema = createEventResultSchemaFromProperty(property, 'Changed') if (schema) { json.components.schemas[schema.title] = schema } @@ -843,6 +1028,43 @@ const generatePolymorphicPullEvents = json => { return json } +const generatePushPullMethods = json => { + const requestors = json.methods.filter( m => m.tags && m.tags.find( t => t.name == 'push-pull')) || [] + requestors.forEach(requestor => { + json.methods.push(createPushEvent(requestor, json)) + + const schema = createEventResultSchemaFromProperty(requestor) + if (schema) { + json.components = json.components || {} + json.components.schemas = json.components.schemas || {} + json.components.schemas[schema.title] = schema + } + }) + + return json +} + +const generateProvidedByMethods = json => { + const requestors = json.methods.filter(m => !m.tags.find(t => t.name === 'event')).filter( m => m.tags && m.tags.find( t => t['x-provided-by'])) || [] + const events = json.methods .filter(m => m.tags.find(t => t.name === 'event')) + .filter( m => m.tags && m.tags.find( t => t['x-provided-by'])) + .filter(e => !json.methods.find(m => m.name === e.tags.find(t => t['x-provided-by'])['x-provided-by'])) + + const pushers = events.map(m => createNotifierFromEvent(m, json)) + pushers.forEach(m => json.methods.push(m)) + + requestors.forEach(requestor => { + const schema = createPullProviderParams(requestor) + json.methods.push(createPullProvider(requestor, schema.title)) + + json.components = json.components || {} + json.components.schemas = json.components.schemas || {} + json.components.schemas[schema.title] = schema + }) + + return json +} + const generateTemporalSetMethods = json => { const temporals = json.methods.filter( m => m.tags && m.tags.find( t => t.name == 'temporal-set')) || [] @@ -855,7 +1077,7 @@ const generateTemporalSetMethods = json => { const generateProviderMethods = json => { - const providers = json.methods.filter( m => m.name.startsWith('onRequest') && m.tags && m.tags.find( t => t.name == 'capabilities' && t['x-provides'])) || [] + const providers = json.methods.filter( m => name(m).startsWith('onRequest') && m.tags && m.tags.find( t => t.name == 'capabilities' && t['x-provides'])) || [] providers.forEach(provider => { if (! isRPCOnlyMethod(provider)) { @@ -951,7 +1173,7 @@ const getAnyOfSchema = (inType, json) => { const generateAnyOfSchema = (anyOf, name, summary) => { let anyOfType = {} - anyOfType["name"] = name[0].toLowerCase() + name.substr(1) + anyOfType["name"] = name; anyOfType["summary"] = summary anyOfType["schema"] = anyOf return anyOfType @@ -961,7 +1183,7 @@ const generateParamsAnyOfSchema = (methodParams, anyOf, anyOfTypes, title, summa let params = [] methodParams.forEach(p => { if (p.schema.anyOf === anyOfTypes) { - let anyOfType = generateAnyOfSchema(anyOf, title, summary) + let anyOfType = generateAnyOfSchema(anyOf, p.name, summary) anyOfType.required = p.required params.push(anyOfType) } @@ -1002,6 +1224,7 @@ 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) @@ -1044,12 +1267,12 @@ const createPolymorphicMethods = (method, json) => { 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.rpc_name = method.name + polymorphicMethodSchema.name = foundAnyOfResult && isEventMethod(method) ? `${method.name}${title}` : method.name 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.result.schema = foundAnyOfResult ? generateResultAnyOfSchema(method, methodResult, anyOf, anyOfTypes, title, summary) : methodResult.schema polymorphicMethodSchema.examples = method.examples polymorphicMethodSchemas.push(Object.assign({}, polymorphicMethodSchema)) }) @@ -1061,6 +1284,53 @@ const createPolymorphicMethods = (method, json) => { return polymorphicMethodSchemas } +const isSubSchema = (schema) => schema.type === 'object' || (schema.type === 'string' && schema.enum) +const isSubEnumOfArraySchema = (schema) => (schema.type === 'array' && schema.items.enum) + +const addComponentSubSchemasNameForProperties = (key, schema) => { + if ((schema.type === "object") && schema.properties) { + Object.entries(schema.properties).forEach(([name, propSchema]) => { + if (isSubSchema(propSchema)) { + key = key + name.charAt(0).toUpperCase() + name.substring(1) + if (!propSchema.title) { + propSchema.title = key + } + propSchema = addComponentSubSchemasNameForProperties(key, propSchema) + } + else if (isSubEnumOfArraySchema(propSchema)) { + key = key + name.charAt(0).toUpperCase() + name.substring(1) + if (!propSchema.items.title) { + propSchema.items.title = key + } + } + }) + } + + return schema +} + +const addComponentSubSchemasName = (obj, schemas) => { + Object.entries(schemas).forEach(([key, schema]) => { + let componentSchemaProperties = schema.allOf ? schema.allOf : [schema] + componentSchemaProperties.forEach((componentSchema) => { + key = key.charAt(0).toUpperCase() + key.substring(1) + componentSchema = addComponentSubSchemasNameForProperties(key, componentSchema) + }) + }) + + return schemas +} + +const promoteAndNameXSchemas = (obj) => { + obj = JSON.parse(JSON.stringify(obj)) + if (obj['x-schemas']) { + Object.entries(obj['x-schemas']).forEach(([name, schemas]) => { + schemas = addComponentSubSchemasName(obj, schemas) + }) + } + return obj +} + const getPathFromModule = (module, path) => { console.error("DEPRECATED: getPathFromModule") @@ -1083,6 +1353,9 @@ const getPathFromModule = (module, path) => { const fireboltize = (json) => { json = generatePropertyEvents(json) json = generatePropertySetters(json) + // TODO: we don't use this yet... consider removing? + // json = generatePushPullMethods(json) + // json = generateProvidedByMethods(json) json = generatePolymorphicPullEvents(json) json = generateProviderMethods(json) json = generateTemporalSetMethods(json) @@ -1092,6 +1365,12 @@ const fireboltize = (json) => { return json } +const fireboltizeMerged = (json) => { + json = copyAllowFocusTags(json) + + return json +} + const getExternalMarkdownPaths = obj => { return getExternalSchemaPaths(obj) .filter(x => /^file:/.test(getPathOr(null, x, obj))) @@ -1245,7 +1524,7 @@ const removeUnusedSchemas = (json) => { return schema } -const getModule = (name, json, copySchemas) => { +const getModule = (name, json, copySchemas, extractSubSchemas) => { let openrpc = JSON.parse(JSON.stringify(json)) openrpc.methods = openrpc.methods .filter(method => method.name.toLowerCase().startsWith(name.toLowerCase() + '.')) @@ -1299,7 +1578,15 @@ const getModule = (name, json, copySchemas) => { ...(openrpc[destination[0]][destination[1]][destination[2]] || {}) } } + const capitalize = str => str[0].toUpperCase() + str.substr(1) + if (!schema.title) { + schema.title = capitalize(parts.pop()) + } + openrpc = setPath(destination, schema, openrpc) + if (extractSubSchemas) { + openrpc = promoteAndNameXSchemas(openrpc) + } } }) } @@ -1352,6 +1639,8 @@ export { isPublicEventMethod, hasPublicAPIs, hasPublicInterfaces, + isAllowFocusMethod, + hasAllowFocusMethods, isPolymorphicReducer, isPolymorphicPullMethod, isTemporalSetMethod, @@ -1375,6 +1664,7 @@ export { getSchemas, getParamsFromMethod, fireboltize, + fireboltizeMerged, getPayloadFromEvent, getPathFromModule, providerHasNoParameters, diff --git a/src/shared/typescript.mjs b/src/shared/typescript.mjs index 28e80086..930549f1 100644 --- a/src/shared/typescript.mjs +++ b/src/shared/typescript.mjs @@ -54,6 +54,7 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, if (schema['$ref']) { if (level === 0) { + throw "Ref at level 0!" return `${prefix}${theTitle};` } else { @@ -62,6 +63,7 @@ function getSchemaShape(schema = {}, module = {}, { name = '', level = 0, title, return getSchemaShape(someJson, module, { name, level, title, summary, descriptions, destination, enums: false }) } else { + throw "unknown $ref!!" ' '.repeat(level) + `${prefix}${theTitle}${operator}` } } diff --git a/src/validate/index.mjs b/src/validate/index.mjs index 33cc6aea..ca7d0712 100644 --- a/src/validate/index.mjs +++ b/src/validate/index.mjs @@ -19,7 +19,7 @@ import { readJson, readFiles, readDir } from "../shared/filesystem.mjs" import { addExternalMarkdown, addExternalSchemas, fireboltize } from "../shared/modules.mjs" import { removeIgnoredAdditionalItems, replaceUri } from "../shared/json-schema.mjs" -import { validate, displayError } from "./validator/index.mjs" +import { validate, displayError, validatePasshtroughs } from "./validator/index.mjs" import { logHeader, logSuccess, logError } from "../shared/io.mjs" import Ajv from 'ajv' @@ -33,7 +33,8 @@ const __dirname = url.fileURLToPath(new URL('.', import.meta.url)) const run = async ({ input: input, schemas: schemas, - transformations = false + transformations = false, + 'pass-throughs': passThroughs }) => { logHeader(`Validating ${path.relative('.', input)} with${transformations ? '' : 'out'} Firebolt transformations.`) @@ -49,6 +50,7 @@ const run = async ({ result.errors.forEach(error => { displayError(error) + // console.dir(error, { depth: 100 }) }) } } @@ -73,10 +75,13 @@ const run = async ({ const jsonSchemaSpec = await (await fetch('https://meta.json-schema.tools')).json() // - OpenRPC uses `additionalItems` when `items` is not an array of schemas. This fails strict validate, so we remove it - // - OpenRPC uses raw.githubusercontent.com URLs for the json-schema spec, we replace this with the up to date spec on meta.json-schema.tools const openRpcSpec = await (await fetch('https://meta.open-rpc.org')).json() + removeIgnoredAdditionalItems(openRpcSpec) - replaceUri('https://raw.githubusercontent.com/json-schema-tools/meta-schema/1.5.9/src/schema.json', 'https://meta.json-schema.tools/', openRpcSpec) + + //AJV doesn't like not having a slash at the end of the URL + replaceUri('https://meta.json-schema.tools', 'https://meta.json-schema.tools/', openRpcSpec) + Object.values(sharedSchemas).forEach(schema => { try { @@ -105,7 +110,7 @@ const run = async ({ addFormats(ajv) // explicitly add our custom extensions so we can keep strict mode on (TODO: put these in a JSON config?) - ajv.addVocabulary(['x-method', 'x-this-param', 'x-additional-params', 'x-schemas', 'components']) + ajv.addVocabulary(['x-method', 'x-this-param', 'x-additional-params', 'x-schemas', 'components', 'x-property']) const firebolt = ajv.compile(fireboltOpenRpcSpec) const jsonschema = ajv.compile(jsonSchemaSpec) @@ -185,7 +190,7 @@ const run = async ({ "methods": { "type": "array", "items": { - "allOf": json.methods.map(method => ({ + "allOf": json.methods.filter(m => m.result).map(method => ({ "if": { "type": "object", "properties": { @@ -265,7 +270,6 @@ const run = async ({ } ] - const examples = ajv.compile(exampleSpec) try { @@ -285,6 +289,11 @@ const run = async ({ // console.dir(exampleSpec, { depth: 100 }) } } + + if (passThroughs) { + const passthroughResult = validatePasshtroughs(json) + printResult(passthroughResult, "Firebolt App pass-through") + } } catch (error) { throw error diff --git a/src/validate/validator/index.mjs b/src/validate/validator/index.mjs index 8b0f76a1..687aaada 100644 --- a/src/validate/validator/index.mjs +++ b/src/validate/validator/index.mjs @@ -17,6 +17,8 @@ */ import groupBy from 'array.prototype.groupby' import util from 'util' +import { getPayloadFromEvent } from '../../shared/modules.mjs' +import { getPropertiesInSchema, getPropertySchema } from '../../shared/json-schema.mjs' const addPrettyPath = (error, json) => { const path = [] @@ -164,4 +166,108 @@ export const validate = (json = {}, schemas = {}, ajv, validator, additionalPack } return { valid: valid, title: json.title || json.info.title, errors: errors } +} + +const schemasMatch = (a, b) => { + const aKeys = Object.keys(a) + const bKeys = Object.keys(b) + const keysMatch = (aKeys.length == bKeys.length) && aKeys.every(key => bKeys.includes(key)) + if (keysMatch) { + const typesMatch = aKeys.every(key => typeof a[key] === typeof b[key]) + if (typesMatch) { + const valuesMatch = aKeys.every(key => typeof a[key] === 'object' || (a[key] === b[key])) + if (valuesMatch) { + const objectsMatch = aKeys.every(key => typeof a[key] !== 'object' || schemasMatch(a[key], b[key])) + if (objectsMatch) { + return true + } + } + } + } + + return false +} + +export const validatePasshtroughs = (json) => { + const providees = json.methods.filter(m => m.tags.find(t => t['x-provided-by'])) + + const result = { + valid: true, + title: 'Mapping of all x-provided-by methods', + errors: [] + } + + providees.forEach(method => { + const providerName = method.tags.find(t => t['x-provided-by'])['x-provided-by'] + const provider = json.methods.find(m => m.name === providerName) + let destination, examples1 + let source, examples2 + let sourceName + + if (!provider) { + result.errors.push({ + message: `The x-provided-by method '${providerName}' does not exist`, + instancePath: `/methods/${json.methods.indexOf(method)}` + }) + return + } + else if (method.tags.find(t => t.name === 'event')) { + destination = getPayloadFromEvent(method) + examples1 = method.examples.map(e => e.result.value) + source = provider.params[provider.params.length-1].schema + sourceName = provider.params[provider.params.length-1].name + examples2 = provider.examples.map(e => e.params[e.params.length-1].value) + } + else { + destination = method.result.schema + examples1 = method.examples.map(e => e.result.value) + source = JSON.parse(JSON.stringify(provider.tags.find(t => t['x-response'])['x-response'])) + sourceName = provider.tags.find(t => t['x-response'])['x-response-name'] + examples2 = provider.tags.find(t => t['x-response'])['x-response'].examples + delete source.examples + } + + if (!schemasMatch(source, destination)) { + const properties = getPropertiesInSchema(destination, json) + + // follow $refs so we can see the schemas + source = getPropertySchema(source, '.', json) + destination = getPropertySchema(destination, '.', json) + + if (properties && properties.length && sourceName) { + let candidate = getPropertySchema(getPropertySchema(destination, `properties.${sourceName}`, json), '.', json) + + if (!candidate) { + result.errors.push({ + message: `The x-provided-by method '${providerName}' does not have a matching result schema or ${sourceName} property`, + instancePath: `/methods/${json.methods.indexOf(method)}` + }) + } else if (!schemasMatch(candidate, source)) { + result.errors.push({ + message: `The x-provided-by method '${providerName}' does not have a matching result schema or ${sourceName} schema`, + instancePath: `/methods/${json.methods.indexOf(method)}` + }) + } + } + else if (!sourceName) { + result.errors.push({ + message: `The x-provided-by method '${providerName}' does not have a matching result schema and has no x-response-name property to inject into`, + instancePath: `/methods/${json.methods.indexOf(method)}` + }) + } + else { + result.errors.push({ + message: `The x-provided-by method '${providerName}' does not have a matching schema and has not candidate sub-schemas`, + instancePath: `/methods/${json.methods.indexOf(method)}` + }) + } + } + }) + if (result.errors.length) { + result.valid = false + result.errors.forEach(error => addPrettyPath(error, json)) + } + + return result + } \ No newline at end of file