From 947c926366b13280e1d9170f9f013deb4da5d14b Mon Sep 17 00:00:00 2001 From: Alexandre Jacinto Date: Tue, 17 Sep 2024 14:21:01 -0300 Subject: [PATCH] RMET-3608 - Prepare to release version `2.11.1-OS12` (#37) * RMET-3477 :: OutSystems Wrapper :: Add OneSignal Plugin Manager (#35) - adds code specific to outsystems lifecycle aka one signal plugin manager - adds the code that will be in outsystems wrapper's javascript nodes - add sonar cloud integration for typescript files https://outsystemsrd.atlassian.net/browse/RMET-3477 * RMET-3608 OneSignal-Cordova-SDK - Update dependency to FCM Android SDK (#36) * feat: update FB Messaging Android library References: https://outsystemsrd.atlassian.net/browse/RMET-3608 * feat: use Firebase BOM to get FB Cloud Messaging lib Context: According to the documentation, this is the recommended way of controlling Firebase library versions. This way, using the Firebase Android BOM, an app with multiple Firebase Android libraries will always use compatible versions of these libraries. References: https://outsystemsrd.atlassian.net/browse/RMET-3608 * Revert "feat: use Firebase BOM to get FB Cloud Messaging lib" This reverts commit a3e6be3c6f17a9ddf4f231e6cafa6af60e42d629. * fix: add necessary Firebase library for OneSignal to work with latest version of Cloud Messaging Context: https://firebase.google.com/support/release-notes/android?_gl=1*d5zec9*_up*MQ..*_ga*OTY5NjA2OTg5LjE3MjYwNTUwMDE.*_ga_CW55HF8NVT*MTcyNjA1NTAwMS4xLjAuMTcyNjA1NTAwMS4wLjAuMA..#messaging_v22-0-0 References: https://outsystemsrd.atlassian.net/browse/RMET-3608 * chore: update changelog References: https://outsystemsrd.atlassian.net/browse/RMET-3608 * chore(release): raise to version 2.11.1-OS12 References: https://outsystemsrd.atlassian.net/browse/RMET-3608 --------- Co-authored-by: Marta Carlos <101343976+OS-martacarlos@users.noreply.github.com> --- .github/workflows/run_unit_tests_workflow.yml | 32 ++++ .gitignore | 2 + CHANGELOG.md | 5 + build-extras-onesignal.gradle | 3 +- jest.config.js | 14 ++ outsystems/CHANGELOG.md | 10 ++ outsystems/README.md | 19 +++ outsystems/nodes/O11/initOneSignal.js | 18 +++ outsystems/nodes/O11/registerDispatcher.js | 37 +++++ outsystems/nodes/O11/unregisterPlugin.js | 4 + outsystems/nodes/ODC/initOneSignal.js | 13 ++ outsystems/nodes/ODC/registerDispatcher.js | 32 ++++ outsystems/nodes/ODC/unregisterPlugin.js | 1 + outsystems/scripts/O11/manager.js | 68 +++++++++ outsystems/scripts/ODC/definitions.d.ts | 1 + outsystems/scripts/ODC/index.d.ts | 26 ++++ outsystems/scripts/ODC/manager.js | 42 +++++ outsystems/scripts/OneSignal_Events.js | 47 ++++++ package.json | 22 ++- plugin.xml | 2 +- sonar-project.properties | 22 +++ src/outsystems/definitions.ts | 1 + src/outsystems/index.ts | 57 +++++++ test/onesignalmanager.spec.ts | 144 ++++++++++++++++++ tsconfig.json | 21 +++ vite.config.ts | 17 +++ 26 files changed, 656 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/run_unit_tests_workflow.yml create mode 100644 jest.config.js create mode 100644 outsystems/CHANGELOG.md create mode 100644 outsystems/README.md create mode 100644 outsystems/nodes/O11/initOneSignal.js create mode 100644 outsystems/nodes/O11/registerDispatcher.js create mode 100644 outsystems/nodes/O11/unregisterPlugin.js create mode 100644 outsystems/nodes/ODC/initOneSignal.js create mode 100644 outsystems/nodes/ODC/registerDispatcher.js create mode 100644 outsystems/nodes/ODC/unregisterPlugin.js create mode 100644 outsystems/scripts/O11/manager.js create mode 100644 outsystems/scripts/ODC/definitions.d.ts create mode 100644 outsystems/scripts/ODC/index.d.ts create mode 100644 outsystems/scripts/ODC/manager.js create mode 100644 outsystems/scripts/OneSignal_Events.js create mode 100644 sonar-project.properties create mode 100644 src/outsystems/definitions.ts create mode 100644 src/outsystems/index.ts create mode 100644 test/onesignalmanager.spec.ts create mode 100644 tsconfig.json create mode 100644 vite.config.ts diff --git a/.github/workflows/run_unit_tests_workflow.yml b/.github/workflows/run_unit_tests_workflow.yml new file mode 100644 index 00000000..a8242161 --- /dev/null +++ b/.github/workflows/run_unit_tests_workflow.yml @@ -0,0 +1,32 @@ +name: Run Unit Tests + +on: + workflow_dispatch: + push: + branches: [ main, development ] + pull_request: + branches: [ main, development ] + +jobs: + test: + name: Unit-Tests + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 18 + + - name: Install dependencies + run: npm install + + - name: Run tests (with coverage) + run: npm test -- --coverage --ci + + - name: SonarCloud Scan + uses: SonarSource/sonarcloud-github-action@master + env: + SONAR_TOKEN: ${{ secrets. SONAR_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3c3629e6..b07ff2ae 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ node_modules +package-lock.json +coverage diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ea80eb6..c59f954a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.11.1-OS12] +### Features + +- Android | Update dependency to FCM Android library (https://outsystemsrd.atlassian.net/browse/RMET-3608). + ## [2.11.1-OS11] ### Fixes diff --git a/build-extras-onesignal.gradle b/build-extras-onesignal.gradle index 53b70371..f8486773 100644 --- a/build-extras-onesignal.gradle +++ b/build-extras-onesignal.gradle @@ -64,6 +64,7 @@ dependencies { implementation 'com.google.android.gms:play-services-basement:18.2.0' implementation 'com.google.android.gms:play-services-tasks:18.0.2' implementation 'com.google.android.gms:play-services-stats:17.0.3' - api 'com.google.firebase:firebase-messaging:20.2.1' + api 'com.google.firebase:firebase-messaging:24.0.1' + api 'com.google.firebase:firebase-iid:21.1.0' implementation 'androidx.cardview:cardview:1.0.0' } diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 00000000..8a09803c --- /dev/null +++ b/jest.config.js @@ -0,0 +1,14 @@ +// jest.config.js + +module.exports = { + preset: 'ts-jest', + testEnvironment: 'node', + moduleFileExtensions: ['ts', 'js'], + testMatch: ['**/*.spec.ts'], + transform: { + '^.+\\.tsx?$':['ts-jest', { + tsconfig: 'tsconfig.json', + }] + }, + }; + \ No newline at end of file diff --git a/outsystems/CHANGELOG.md b/outsystems/CHANGELOG.md new file mode 100644 index 00000000..cd47bc3d --- /dev/null +++ b/outsystems/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + + +### 2024-06-19 + +- Re-write `OneSignal_Manager` as an UMD module, [RMET-3477](https://outsystemsrd.atlassian.net/browse/RMET-3477) \ No newline at end of file diff --git a/outsystems/README.md b/outsystems/README.md new file mode 100644 index 00000000..0d141656 --- /dev/null +++ b/outsystems/README.md @@ -0,0 +1,19 @@ +# OutSystems Wrapper + + +Welcome! Here lies the code related to the OutSystems Wrapper *aka* the JavaScript code that is present on the OutSystems Plugin, downloadable from Forge. + +## Structure + +In an OutSystems Plugin, there's two ways to host JavaScript code: JavaScript Nodes and script files. + +* `nodes` holds the JavaScript code inside a OutSystems Javascript node, with the same name +* `scripts` JavaScript files that the plugin imports + +And lastly, but not least, the wrapper differs depending on the platform (ODC vs O11) and, as such, so does this folder. + +## How is it used + +This repository holds the code related to the OneSignal Plugin's module, `OneSignal_Manager`. Up until 2024, this manager was counting on the existence of RequireJS in an OutSystems' App Runtime to create this module. Since RequireJS will no longer be part of OutSystems' Runtime, `OneSignal_Manager` needed to be updated. The approach was to create an UMD module, available to any plugin/app that imports `OneSignal_Manager`. + +Additionally, this module assumes the Common Plugin's `PluginManager` is imported in the app. \ No newline at end of file diff --git a/outsystems/nodes/O11/initOneSignal.js b/outsystems/nodes/O11/initOneSignal.js new file mode 100644 index 00000000..bda72eb6 --- /dev/null +++ b/outsystems/nodes/O11/initOneSignal.js @@ -0,0 +1,18 @@ +require(["OneSignalPluginManager"], function(oneSignalMgr){ + var builder = window.plugins.OneSignal.startInit($parameters.ApiKey); + builder.handleNotificationReceived(oneSignalMgr.notificationReceivedDelegate); + builder.handleNotificationOpened(oneSignalMgr.notificationOpenedDelegate); + builder.inFocusDisplaying($parameters.InFocusDisplayOption); + + // Set your iOS Settings + var iosSettings = {}; + iosSettings["kOSSettingsKeyAutoPrompt"] = true; + iosSettings["kOSSettingsKeyInAppLaunchURL"] = false; + + builder.iOSSettings(iosSettings); + + builder.endInit(); + + $resolve(); + +}); \ No newline at end of file diff --git a/outsystems/nodes/O11/registerDispatcher.js b/outsystems/nodes/O11/registerDispatcher.js new file mode 100644 index 00000000..6dce4656 --- /dev/null +++ b/outsystems/nodes/O11/registerDispatcher.js @@ -0,0 +1,37 @@ +require(["PluginManager","OneSignalPluginManager"], function(module, oneSignalMgr){ + + var onReady = function(scope) { + console.log("onReady scope"); + scope.handleNotificationOpened = scope.newCallback(function(jsonData) { + try{ + console.log("handleNotificationOpened"); + window.oneSignalEvents.triggerOnDispatchNotificationOpened(jsonData); + } catch(err) { + console.error(err); + } + }); + + scope.handleNotificationReceived = scope.newCallback(function(jsonData) { + try{ + console.log("handleNotificationReceived"); + window.oneSignalEvents.triggerOnDispatchNotificationReceived(jsonData); + } catch(err) { + console.error(err); + } + }); + + oneSignalMgr.setNotificationReceivedCallback(scope.handleNotificationReceived); + oneSignalMgr.setNotificationOpenedCallback(scope.handleNotificationOpened); + + } + + var onDestroy = function(scope) { + console.log("onDestroy scope"); + oneSignalMgr.unregisterReceivedCallback(scope.handleNotificationReceived); + oneSignalMgr.unregisterOpenedCallback(scope.handleNotificationOpened); + } + + module.createScope(`OneSignalPlugin_${$parameters.Token}`, onReady, onDestroy); + + $resolve(); +}); \ No newline at end of file diff --git a/outsystems/nodes/O11/unregisterPlugin.js b/outsystems/nodes/O11/unregisterPlugin.js new file mode 100644 index 00000000..d63edfc4 --- /dev/null +++ b/outsystems/nodes/O11/unregisterPlugin.js @@ -0,0 +1,4 @@ +require(["PluginManager"], function(module){ + module.destroyScope(`OneSignalPlugin_${$parameters.Token}`); + $resolve(); +}); \ No newline at end of file diff --git a/outsystems/nodes/ODC/initOneSignal.js b/outsystems/nodes/ODC/initOneSignal.js new file mode 100644 index 00000000..facec5d6 --- /dev/null +++ b/outsystems/nodes/ODC/initOneSignal.js @@ -0,0 +1,13 @@ +var builder = window.plugins.OneSignal.startInit($parameters.ApiKey); +builder.handleNotificationReceived(OSOneSignal.notificationReceivedDelegate); +builder.handleNotificationOpened(OSOneSignal.notificationOpenedDelegate); +builder.inFocusDisplaying($parameters.InFocusDisplayOption); + +// Set your iOS Settings +var iosSettings = {}; +iosSettings["kOSSettingsKeyAutoPrompt"] = true; +iosSettings["kOSSettingsKeyInAppLaunchURL"] = false; + +builder.iOSSettings(iosSettings); + +builder.endInit(); \ No newline at end of file diff --git a/outsystems/nodes/ODC/registerDispatcher.js b/outsystems/nodes/ODC/registerDispatcher.js new file mode 100644 index 00000000..a419a0d0 --- /dev/null +++ b/outsystems/nodes/ODC/registerDispatcher.js @@ -0,0 +1,32 @@ +var onReady = function(scope) { + console.log("onReady scope"); + scope.handleNotificationOpened = scope.newCallback(function(jsonData) { + try{ + console.log("handleNotificationOpened"); + window.oneSignalEvents.triggerOnDispatchNotificationOpened(jsonData); + } catch(err) { + console.error(err); + } + }); + + scope.handleNotificationReceived = scope.newCallback(function(jsonData) { + try{ + console.log("handleNotificationReceived"); + window.oneSignalEvents.triggerOnDispatchNotificationReceived(jsonData); + } catch(err) { + console.error(err); + } + }); + + OSOneSignal.setNotificationReceivedCallback(scope.handleNotificationReceived); + OSOneSignal.setNotificationOpenedCallback(scope.handleNotificationOpened); + +} + +var onDestroy = function(scope) { + console.log("onDestroy scope"); + OSOneSignal.unregisterReceivedCallback(scope.handleNotificationReceived); + OSOneSignal.unregisterOpenedCallback(scope.handleNotificationOpened); +} + +OSCommonPlugin.PluginManager.createScope(`OneSignalPlugin_${$parameters.Token}`, onReady, onDestroy); \ No newline at end of file diff --git a/outsystems/nodes/ODC/unregisterPlugin.js b/outsystems/nodes/ODC/unregisterPlugin.js new file mode 100644 index 00000000..74505f26 --- /dev/null +++ b/outsystems/nodes/ODC/unregisterPlugin.js @@ -0,0 +1 @@ +OSCommonPlugin.PluginManager.destroyScope(`OneSignalPlugin_${$parameters.Token}`); \ No newline at end of file diff --git a/outsystems/scripts/O11/manager.js b/outsystems/scripts/O11/manager.js new file mode 100644 index 00000000..16ff3cfe --- /dev/null +++ b/outsystems/scripts/O11/manager.js @@ -0,0 +1,68 @@ +define("OneSignalPluginManager", ["exports"], function(exports) { + + var notificationReceivedCallback = []; + var notificationOpenedCallback = []; + + /** + * Public delegate for OneSignal's handleNotificationReceived + */ + function notificationReceivedDelegate(jsonData) { + notificationReceivedCallback.forEach(function(callback){ + callback.call(undefined, jsonData); + }); + } + + /** + * Public delegate for OneSignal's handleNotificationOpened + */ + function notificationOpenedDelegate(jsonData) { + notificationOpenedCallback.forEach(function(callback){ + callback.call(undefined, jsonData); + }); + } + + /** + * Sets the current callback for OneSignal's Notification Received events. + */ + function setNotificationReceivedCallback(callback) { + notificationReceivedCallback.push(callback); + } + + /** + * Sets the current callback for OneSignal's Notification Opened events. + */ + function setNotificationOpenedCallback(callback) { + notificationOpenedCallback.push(callback); + } + + /** + * Unregister the old callback of OneSignal's Notification Received + */ + function unregisterReceivedCallback(callback) { + var cbIndex = notificationReceivedCallback.indexOf(callback); + if(cbIndex >= 0) { + notificationReceivedCallback[cbIndex] = undefined; + delete notificationReceivedCallback[cbIndex]; + } + } + + /** + * Unregister the old callback of OneSignal's Notification Opened + */ + function unregisterOpenedCallback(callback) { + var cbIndex = notificationOpenedCallback.indexOf(callback); + if(cbIndex >= 0) { + notificationOpenedCallback[cbIndex] = undefined; + delete notificationOpenedCallback[cbIndex]; + } + } + + exports.notificationReceivedDelegate = notificationReceivedDelegate; + exports.notificationOpenedDelegate = notificationOpenedDelegate; + + exports.setNotificationReceivedCallback = setNotificationReceivedCallback; + exports.setNotificationOpenedCallback = setNotificationOpenedCallback; + + exports.unregisterReceivedCallback = unregisterReceivedCallback; + exports.unregisterOpenedCallback = unregisterOpenedCallback; +}); \ No newline at end of file diff --git a/outsystems/scripts/ODC/definitions.d.ts b/outsystems/scripts/ODC/definitions.d.ts new file mode 100644 index 00000000..712c66c5 --- /dev/null +++ b/outsystems/scripts/ODC/definitions.d.ts @@ -0,0 +1 @@ +export type OneSignalCallback = (...args: any[]) => void; diff --git a/outsystems/scripts/ODC/index.d.ts b/outsystems/scripts/ODC/index.d.ts new file mode 100644 index 00000000..c9c47d99 --- /dev/null +++ b/outsystems/scripts/ODC/index.d.ts @@ -0,0 +1,26 @@ +import { OneSignalCallback } from './definitions'; + +/** + * Public delegate for OneSignal's handleNotificationReceived + */ +export declare function notificationReceivedDelegate(jsonData: any): void; +/** + * Public delegate for OneSignal's handleNotificationOpened + */ +export declare function notificationOpenedDelegate(jsonData: any): void; +/** + * Sets the current callback for OneSignal's Notification Received events. + */ +export declare function setNotificationReceivedCallback(callback: OneSignalCallback): void; +/** + * Sets the current callback for OneSignal's Notification Opened events. + */ +export declare function setNotificationOpenedCallback(callback: OneSignalCallback): void; +/** + * Unregister the old callback of OneSignal's Notification Received + */ +export declare function unregisterReceivedCallback(callback: OneSignalCallback): void; +/** + * Unregister the old callback of OneSignal's Notification Opened + */ +export declare function unregisterOpenedCallback(callback: OneSignalCallback): void; diff --git a/outsystems/scripts/ODC/manager.js b/outsystems/scripts/ODC/manager.js new file mode 100644 index 00000000..7a3662ea --- /dev/null +++ b/outsystems/scripts/ODC/manager.js @@ -0,0 +1,42 @@ +(function(global, factory) { + typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define(["exports"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.OSOneSignal = {})); +})(this, function(exports2) { + "use strict"; + let notificationReceivedCallback = []; + let notificationOpenedCallback = []; + function notificationReceivedDelegate(jsonData) { + notificationReceivedCallback.forEach(function(callback) { + callback(jsonData); + }); + } + function notificationOpenedDelegate(jsonData) { + notificationOpenedCallback.forEach(function(callback) { + callback(jsonData); + }); + } + function setNotificationReceivedCallback(callback) { + notificationReceivedCallback.push(callback); + } + function setNotificationOpenedCallback(callback) { + notificationOpenedCallback.push(callback); + } + function unregisterReceivedCallback(callback) { + let cbIndex = notificationReceivedCallback.indexOf(callback); + if (cbIndex >= 0) { + notificationReceivedCallback.splice(cbIndex, 1); + } + } + function unregisterOpenedCallback(callback) { + let cbIndex = notificationOpenedCallback.indexOf(callback); + if (cbIndex >= 0) { + notificationOpenedCallback.splice(cbIndex, 1); + } + } + exports2.notificationOpenedDelegate = notificationOpenedDelegate; + exports2.notificationReceivedDelegate = notificationReceivedDelegate; + exports2.setNotificationOpenedCallback = setNotificationOpenedCallback; + exports2.setNotificationReceivedCallback = setNotificationReceivedCallback; + exports2.unregisterOpenedCallback = unregisterOpenedCallback; + exports2.unregisterReceivedCallback = unregisterReceivedCallback; + Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" }); +}); diff --git a/outsystems/scripts/OneSignal_Events.js b/outsystems/scripts/OneSignal_Events.js new file mode 100644 index 00000000..cfb9a7e2 --- /dev/null +++ b/outsystems/scripts/OneSignal_Events.js @@ -0,0 +1,47 @@ +/** + * Due to the nature of OutSystems Screens' & Blocks' Lifecyle + * We keep track of the active callbacks (valid client actions) via this window object + */ +(function() { + + var callbacks = { + onDispatchNotificationOpened: null, + onDispatchNotificationReceived: null + } + + var token; + + var OneSignalEventsSync = { + registerCallbacks: function (onDispatchNotificationReceived, onDispatchNotificationOpened){ + token = (new Date()).getTime().toString(); + + callbacks.onDispatchNotificationReceived = onDispatchNotificationReceived; + callbacks.onDispatchNotificationOpened = onDispatchNotificationOpened; + + return token + }, + + unregisterCallbacks: function (previousToken){ + if(token === previousToken) { + callbacks = null; + } + }, + + triggerOnDispatchNotificationReceived: function (json){ + if(callbacks && callbacks.onDispatchNotificationReceived) { + callbacks.onDispatchNotificationReceived(JSON.stringify(json)); + } + }, + + triggerOnDispatchNotificationOpened: function (json){ + if(callbacks && callbacks.onDispatchNotificationOpened) { + callbacks.onDispatchNotificationOpened(JSON.stringify(json)); + } + } + + } + + if (!window.oneSignalEvents) { + window.oneSignalEvents = OneSignalEventsSync; + } +})(); diff --git a/package.json b/package.json index 516de784..6fe1bf52 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "2.11.1-OS11", + "version": "2.11.1-OS12", "name": "onesignal-cordova-plugin", "cordova_name": "OneSignal Push Notifications", "description": "OneSignal is a high volume Push Notification service for mobile apps. In addition to basic notification delivery, OneSignal also provides tools to localize, target, schedule, and automate notifications that you send.", @@ -64,5 +64,23 @@ "bugs": { "url": "https://github.com/onesignal/OneSignal-Cordova-SDK/issues" }, - "homepage": "https://github.com/onesignal/OneSignal-Cordova-SDK#readme" + "homepage": "https://github.com/onesignal/OneSignal-Cordova-SDK#readme", + "scripts": { + "build": "vite build", + "test": "jest --coverage --ci" + }, + "devDependencies": { + "jest": "^29.7.0", + "@types/jest": "^29.5.11", + "ts-jest": "^29.1.2", + "@rollup/plugin-typescript": "^11.1.6", + "@types/node": "^20.12.12", + "typescript": "~5.4.5", + "vite": "^5.2.11", + "vite-plugin-dts": "^3.9.1", + "rimraf": "4.3.1", + "eslint": "^7.11.0", + "prettier": "~2.3.0", + "@types/cordova": "^11.0.3" + } } diff --git a/plugin.xml b/plugin.xml index 11ca955b..d8db9b92 100644 --- a/plugin.xml +++ b/plugin.xml @@ -2,7 +2,7 @@ + version="2.11.1-OS12"> OneSignal Push Notifications Josh Kasten, Bradley Hesse, Rodrigo Gomez-Palacio diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000..0ac4819d --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,22 @@ +# Organization and project keys are displayed in the right sidebar of the project homepage +sonar.organization=outsystemsrd +sonar.projectKey=OutSystems_OneSignal-Cordova-SDK +sonar.host.url=https://sonarcloud.io + +sonar.language=ts + +# Path is relative to the sonar-project.properties file. Defaults to . +sonar.sources=src/outsystems +sonar.tests=test + +# Defining path to coverage file +sonar.javascript.lcov.reportPaths=./coverage/lcov.info + +# Specify the TypeScript file suffixes +sonar.typescript.file.suffixes=.ts,.tsx + +# Define TS lint +sonar.typescript.tslint.reportPaths=tslint-report.json + +# Exclude directories +sonar.exclusions=**/node_modules/**,**/*.spec.ts, **/*.java diff --git a/src/outsystems/definitions.ts b/src/outsystems/definitions.ts new file mode 100644 index 00000000..f1fd4982 --- /dev/null +++ b/src/outsystems/definitions.ts @@ -0,0 +1 @@ +export type OneSignalCallback = (...args: any[]) => void; \ No newline at end of file diff --git a/src/outsystems/index.ts b/src/outsystems/index.ts new file mode 100644 index 00000000..71b7ce8e --- /dev/null +++ b/src/outsystems/index.ts @@ -0,0 +1,57 @@ +import { OneSignalCallback } from "./definitions"; + +let notificationReceivedCallback: OneSignalCallback[] = []; +let notificationOpenedCallback: OneSignalCallback[] = []; + +/** + * Public delegate for OneSignal's handleNotificationReceived + */ +export function notificationReceivedDelegate(jsonData: any) { + notificationReceivedCallback.forEach(function(callback:OneSignalCallback){ + callback(jsonData); + }); +} + +/** + * Public delegate for OneSignal's handleNotificationOpened + */ +export function notificationOpenedDelegate(jsonData: any) { + notificationOpenedCallback.forEach(function(callback:OneSignalCallback){ + callback(jsonData); + }); +} + +/** + * Sets the current callback for OneSignal's Notification Received events. + */ +export function setNotificationReceivedCallback(callback:OneSignalCallback) { + notificationReceivedCallback.push(callback); +} + +/** + * Sets the current callback for OneSignal's Notification Opened events. + */ +export function setNotificationOpenedCallback(callback:OneSignalCallback) { + notificationOpenedCallback.push(callback); +} + +/** + * Unregister the old callback of OneSignal's Notification Received + */ +export function unregisterReceivedCallback(callback:OneSignalCallback) { + let cbIndex = notificationReceivedCallback.indexOf(callback); + if(cbIndex >= 0) { + notificationReceivedCallback.splice(cbIndex, 1); + } +} + +/** + * Unregister the old callback of OneSignal's Notification Opened + */ +export function unregisterOpenedCallback(callback:OneSignalCallback) { + let cbIndex = notificationOpenedCallback.indexOf(callback); + if(cbIndex >= 0) { + notificationOpenedCallback.splice(cbIndex, 1); + } + +} \ No newline at end of file diff --git a/test/onesignalmanager.spec.ts b/test/onesignalmanager.spec.ts new file mode 100644 index 00000000..c16e04b9 --- /dev/null +++ b/test/onesignalmanager.spec.ts @@ -0,0 +1,144 @@ + +import * as Manager from '../src/outsystems/index' + +describe("One Signal Manager: Notification Received Callback Tests", () => { + + it("if one received callback is registered, it should be called accordignly", () => { + + let mockCallback = jest.fn((num: number) => num++); + + Manager.setNotificationReceivedCallback(mockCallback); + expect(mockCallback.mock.calls.length).toBe(0); + + Manager.notificationReceivedDelegate(0); + + expect(mockCallback).toHaveBeenCalledWith(0); + expect(mockCallback.mock.calls.length).toBe(1); + }); + + it("if multiple received callback are registered, they should be called accordignly", () => { + + let mockCallback1 = jest.fn((num: number) => num++); + let mockCallback2 = jest.fn((num: number) => num++); + + Manager.setNotificationReceivedCallback(mockCallback1); + Manager.setNotificationReceivedCallback(mockCallback2); + + expect(mockCallback1.mock.calls.length).toBe(0); + expect(mockCallback2.mock.calls.length).toBe(0); + + Manager.notificationReceivedDelegate(0); + + expect(mockCallback1.mock.calls.length).toBe(1); + expect(mockCallback2.mock.calls.length).toBe(1); + + expect(mockCallback1).toHaveBeenCalledWith(0); + expect(mockCallback2).toHaveBeenCalledWith(0); + }); + + it("if multiple received callback are registered, but one is removed, only one should be called", () => { + + let mockCallback1 = jest.fn((num: number) => num++); + let mockCallback2 = jest.fn((num: number) => num++); + + Manager.setNotificationReceivedCallback(mockCallback1); + Manager.setNotificationReceivedCallback(mockCallback2); + + expect(mockCallback1.mock.calls.length).toBe(0); + expect(mockCallback2.mock.calls.length).toBe(0); + + Manager.unregisterReceivedCallback(mockCallback1); + Manager.notificationReceivedDelegate(0); + + expect(mockCallback1.mock.calls.length).toBe(0); + expect(mockCallback2.mock.calls.length).toBe(1); + + expect(mockCallback2).toHaveBeenCalledWith(0); + }); + +}); + +describe("One Signal Manager: Notification Opened Callback Tests", () => { + + it("if one opened callback is registered, it should be called accordignly", () => { + + let mockCallback = jest.fn((num: number) => num++); + + Manager.setNotificationOpenedCallback(mockCallback); + expect(mockCallback.mock.calls.length).toBe(0); + + Manager.notificationOpenedDelegate(0); + + expect(mockCallback).toHaveBeenCalledWith(0); + expect(mockCallback.mock.calls.length).toBe(1); + }); + + it("if multiple Opened callback are registered, they should be called accordignly", () => { + + let mockCallback1 = jest.fn((num: number) => num++); + let mockCallback2 = jest.fn((num: number) => num++); + + Manager.setNotificationOpenedCallback(mockCallback1); + Manager.setNotificationOpenedCallback(mockCallback2); + + expect(mockCallback1.mock.calls.length).toBe(0); + expect(mockCallback2.mock.calls.length).toBe(0); + + Manager.notificationOpenedDelegate(0); + + expect(mockCallback1.mock.calls.length).toBe(1); + expect(mockCallback2.mock.calls.length).toBe(1); + + expect(mockCallback1).toHaveBeenCalledWith(0); + expect(mockCallback2).toHaveBeenCalledWith(0); + }); + + it("if multiple Opened callback are registered, but one is removed, only one should be called", () => { + + let mockCallback1 = jest.fn((num: number) => num++); + let mockCallback2 = jest.fn((num: number) => num++); + + Manager.setNotificationOpenedCallback(mockCallback1); + Manager.setNotificationOpenedCallback(mockCallback2); + + expect(mockCallback1.mock.calls.length).toBe(0); + expect(mockCallback2.mock.calls.length).toBe(0); + + Manager.unregisterOpenedCallback(mockCallback1); + Manager.notificationOpenedDelegate(0); + + expect(mockCallback1.mock.calls.length).toBe(0); + expect(mockCallback2.mock.calls.length).toBe(1); + + expect(mockCallback2).toHaveBeenCalledWith(0); + }); + +}); + +describe("One Signal Manager: Both Callback Tests", () => { + + it("if both callbacks are registered, and only one is called, then it should work as expected", () => { + + let openCallback = jest.fn((num: number) => num++); + let receivedCallback = jest.fn((num: number) => num++); + + Manager.setNotificationOpenedCallback(openCallback); + Manager.setNotificationReceivedCallback(receivedCallback); + + expect(openCallback.mock.calls.length).toBe(0); + expect(receivedCallback.mock.calls.length).toBe(0); + + Manager.notificationOpenedDelegate(0); + + expect(openCallback).toHaveBeenCalledWith(0); + expect(openCallback.mock.calls.length).toBe(1); + expect(receivedCallback.mock.calls.length).toBe(0); + + Manager.notificationReceivedDelegate(1); + + expect(receivedCallback).toHaveBeenCalledWith(1); + expect(receivedCallback.mock.calls.length).toBe(1); + expect(openCallback.mock.calls.length).toBe(1); + }); + +}); \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..97ff8325 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "allowUnreachableCode": false, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "pretty": true, + "strict": true, + "esModuleInterop": true, + "lib": ["dom", "es2020"], + "module": "esnext", + "moduleResolution": "node", + "target": "es2017" + }, + "include": [ + "src/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 00000000..5370a15c --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,17 @@ +import { defineConfig } from 'vite'; +import dts from 'vite-plugin-dts'; + +export default defineConfig({ + plugins: [dts()], + build: { + minify: false, + outDir: 'outsystems/scripts/ODC', + target: 'modules', + lib: { + entry: './src/outsystems/index.ts', + name: 'OSOneSignal', + fileName: (format) => `manager.${format === 'es' ? 'mjs' : format === 'cjs' ? 'cjs' : 'js'}`, + formats: ['umd'], + } + }, +}); \ No newline at end of file