From d6f6147e926476da8145e5e6e66d89c271a5098b Mon Sep 17 00:00:00 2001 From: Arturs Sosins Date: Wed, 8 Jan 2025 14:16:03 +0200 Subject: [PATCH 01/28] old hooks --- plugins/hooks/README.md | 134 ---------------- plugins/hooks/api/api.js | 145 +++++------------- .../hooks/api/parts/triggers/api_endpoint.js | 4 +- .../public/localization/hooks.properties | 22 ++- .../frontend/public/templates/vue-table.html | 25 ++- plugins/hooks/tests.js | 10 -- 6 files changed, 58 insertions(+), 282 deletions(-) delete mode 100644 plugins/hooks/README.md diff --git a/plugins/hooks/README.md b/plugins/hooks/README.md deleted file mode 100644 index 97a82046be6..00000000000 --- a/plugins/hooks/README.md +++ /dev/null @@ -1,134 +0,0 @@ -# Countly Hooks Plugin - -The Hooks plugin provides powerful automation for integrating Countly data with external systems. This plugin can trigger external HTTP endpoints based on internal events and incoming data, and send automated email notifications for events like user profile updates or users entering a cohort. Hooks offers a new way to feed external systems with Countly data, enabling real-time interactions and automating workflows. - -## File Structure - -```javascript -hooks/ # Main hooks plugin directory -├── api/ # Backend API logic -│ ├── jobs/ # Job scheduling logic -│ │ └── schedule.js # Handles scheduling tasks -│ └── parts/ # Logic for effects and triggers -│ ├── effects/ # Different types of effects used in hooks -│ │ ├── custom_code.js # Handles custom code execution -│ │ ├── email.js # Manages email-related hooks -│ │ ├── http.js # HTTP requests for hooks -│ │ └── index.js # Effect index for organization -│ └── triggers/ # Triggers for executing hooks -│ ├── api_endpoint.js # API endpoint trigger -│ ├── incoming_data.js # Triggers for incoming data -│ ├── internal_event.js # Internal event trigger -│ ├── scheduled.js # Trigger for scheduled tasks -│ └── index.js # Trigger index file -├── api.js # Main API logic for backend requests -├── testData.js # Sample test data for hooks -├── utils.js # Utility functions for hooks -├── frontend/ # Frontend resources -│ ├── public/ # Publicly accessible files -│ │ ├── javascripts/ # JavaScript for frontend logic -│ │ │ ├── countly.hooks.effects.js # Effect logic for frontend hooks -│ │ │ ├── countly.models.js # Model definitions for hooks -│ │ │ └── countly.views.js # View logic for rendering hooks -│ │ ├── localization/ # Localization files for translations -│ │ ├── stylesheets/ # CSS and SCSS for styling hooks UI -│ │ │ ├── vue-main.css # Compiled CSS for UI -│ │ │ └── vue-main.scss # Source SCSS file for styling -│ │ └── templates/ # HTML templates for UI components -│ │ ├── vue-drawer.html # Drawer UI for hooks -│ │ ├── vue-effects.html # Effects UI template -│ │ ├── vue-hooks-detail-error-table.html # Template for error table -│ │ ├── vue-hooks-detail.html # Detail view of individual hooks -│ │ ├── vue-main.html # Main template for hooks -│ │ └── vue-table.html # Table template for hooks display -│ └── app.js # Main frontend application logic -├── install.js # Installation script for the plugin -├── package-lock.json # Lock file for Node.js dependencies -├── package.json # Package configuration for Node.js -├── tests.js # Test scripts for validating hooks functionality -└── uninstall.js # Uninstallation script for removing the plugin -``` - -## Installation - -1. Navigate to the directory where the Hooks plugin is located. This could be a relative or absolute path depending on your environment setup: - - ```bash - cd /path/to/your/project/hooks - ``` - -2. Install dependencies: - - ```bash - npm install - ``` - -## Using Hooks - -First, ensure that Hooks is enabled. In the Sidebar, navigate to Management > Feature Management and switch on the Hooks toggle. - -## Creating a New Hook - -To create a new Hook in the Countly Hooks Plugin, follow these steps: - -### Step 1: Start a New Hook - -- Click on the **+ New Hook** button located in the top right corner of the default view. A new drawer will open where you’ll need to fill out the following fields. - -### Step 2: Fill in Hook Details - -1. **Hook Name**: Enter a concise and descriptive name for your Hook. This field is required and should adhere to SDK naming limitations. -2. **Description** (optional): Provide a brief explanation of the Hook’s purpose. This can help your colleagues understand why it was created. Avoid long descriptions and special characters. -3. **Source App**: Select the application where the Hook will apply. You can use the search bar to find your app, but only one app can be chosen per Hook. Click on **Next Step** to proceed to trigger selection. - -### Step 3: Select the Trigger Type - -From the dropdown, choose a trigger type for your Hook. Options include: - -- **Tracked Data** -- **Internal Actions** -- **API Endpoint** - -#### For Tracked Data - -- Choose a specific data point from the list or dropdown. -- Set a filtering rule if needed; you may add multiple conditions. - -#### For Internal Actions - -- Select a trigger from the dropdown. -- Depending on the chosen trigger, specify the details using the subsequent dropdown (e.g., select a specific cohort from the list). - -#### For API Endpoint - -- Choose a trigger from the dropdown. -- Specify further using the new dropdown that appears (e.g., select a cohort). - -### Step 4: Set up a Recurring Trigger (Optional) - -Select one of the following frequencies for the trigger: - -- **Every hour** (includes time zone selection) -- **Every day** (includes time zone selection) -- **Every week** (select day of the week, hour, and time zone) -- **Every month** (select day of the month from 1 to 28, hour, and time zone) - -Then, specify when the trigger should run. - -### Step 5: Choose an Action Type - -Choose an action from the dropdown options: - -- **Send Email** -- **Make HTTP Request** -- **Custom Code** - -You can add multiple actions by clicking **Add Action**. - -### Step 6: Test the Hook (Optional) - -- To verify if the Hook functions as expected, click the **Test the Hook** button. For further guidance, refer to the “Testing a Hook” section below. - -### Step 7: Save the Hook - -After completing all fields, click **Save**. A success message will confirm that your Hook has been saved successfully. diff --git a/plugins/hooks/api/api.js b/plugins/hooks/api/api.js index 4edf78272e5..7c290e822f3 100644 --- a/plugins/hooks/api/api.js +++ b/plugins/hooks/api/api.js @@ -2,6 +2,7 @@ const Triggers = require('./parts/triggers/index.js'); const Effects = require('./parts/effects/index.js'); const asyncLib = require('async'); const EventEmitter = require('events'); + const common = require('../../../api/utils/common.js'); const { validateRead, validateCreate, validateDelete, validateUpdate } = require('../../../api/utils/rights.js'); const plugins = require('../../pluginManager.js'); @@ -272,82 +273,46 @@ plugins.register("/permissions/features", function(ob) { plugins.register("/i/hook/save", function(ob) { let paramsInstance = ob.params; + validateCreate(ob.params, FEATURE_NAME, function(params) { let hookConfig = params.qstring.hook_config; - if (!hookConfig) { - common.returnMessage(params, 400, 'Invalid hookConfig'); - return true; - } - try { hookConfig = JSON.parse(hookConfig); hookConfig = sanitizeConfig(hookConfig); - if (hookConfig) { - // Null check for hookConfig - if (!(common.validateArgs(hookConfig, CheckHookProperties(hookConfig)))) { - common.returnMessage(params, 400, 'Not enough args'); - return true; - } + if (!(common.validateArgs(hookConfig, CheckHookProperties(hookConfig)))) { + common.returnMessage(params, 400, 'Not enough args'); + return true; + } - if (hookConfig.effects && !validateEffects(hookConfig.effects)) { - common.returnMessage(params, 400, 'Invalid configuration for effects'); - return true; - } + if (hookConfig && hookConfig.effects && !validateEffects(hookConfig.effects)) { + common.returnMessage(params, 400, 'Invalid configuration for effects'); + return true; + } - if (hookConfig._id) { - const id = hookConfig._id; - delete hookConfig._id; - return common.db.collection("hooks").findAndModify( - { _id: common.db.ObjectID(id) }, - {}, - {$set: hookConfig}, - {new: true}, - function(err, result) { - if (!err) { - // Audit log: Hook updated - if (result && result.value) { - plugins.dispatch("/systemlogs", { - params: params, - action: "hook_updated", - data: { - updatedHookID: result.value._id, - updatedBy: params.member._id, - updatedHookName: result.value.name - } - }); - } - else { - common.returnMessage(params, 500, "No result found"); - } - common.returnOutput(params, result && result.value); - } - else { - common.returnMessage(params, 500, "Failed to save an hook"); - } + if (hookConfig._id) { + const id = hookConfig._id; + delete hookConfig._id; + return common.db.collection("hooks").findAndModify( + { _id: common.db.ObjectID(id) }, + {}, + {$set: hookConfig}, + {new: true}, + function(err, result) { + if (!err) { + common.returnOutput(params, result && result.value); } - ); - } - - } - if (hookConfig) { - hookConfig.createdBy = params.member._id; // Accessing property now with proper check - hookConfig.created_at = new Date().getTime(); + else { + common.returnMessage(params, 500, "Failed to save an hook"); + } + }); } + hookConfig.createdBy = params.member._id; + hookConfig.created_at = new Date().getTime(); return common.db.collection("hooks").insert( hookConfig, function(err, result) { log.d("insert new hook:", err, result); if (!err && result && result.insertedIds && result.insertedIds[0]) { - // Audit log: Hook created - plugins.dispatch("/systemlogs", { - params: params, - action: "hook_created", - data: { - createdHookID: hookConfig._id, - createdBy: params.member._id, - createdHookName: hookConfig.name - } - }); common.returnOutput(params, result.insertedIds[0]); } else { @@ -502,8 +467,8 @@ plugins.register("/o/hook/list", function(ob) { }); } catch (err) { - log.e('get hook list failed', err); - common.returnMessage(params, 500, "Failed to get hook list" + err.message); + log.e('get hook list failed'); + common.returnMessage(params, 500, "Failed to get hook list"); } }, paramsInstance); return true; @@ -544,16 +509,7 @@ plugins.register("/i/hook/status", function(ob) { } Promise.all(batch).then(function() { log.d("hooks all updated."); - // Audit log: Hook status updated - plugins.dispatch("/systemlogs", { - params: params, - action: "hook_status_updated", - data: { updatedHooksCount: Object.keys(statusList).length, requestedBy: params.member._id } - }); common.returnOutput(params, true); - }).catch(function(err) { - log.e('Failed to update hook statuses: ', err); - common.returnMessage(params, 500, "Failed to update hook statuses: " + err.message); }); }, paramsInstance); return true; @@ -587,23 +543,14 @@ plugins.register("/i/hook/delete", function(ob) { function(err, result) { log.d(err, result, "delete an hook"); if (!err) { - // Audit log: Hook deleted - plugins.dispatch("/systemlogs", { - params: params, - action: "hook_deleted", - data: { - deletedHookID: hookID, - requestedBy: params.member._id - } - }); common.returnMessage(params, 200, "Deleted an hook"); } } ); } catch (err) { - log.e('delete hook failed', hookID, err); - common.returnMessage(params, 500, "Failed to delete an hook" + err.message); + log.e('delete hook failed', hookID); + common.returnMessage(params, 500, "Failed to delete an hook"); } }, paramsInstance); return true; @@ -615,56 +562,40 @@ plugins.register("/i/hook/test", function(ob) { validateCreate(paramsInstance, FEATURE_NAME, async(params) => { let hookConfig = params.qstring.hook_config; - if (!hookConfig) { - common.returnMessage(params, 400, 'Invalid hookConfig'); - return; - } - try { hookConfig = JSON.parse(hookConfig); - if (!hookConfig) { - common.returnMessage(params, 400, 'Parsed hookConfig is invalid'); - return; - } hookConfig = sanitizeConfig(hookConfig); const mockData = JSON.parse(params.qstring.mock_data); if (!(common.validateArgs(hookConfig, CheckHookProperties(hookConfig)))) { - common.returnMessage(params, 403, "hook config invalid" + JSON.stringify(hookConfig)); - return; // Add return to exit early + common.returnMessage(params, 403, "hook config invalid"); } - // Null check for effects - if (hookConfig.effects && !validateEffects(hookConfig.effects)) { + if (hookConfig && hookConfig.effects && !validateEffects(hookConfig.effects)) { common.returnMessage(params, 400, 'Config invalid'); - return; // Add return to exit early + return true; } - // trigger process log.d(JSON.stringify(hookConfig), "[hook test config]"); const results = []; // build mock data const trigger = hookConfig.trigger; - if (!trigger) { - common.returnMessage(params, 400, 'Trigger is missing'); - return; - } hookConfig._id = null; - log.d("[hook test mock data]", mockData); const obj = { is_mock: true, params: mockData, rule: hookConfig }; + log.d("[hook test config data]", obj); const t = new Triggers[trigger.type]({ rules: [hookConfig], }); - // output trigger result + // out put trigger result const triggerResult = await t.process(obj); log.d("[hook trigger test result]", triggerResult); results.push(JSON.parse(JSON.stringify(triggerResult))); @@ -695,8 +626,8 @@ plugins.register("/i/hook/test", function(ob) { return false; } catch (e) { - log.e("hook test error", e, hookConfig); - common.returnMessage(params, 503, "Hook test failed." + e.message); + log.e("hook test error", e); + common.returnMessage(params, 503, "Hook test failed."); return; } }, paramsInstance); diff --git a/plugins/hooks/api/parts/triggers/api_endpoint.js b/plugins/hooks/api/parts/triggers/api_endpoint.js index 8424a9d314f..c706d890f8e 100644 --- a/plugins/hooks/api/parts/triggers/api_endpoint.js +++ b/plugins/hooks/api/parts/triggers/api_endpoint.js @@ -47,8 +47,8 @@ class APIEndPointTrigger { */ async process(ob) { // log.d(JSON.stringify(ob), "[hook trigger api_endpoint]"); - const {params} = ob || {}; - const {paths} = params || {}; + const {params} = ob; + const {paths} = params; const hookPath = paths.length >= 4 ? paths[3] : null; const {qstring} = params || {}; diff --git a/plugins/hooks/frontend/public/localization/hooks.properties b/plugins/hooks/frontend/public/localization/hooks.properties index d5c34189a8b..fa28388885a 100644 --- a/plugins/hooks/frontend/public/localization/hooks.properties +++ b/plugins/hooks/frontend/public/localization/hooks.properties @@ -46,6 +46,7 @@ configs.help.hooks-timeWindowForRequestLimit=The time window for the request lim hooks.InternalEventTrigger = Internal Actions hooks.trigger-api-endpoint-uri= API Endpoint hooks.trigger-introduction = Introduction +hooks.trigger-api-endpoint-intro-content = Send a GET request with query string parameter “payload” as a JSON string to the below URL:
{0} hooks.APIEndPointTrigger = API Endpoint hooks.internal-event-selector-title = Internal Actions hooks.internal-event-selector-placeholder = Please select an internal action @@ -79,13 +80,13 @@ hooks.http-method-get = GET hooks.http-method-post = POST hooks.http-effect-description= Use {{payload_json}} to include the entire trigger data as a JSON object in your request body, or {{payload_string}} to include a stringified JSON in your query string. You can also use individual properties within the trigger data such as {{user}}. -hooks.intro-hooks-trigger = /hooks/trigger will capture triggered data from the selected hook trigger. Output: Trigger output data from selected hook. -hooks.intro-i-app_users-delete= /i/app_users/delete will capture deleted user profiles.Output: Individual user profile data -hooks.intro-i-app_users-update = /i/app_users/update will capture updated user profiles.Output: Individual user profile data -hooks.intro-i-app_users-create = /i/app_users/create will capture created user profiles.Output: Individual user profile data -hooks.intro-cohort-exit = /cohort/exit will capture users who exit from the selected cohort. Output: Individual user profile data -hooks.intro-cohort-enter = /cohort/enter will capture users who enter into the selected cohort. Output: Individual user profile data -hooks.intro-incoming-data = Will capture event data when match filter rules. Output: Event data & user profile data. +hooks.intro-hooks-trigger = /hooks/trigger will capture triggered data from the selected hook trigger.
Output: Trigger output data from selected hook. +hooks.intro-i-app_users-delete= /i/app_users/delete will capture deleted user profiles.
Output: Individual user profile data +hooks.intro-i-app_users-update = /i/app_users/update will capture updated user profiles.
Output: Individual user profile data +hooks.intro-i-app_users-create = /i/app_users/create will capture created user profiles.
Output: Individual user profile data +hooks.intro-cohort-exit = /cohort/exit will capture users who exit from the selected cohort.
Output: Individual user profile data +hooks.intro-cohort-enter = /cohort/enter will capture users who enter into the selected cohort.
Output: Individual user profile data +hooks.intro-incoming-data = Will capture event data when match filter rules.
Output: Event data & user profile data. hooks.copy-notify-message = API Endpoint URL is copied. hooks.copy-notify-title = Copy URL hooks.Select_country = Select Country @@ -142,9 +143,4 @@ hooks.actions-tips = Select the actions the hook will do upon being triggered. Y hooks.application-tips = The app(s) for which you want to create a hook. hooks.trigger-count-tips = Number of times the hook has been triggered. hooks.trigger-action-tips = Identifies the trigger for the hook, and the actions that show the method through which data will be sent. -hooks.trigger-save-failed = Hook could not be saved. - -systemlogs.action.hook_created = Hook Created -systemlogs.action.hook_updated = Hook Updated -systemlogs.action.hook_status_updated = Hook Status Updated -systemlogs.action.hook_deleted = Hook Deleted \ No newline at end of file +hooks.trigger-save-failed = Hook could not be saved. \ No newline at end of file diff --git a/plugins/hooks/frontend/public/templates/vue-table.html b/plugins/hooks/frontend/public/templates/vue-table.html index 389d069f389..57019468a6d 100644 --- a/plugins/hooks/frontend/public/templates/vue-table.html +++ b/plugins/hooks/frontend/public/templates/vue-table.html @@ -1,6 +1,5 @@ - + @@ -59,17 +58,11 @@ - - @@ -78,10 +71,10 @@ @@ -71,10 +78,10 @@