diff --git a/.github/workflows/merge-to-main.yml b/.github/workflows/merge-to-main.yml new file mode 100644 index 00000000..2c4c8f1d --- /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" diff --git a/.github/workflows/merge-to-next-major.yml b/.github/workflows/merge-to-next-major.yml new file mode 100644 index 00000000..c2af8335 --- /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" 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 b1b313e5..ee972dee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,17 +1,74 @@ -# [2.2.0-next.1](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.1.1-next.3...v2.2.0-next.1) (2023-11-30) +# [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)) + +# [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)) + +# [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 + +# [2.3.0-next.1](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.2.0...v2.3.0-next.1) (2024-01-12) + + +### Bug Fixes + +* 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 + +* Support for context-free property subscribers ([9809273](https://github.com/rdkcentral/firebolt-openrpc/commit/980927309fa6efc7b03a490aa5fd7909f39ff4de)) + +# [2.2.0](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.1.2...v2.2.0) (2023-11-30) ### Features * Improve generated doc formatting ([#155](https://github.com/rdkcentral/firebolt-openrpc/issues/155)) ([4613281](https://github.com/rdkcentral/firebolt-openrpc/commit/46132810ac5e5b0a8b9baa22903cb8a367b46f63)) -## [2.1.1-next.3](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.1.1-next.2...v2.1.1-next.3) (2023-11-09) +## [2.1.2](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.1.1...v2.1.2) (2023-11-09) ### Bug Fixes * Provider openrpc response and error methods have a wrapper object ([#148](https://github.com/rdkcentral/firebolt-openrpc/issues/148)) ([373626e](https://github.com/rdkcentral/firebolt-openrpc/commit/373626ed598447bb670ac67e97ca8ed1bf32f28f)) +## [2.1.1](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.1.0...v2.1.1) (2023-11-01) + + +### Bug Fixes + +* Errors thrown in async provider methods do not send the JSONRPC error response ([#143](https://github.com/rdkcentral/firebolt-openrpc/issues/143)) ([ec8e259](https://github.com/rdkcentral/firebolt-openrpc/commit/ec8e2599d530f9d2bd38fbb53b36e13c038edf2a)) +* Macro not replaced for provider docs ([ef1aecd](https://github.com/rdkcentral/firebolt-openrpc/commit/ef1aecd0092a8652bc7b936604f9478b7b55df27)) + ## [2.1.1-next.2](https://github.com/rdkcentral/firebolt-openrpc/compare/v2.1.1-next.1...v2.1.1-next.2) (2023-11-01) 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/package-lock.json b/package-lock.json index b4220f0e..4945e7c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@firebolt-js/openrpc", - "version": "2.2.0-next.1", + "version": "3.0.0-next.3", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@firebolt-js/openrpc", - "version": "2.2.0-next.1", + "version": "3.0.0-next.3", "license": "Apache-2.0", "dependencies": { "ajv": "^8.3.0", diff --git a/package.json b/package.json index 5ecb67ba..a518fb65 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@firebolt-js/openrpc", - "version": "2.2.0-next.1", + "version": "3.0.0-next.3", "description": "The Firebolt SDK Code & Doc Generator", "main": "languages/javascript/src/sdk.mjs", "type": "module", diff --git a/src/macrofier/engine.mjs b/src/macrofier/engine.mjs index 3f7596ec..0696e5ff 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, options.type) + + Array.from(new Set(['methods'].concat(config.additionalMethodTemplates))).filter(dir => dir).forEach(dir => { + + 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 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))) + 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')) : '' - 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) + 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') @@ -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 = {}, 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 - } + const suffix = state.destination && config.templateExtensionMap ? state.destination.split(state.destination.includes('_') ? '_' : '.').pop() : '' - template = getTemplateForDeclaration(methodObj, templates) - - 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) + } + } + }) 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 = {}) { 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 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,12 @@ 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) - 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 +1353,17 @@ 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\.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 +1373,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 +1396,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 +1417,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 +1443,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 +1548,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 +1565,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 +1583,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,9 +1642,9 @@ 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 @@ -1429,8 +1657,8 @@ function insertParameterMacros(template, param, method, module) { .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)) } function insertCapabilityMacros(template, capabilities, method, module) { @@ -1452,33 +1680,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 } ) - +function getProviderInterfaceName(iface, capability, moduleJson = {}) { const uglyName = capability.split(":").slice(-2).map(capitalize).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 +} - let interfaceShape = getTemplate('/codeblocks/interface', templates) +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) + + 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 +1827,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 +1842,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 +1892,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 +1907,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 +1926,7 @@ function insertProviderParameterMacros(data = '', parameters, module = {}, optio export { generateMacros, + clearMacros, insertMacros, generateAggregateMacros, insertAggregateMacros @@ -1604,6 +1934,7 @@ export { export default { generateMacros, + clearMacros, insertMacros, generateAggregateMacros, insertAggregateMacros, diff --git a/src/macrofier/index.mjs b/src/macrofier/index.mjs index ce8d3d8d..99a1a3e0 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,12 @@ 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 + + typer.setTemplates && typer.setTemplates(templates) + typer.setPrimitives(primitives) + typer.setAllocatedPrimitiveProxies(allocatedPrimitiveProxies) + typer.setConvertTuples(convertTuplesToArraysOrObjects) + let templatesPermission = {} if (persistPermission) { templatesPermission = Object.assign(await readFilesPermissions(sharedTemplateList, sharedTemplates), @@ -112,14 +135,13 @@ const macrofy = async ( 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 +149,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 +183,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 +197,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..55ef7b7e --- /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" || 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..b1f6175a 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" @@ -101,6 +101,8 @@ const run = async ({ logSuccess(`Generated the ${json.info.title} module.`) }) + openrpc = fireboltizeMerged(openrpc) + await writeJson(output, openrpc) console.log() @@ -109,4 +111,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..5d1d3a27 100644 --- a/src/shared/json-schema.mjs +++ b/src/shared/json-schema.mjs @@ -233,7 +233,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 +354,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 @@ -376,5 +447,7 @@ export { 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..81335115 100644 --- a/src/shared/modules.mjs +++ b/src/shared/modules.mjs @@ -85,7 +85,7 @@ 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 })) @@ -95,69 +95,63 @@ function getProviderInterface(capability, module) { // remove `onRequest` method.name = method.name.charAt(9).toLowerCase() + method.name.substr(10) - + method.params = [ { "name": "parameters", + "required": true, "schema": payload.properties.parameters - }, - { - "name": "session", - "schema": { - "type": focusable ? "FocusableProviderSession" : "ProviderSession" - } } ] - 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 +289,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 +369,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 = [ @@ -805,6 +811,18 @@ 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')) || [] @@ -951,7 +969,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 +979,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 +1020,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 +1063,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 +1080,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") @@ -1092,6 +1158,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 +1317,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 +1371,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 +1432,8 @@ export { isPublicEventMethod, hasPublicAPIs, hasPublicInterfaces, + isAllowFocusMethod, + hasAllowFocusMethods, isPolymorphicReducer, isPolymorphicPullMethod, isTemporalSetMethod, @@ -1375,6 +1457,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..f9002554 100644 --- a/src/validate/index.mjs +++ b/src/validate/index.mjs @@ -73,10 +73,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 +108,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)