From 0572b378a24fa84c7ed7ed0d540658fb58039c4d Mon Sep 17 00:00:00 2001 From: David Durman Date: Wed, 2 Oct 2024 13:47:59 +0200 Subject: [PATCH] utils.controls.Digest (#162) * utils.controls.Digest * fix linting errors * increase minor version of the controls bundle, add changelog entry with Digest component * fix output for array output type * add index and count to outputs * fix typo * add lock to prevent "too fast" processing and therefore prevent duplicate output * add option to drain by webhook --- src/appmixer/utils/controls/Digest/Digest.js | 153 ++++++++ .../utils/controls/Digest/component.json | 152 ++++++++ .../utils/controls/Digest/package-lock.json | 63 ++++ .../utils/controls/Digest/package.json | 8 + .../controls/ListTimeZones/ListTimeZones.js | 18 + .../controls/ListTimeZones/component.json | 21 ++ .../controls/ListTimeZones/timezones.json | 337 ++++++++++++++++++ src/appmixer/utils/controls/bundle.json | 5 +- 8 files changed, 756 insertions(+), 1 deletion(-) create mode 100644 src/appmixer/utils/controls/Digest/Digest.js create mode 100644 src/appmixer/utils/controls/Digest/component.json create mode 100644 src/appmixer/utils/controls/Digest/package-lock.json create mode 100644 src/appmixer/utils/controls/Digest/package.json create mode 100644 src/appmixer/utils/controls/ListTimeZones/ListTimeZones.js create mode 100644 src/appmixer/utils/controls/ListTimeZones/component.json create mode 100644 src/appmixer/utils/controls/ListTimeZones/timezones.json diff --git a/src/appmixer/utils/controls/Digest/Digest.js b/src/appmixer/utils/controls/Digest/Digest.js new file mode 100644 index 000000000..b3477b935 --- /dev/null +++ b/src/appmixer/utils/controls/Digest/Digest.js @@ -0,0 +1,153 @@ +'use strict'; + +const parser = require('cron-parser'); +const moment = require('moment'); + +module.exports = { + + async receive(context) { + + const { threshold, generateOutputPortOptions, getWebhookUrl, outputType = 'array' } = context.properties; + + if (getWebhookUrl) { + return context.sendJson({ + inputs: { + webhookUrl: { + defaultValue: context.getWebhookUrl() + } + } + }, 'out'); + } + + if (generateOutputPortOptions) { + return this.getOutputPortOptions(context, outputType); + } + + let lock; + try { + lock = await context.lock(context.componentId); + + const entries = await context.stateGet('entries') || []; + + if (context.messages.webhook) { + // Manually drained by webhook. + await this.sendEntries(context, entries, outputType); + await context.stateUnset('entries'); + return context.response(); + } + + if (context.messages.timeout) { + if (!threshold || (threshold && entries.length >= threshold)) { + if (entries.length > 0) { + await this.sendEntries(context, entries, outputType); + await context.stateUnset('entries'); + } + } + const previousDate = context.messages.timeout.content.previousDate; + return this.scheduleDrain(context, { previousDate }); + } + + const { entry } = context.messages.in.content; + entries.push(entry); + await context.stateSet('entries', entries); + + if (threshold) { + if (entries.length >= threshold) { + await this.sendEntries(context, entries, outputType); + await context.stateUnset('entries'); + } + } + } finally { + if (lock) { + lock.unlock(); + } + } + }, + + async start(context) { + + const { minute, hour, dayMonth, dayWeek } = context.properties; + + if (minute || hour || dayMonth || dayWeek) { + return this.scheduleDrain(context, { previousDate: null }); + } + }, + + async sendEntries(context, entries = [], outputType) { + + if (outputType === 'first') { + if (entries.length) { + await context.sendJson({ entry: entries[0], index: 0, count: entries.length }, 'out'); + } + } else if (outputType === 'object') { + for (let index = 0; index < entries.length; index++) { + const entry = entries[index]; + await context.sendJson({ entry, index, count: entries.length }, 'out'); + } + } else if (outputType === 'array') { + return context.sendJson({ entries, count: entries.length }, 'out'); + } else if (outputType === 'file') { + if (entries.length) { + // Stored into CSV file. + const headers = Object.keys(entries[0] || {}); + let csvRows = []; + csvRows.push(headers.join(',')); + for (const entry of entries) { + const values = headers.map(header => { + const val = entry[header]; + return `"${val}"`; + }); + csvRows.push(values.join(',')); + } + const csvString = csvRows.join('\n'); + let buffer = Buffer.from(csvString, 'utf8'); + const fileName = `utils-controls-Digest-${(new Date).toISOString()}.csv`; + const savedFile = await context.saveFileStream(fileName, buffer); + await context.sendJson({ fileId: savedFile.fileId, count: entries.length }, 'out'); + } + } + }, + + async scheduleDrain(context, { previousDate = null }) { + + const { timezone, minute, hour, dayMonth, dayWeek } = context.properties; + if (timezone && !moment.tz.zone(timezone)) { + throw new context.CancelError('Invalid timezone'); + } + + const expression = `${minute} ${hour} ${dayMonth} * ${dayWeek}`; + const options = timezone ? { tz: timezone } : {}; + const interval = parser.parseExpression(expression, options); + if (!interval.hasNext()) { + throw new context.CancelError('Next scheduled date doesn\'t exist'); + } + + const now = moment().toISOString(); + const nextDate = interval.next().toISOString(); + previousDate = previousDate ? moment(previousDate).toISOString() : null; + + const diff = moment(nextDate).diff(now); + await context.setTimeout({ previousDate: now }, diff); + }, + + getOutputPortOptions(context, outputType) { + + if (outputType === 'object' || outputType === 'first') { + return context.sendJson([ + { label: 'Current Entry Index', value: 'index', schema: { type: 'integer' } }, + { label: 'Entries Count', value: 'count', schema: { type: 'integer' } }, + { label: 'Entry', value: 'entry' } + ], 'out'); + } else if (outputType === 'array') { + return context.sendJson([ + { label: 'Entries Count', value: 'count', schema: { type: 'integer' } }, + { label: 'Entries', value: 'entries', schema: { type: 'array' } } + ], 'out'); + } else if (outputType === 'file') { + return context.sendJson([ + { label: 'Entries Count', value: 'count', schema: { type: 'integer' } }, + { label: 'File ID', value: 'fileId', schema: { type: 'string', format: 'appmixer-file-id' } } + ], 'out'); + } + } +}; diff --git a/src/appmixer/utils/controls/Digest/component.json b/src/appmixer/utils/controls/Digest/component.json new file mode 100644 index 000000000..6e88f41a8 --- /dev/null +++ b/src/appmixer/utils/controls/Digest/component.json @@ -0,0 +1,152 @@ +{ + "name": "appmixer.utils.controls.Digest", + "author": "Appmixer ", + "description": "Compile data in a single batch and send it at regular intervals or when a certain number of entries is reached.", + "version": "1.0.0", + "properties": { + "schema": { + "properties": { + "threshold": { "type": "number" }, + "getWebhookUrl": { "type": "boolean" }, + "webhookUrl": { "type": "string" }, + "minute": { "type": "string" }, + "hour": { "type": "string" }, + "dayMonth": { "type": "string" }, + "dayWeek": { "type": "string" }, + "timezone": { "type": "string" }, + "outputType": { "type": "string" } + } + }, + "inspector": { + "groups": { + "threshold": { + "label": "Drain by Threshold", + "index": 1, + "open": true + }, + "webhook": { + "label": "Drain by Webhook", + "index": 2, + "open": false + }, + "schedule": { + "label": "Drain by Schedule", + "index": 3, + "open": false + } + }, + "inputs": { + "threshold": { + "group": "threshold", + "type": "number", + "index": 1, + "label": "Threshold", + "tooltip": "Enter the number of entries that will trigger the output. If both the threshold and the interval (configuration below) are set, the output will be triggered in regular intervals but only if the threshold is reached, i.e. both conditions must be met. If you only want to trigger the output when the threshold is reached, leave the interval configuration below empty. If you only want to trigger the output at regular intervals, leave the threshold empty." + }, + "webhookUrl": { + "group": "webhook", + "type": "text", + "index": 2, + "label": "Webhook URL to Drain Entries", + "tooltip": "Optionally, you can send a POST request to this URL to manually drain the entries at any time (e.g., even when the threshold is not reached). In other words, sending a POST request to the URL releases all the collected entries and triggers an output.", + "readonly": true, + "source": { + "url": "/component/appmixer/utils/controls/Digest?outPort=out", + "data": { + "properties": { "getWebhookUrl": true } + } + } + }, + "minute": { + "group": "schedule", + "type": "text", + "index": 3, + "label": "Minute", + "tooltip": "Allowed characters are *, -, /, 0-59. Specify the minute of the hour when the digest will be sent. If the minute is set to *, the digest will be sent every minute. Use the - character to specify range of values, e.g. 1-5 means all 1st, 2nd, 3rd, 4th and 5th minute of the hour. Use the / character to specify a step value, e.g. 0-20/2 means every second minute from 0 through 20 minutes of the hour. Use the , character to specify a list of values, e.g. 1,5,10 means the 1st, 5th and 10th minute of the hour." + }, + "hour": { + "group": "schedule", + "type": "text", + "label": "Hour", + "index": 4, + "tooltip": "Allowed values are *, -, /, 0-23. Specify the hour of the day when the digest will be sent. If the hour is set to *, the digest will be sent every hour. Use the - character to specify range of values, e.g. 1-5 means all 1st, 2nd, 3rd, 4th and 5th hour of the day. Use the / character to specify a step value, e.g. 0-20/2 means every second hour from 0 through 20 hours of the day. Use the , character to specify a list of values, e.g. 1,5,10 means the 1st, 5th and 10th hour of the day." + }, + "dayMonth": { + "group": "schedule", + "type": "text", + "index": 5, + "label": "Day of the Month", + "tooltip": "Allowed values are *, -, /, 1-31. Specify the day of the month when the digest will be sent. If the day is set to *, the digest will be sent every day. Use the - character to specify range of values, e.g. 1-5 means all 1st, 2nd, 3rd, 4th and 5th day of the month. Use the / character to specify a step value, e.g. 0-20/2 means every second day from 0 through 20 days of the month. Use the , character to specify a list of values, e.g. 1,5,10 means the 1st, 5th and 10th day of the month." + }, + "dayWeek": { + "group": "schedule", + "type": "text", + "index": 6, + "label": "Day of the Week", + "tooltip": "Allowed values are *, -, /, 0-6, SUN-SAT. Specify the day of the week when the digest will be sent. If the day is set to *, the digest will be sent every day. Use the - character to specify range of values, e.g. 1-3 means all Monday, Tuesday and Wednesday. Use the / character to specify a step value, e.g. 1-5/2 means every second day of the week from Monday through Friday. Use the , character to specify a list of values, e.g. 1,2 means on Monday and Tuesday of the week." + }, + "timezone": { + "group": "schedule", + "type": "text", + "index": 7, + "label": "Timezone", + "tooltip": "Specify the timezone for scheduling (e.g., 'Europe/Prague'). GMT is used by default.", + "source": { + "url": "/component/appmixer/utils/controls/ListTimeZones?outPort=out", + "data": { + "properties": { "sendWholeArray": true }, + "transform": "./ListTimeZones#timezonesToSelectArray" + } + } + }, + "outputType": { + "type": "select", + "label": "Output Type", + "index": 8, + "defaultValue": "array", + "tooltip": "Choose whether you want to receive the entries as one complete list, or one entry at a time (as soon as the threshold or interval conditions are met, one entry right after the another, at the same time) or a CSV file with all items.", + "options": [ + { "label": "First Entry", "value": "first" }, + { "label": "All entries at once", "value": "array" }, + { "label": "One entry at a time", "value": "object" }, + { "label": "CSV file with all entries", "value": "file" } + ] + } + } + } + }, + "inPorts": [ + { + "name": "in", + "schema": { + "properties": { + "entry": {} + } + }, + "inspector": { + "inputs": { + "entry": { + "type": "text", + "index": 1, + "label": "Entry", + "tooltip": "Enter the data that will be added to the digest." + } + } + } + } + ], + "outPorts": [{ + "name": "out", + "source": { + "url": "/component/appmixer/utils/controls/Digest?outPort=out", + "data": { + "properties": { + "generateOutputPortOptions": true, + "outputType": "properties/outputType" + } + } + } + }], + "icon": "data:image/png;base64,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" +} + diff --git a/src/appmixer/utils/controls/Digest/package-lock.json b/src/appmixer/utils/controls/Digest/package-lock.json new file mode 100644 index 000000000..328142355 --- /dev/null +++ b/src/appmixer/utils/controls/Digest/package-lock.json @@ -0,0 +1,63 @@ +{ + "name": "appmixer.utils.controls.Digest", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "appmixer.utils.controls.Digest", + "version": "1.0.0", + "dependencies": { + "cron-parser": "4.9.0", + "moment": "2.30.1" + } + }, + "node_modules/cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "dependencies": { + "luxon": "^3.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } + } + }, + "dependencies": { + "cron-parser": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", + "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", + "requires": { + "luxon": "^3.2.1" + } + }, + "luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==" + }, + "moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==" + } + } +} diff --git a/src/appmixer/utils/controls/Digest/package.json b/src/appmixer/utils/controls/Digest/package.json new file mode 100644 index 000000000..74103c370 --- /dev/null +++ b/src/appmixer/utils/controls/Digest/package.json @@ -0,0 +1,8 @@ +{ + "name": "appmixer.utils.controls.Digest", + "version": "1.0.0", + "dependencies": { + "cron-parser": "4.9.0", + "moment": "2.30.1" + } +} diff --git a/src/appmixer/utils/controls/ListTimeZones/ListTimeZones.js b/src/appmixer/utils/controls/ListTimeZones/ListTimeZones.js new file mode 100644 index 000000000..b7b57da30 --- /dev/null +++ b/src/appmixer/utils/controls/ListTimeZones/ListTimeZones.js @@ -0,0 +1,18 @@ +'use strict'; +const timezones = require('./timezones.json'); + +module.exports = { + + async receive(context) { + + await context.sendJson({ timezones }, 'out'); + + }, + timezonesToSelectArray({ timezones }) { + + return timezones.map(timezone => { + return { label: `${timezone.name} (${timezone.timezone})`, value: timezone.timezone }; + }); + } +}; + diff --git a/src/appmixer/utils/controls/ListTimeZones/component.json b/src/appmixer/utils/controls/ListTimeZones/component.json new file mode 100644 index 000000000..3a3b2e782 --- /dev/null +++ b/src/appmixer/utils/controls/ListTimeZones/component.json @@ -0,0 +1,21 @@ +{ + "name": "appmixer.utils.controls.ListTimeZones", + "author": "Appmixer ", + "description": "List all timezones.", + "private": true, + "version": "1.0.0", + "inPorts": [ + { + "name": "in" + } + ], + "outPorts": [ + { + "name": "out", + "options": [ + { "label": "Timezones", "value": "timezones" } + ] + } + ], + "icon": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij4KICA8ZyBpZD0iR3JvdXBfNTQzIiBkYXRhLW5hbWU9Ikdyb3VwIDU0MyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTIyIC00NDYuNSkiPgogICAgPHJlY3QgaWQ9IlJlY3RhbmdsZV8zMzM3IiBkYXRhLW5hbWU9IlJlY3RhbmdsZSAzMzM3IiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIyIDQ0Ni41KSIgZmlsbD0ibm9uZSIvPgogICAgPGcgaWQ9ImFwcC51dGlscy50aW1lcnMiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDI0IDQ0OSkiPgogICAgICA8cGF0aCBpZD0iYWxhcm0tY2xvY2siIGQ9Ik0xMSwxMGgyYTEsMSwwLDAsMSwwLDJIMTBhMSwxLDAsMCwxLTEtMVY2YTEsMSwwLDAsMSwyLDBabTcuNjMtMS41NjJhOSw5LDAsMSwxLTE3LjI2LDBBNSw1LDAsMSwxLDkuMDM4LDIuMDUxYTkuMSw5LjEsMCwwLDEsMS45MjQsMEE1LDUsMCwxLDEsMTguNjMsOC40MzhabS0uOTM4LTIuMTEzYTMsMywwLDAsMC00LjQ4LTMuNzM1LDkuMDMsOS4wMywwLDAsMSw0LjQ4LDMuNzM1Wk02Ljc4NywyLjU5YTMsMywwLDAsMC00LjQ4LDMuNzM1QTkuMDMsOS4wMywwLDAsMSw2Ljc4NywyLjU5Wk0xMCwxOGE3LDcsMCwxLDAtNy03LDcsNywwLDAsMCw3LDdaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLjAwMSAwLjAwMSkiLz4KICAgIDwvZz4KICA8L2c+Cjwvc3ZnPgo=" +} diff --git a/src/appmixer/utils/controls/ListTimeZones/timezones.json b/src/appmixer/utils/controls/ListTimeZones/timezones.json new file mode 100644 index 000000000..919ea4052 --- /dev/null +++ b/src/appmixer/utils/controls/ListTimeZones/timezones.json @@ -0,0 +1,337 @@ +[ { + "name" : "(GMT-11:00) Midway Island", + "timezone" : "Pacific/Midway" + },{ + "name" : "(GMT-11:00) Samoa", + "timezone" : "US/Samoa" + },{ + "name" : "(GMT-10:00) Hawaii", + "timezone" : "US/Hawaii" + },{ + "name" : "(GMT-09:00) Alaska", + "timezone" : "US/Alaska" + },{ + "name" : "(GMT-08:00) Pacific Time (US & Canada)", + "timezone" : "US/Pacific" + },{ + "name" : "(GMT-08:00) Tijuana", + "timezone" : "America/Tijuana" + },{ + "name" : "(GMT-07:00) Arizona", + "timezone" : "US/Arizona" + },{ + "name" : "(GMT-07:00) Mountain Time (US & Canada)", + "timezone" : "US/Mountain" + },{ + "name" : "(GMT-07:00) Chihuahua", + "timezone" : "America/Chihuahua" + },{ + "name" : "(GMT-07:00) Mazatlan", + "timezone" : "America/Mazatlan" + },{ + "name" : "(GMT-06:00) Mexico City", + "timezone" : "America/Mexico_City" + },{ + "name" : "(GMT-06:00) Monterrey", + "timezone" : "America/Monterrey" + },{ + "name" : "(GMT-06:00) Saskatchewan", + "timezone" : "Canada/Saskatchewan" + },{ + "name" : "(GMT-06:00) Central Time (US & Canada)", + "timezone" : "US/Central" + },{ + "name" : "(GMT-05:00) Eastern Time (US & Canada)", + "timezone" : "US/Eastern" + },{ + "name" : "(GMT-05:00) Indiana (East)", + "timezone" : "US/East-Indiana" + },{ + "name" : "(GMT-05:00) Bogota", + "timezone" : "America/Bogota" + },{ + "name" : "(GMT-05:00) Lima", + "timezone" : "America/Lima" + },{ + "name" : "(GMT-04:30) Caracas", + "timezone" : "America/Caracas" + },{ + "name" : "(GMT-04:00) Atlantic Time (Canada)", + "timezone" : "Canada/Atlantic" + },{ + "name" : "(GMT-04:00) La Paz", + "timezone" : "America/La_Paz" + },{ + "name" : "(GMT-04:00) Santiago", + "timezone" : "America/Santiago" + },{ + "name" : "(GMT-03:30) Newfoundland", + "timezone" : "Canada/Newfoundland" + },{ + "name" : "(GMT-03:00) Buenos Aires", + "timezone" : "America/Buenos_Aires" + },{ + "name" : "(GMT-03:00) Greenland", + "timezone" : "Greenland" + },{ + "name" : "(GMT-02:00) Stanley", + "timezone" : "Atlantic/Stanley" + },{ + "name" : "(GMT-01:00) Azores", + "timezone" : "Atlantic/Azores" + },{ + "name" : "(GMT-01:00) Cape Verde Is.", + "timezone" : "Atlantic/Cape_Verde" + },{ + "name" : "(GMT) Casablanca", + "timezone" : "Africa/Casablanca" + },{ + "name" : "(GMT) Dublin", + "timezone" : "Europe/Dublin" + },{ + "name" : "(GMT) Lisbon", + "timezone" : "Europe/Lisbon" + },{ + "name" : "(GMT) London", + "timezone" : "Europe/London" + },{ + "name" : "(GMT) Monrovia", + "timezone" : "Africa/Monrovia" + },{ + "name" : "(GMT+01:00) Amsterdam", + "timezone" : "Europe/Amsterdam" + },{ + "name" : "(GMT+01:00) Belgrade", + "timezone" : "Europe/Belgrade" + },{ + "name" : "(GMT+01:00) Berlin", + "timezone" : "Europe/Berlin" + },{ + "name" : "(GMT+01:00) Bratislava", + "timezone" : "Europe/Bratislava" + },{ + "name" : "(GMT+01:00) Brussels", + "timezone" : "Europe/Brussels" + },{ + "name" : "(GMT+01:00) Budapest", + "timezone" : "Europe/Budapest" + },{ + "name" : "(GMT+01:00) Copenhagen", + "timezone" : "Europe/Copenhagen" + },{ + "name" : "(GMT+01:00) Ljubljana", + "timezone" : "Europe/Ljubljana" + },{ + "name" : "(GMT+01:00) Madrid", + "timezone" : "Europe/Madrid" + },{ + "name" : "(GMT+01:00) Paris", + "timezone" : "Europe/Paris" + },{ + "name" : "(GMT+01:00) Prague", + "timezone" : "Europe/Prague" + },{ + "name" : "(GMT+01:00) Rome", + "timezone" : "Europe/Rome" + },{ + "name" : "(GMT+01:00) Sarajevo", + "timezone" : "Europe/Sarajevo" + },{ + "name" : "(GMT+01:00) Skopje", + "timezone" : "Europe/Skopje" + },{ + "name" : "(GMT+01:00) Stockholm", + "timezone" : "Europe/Stockholm" + },{ + "name" : "(GMT+01:00) Vienna", + "timezone" : "Europe/Vienna" + },{ + "name" : "(GMT+01:00) Warsaw", + "timezone" : "Europe/Warsaw" + },{ + "name" : "(GMT+01:00) Zagreb", + "timezone" : "Europe/Zagreb" + },{ + "name" : "(GMT+02:00) Athens", + "timezone" : "Europe/Athens" + },{ + "name" : "(GMT+02:00) Bucharest", + "timezone" : "Europe/Bucharest" + },{ + "name" : "(GMT+02:00) Cairo", + "timezone" : "Africa/Cairo" + },{ + "name" : "(GMT+02:00) Harare", + "timezone" : "Africa/Harare" + },{ + "name" : "(GMT+02:00) Helsinki", + "timezone" : "Europe/Helsinki" + },{ + "name" : "(GMT+02:00) Istanbul", + "timezone" : "Europe/Istanbul" + },{ + "name" : "(GMT+02:00) Jerusalem", + "timezone" : "Asia/Jerusalem" + },{ + "name" : "(GMT+02:00) Kyiv", + "timezone" : "Europe/Kiev" + },{ + "name" : "(GMT+02:00) Minsk", + "timezone" : "Europe/Minsk" + },{ + "name" : "(GMT+02:00) Riga", + "timezone" : "Europe/Riga" + },{ + "name" : "(GMT+02:00) Sofia", + "timezone" : "Europe/Sofia" + },{ + "name" : "(GMT+02:00) Tallinn", + "timezone" : "Europe/Tallinn" + },{ + "name" : "(GMT+02:00) Vilnius", + "timezone" : "Europe/Vilnius" + },{ + "name" : "(GMT+03:00) Baghdad", + "timezone" : "Asia/Baghdad" + },{ + "name" : "(GMT+03:00) Kuwait", + "timezone" : "Asia/Kuwait" + },{ + "name" : "(GMT+03:00) Nairobi", + "timezone" : "Africa/Nairobi" + },{ + "name" : "(GMT+03:00) Riyadh", + "timezone" : "Asia/Riyadh" + },{ + "name" : "(GMT+03:00) Moscow", + "timezone" : "Europe/Moscow" + },{ + "name" : "(GMT+03:30) Tehran", + "timezone" : "Asia/Tehran" + },{ + "name" : "(GMT+04:00) Baku", + "timezone" : "Asia/Baku" + },{ + "name" : "(GMT+04:00) Volgograd", + "timezone" : "Europe/Volgograd" + },{ + "name" : "(GMT+04:00) Muscat", + "timezone" : "Asia/Muscat" + },{ + "name" : "(GMT+04:00) Tbilisi", + "timezone" : "Asia/Tbilisi" + },{ + "name" : "(GMT+04:00) Yerevan", + "timezone" : "Asia/Yerevan" + },{ + "name" : "(GMT+04:30) Kabul", + "timezone" : "Asia/Kabul" + },{ + "name" : "(GMT+05:00) Karachi", + "timezone" : "Asia/Karachi" + },{ + "name" : "(GMT+05:00) Tashkent", + "timezone" : "Asia/Tashkent" + },{ + "name" : "(GMT+05:30) Kolkata", + "timezone" : "Asia/Kolkata" + },{ + "name" : "(GMT+05:45) Kathmandu", + "timezone" : "Asia/Kathmandu" + },{ + "name" : "(GMT+06:00) Ekaterinburg", + "timezone" : "Asia/Yekaterinburg" + },{ + "name" : "(GMT+06:00) Almaty", + "timezone" : "Asia/Almaty" + },{ + "name" : "(GMT+06:00) Dhaka", + "timezone" : "Asia/Dhaka" + },{ + "name" : "(GMT+07:00) Novosibirsk", + "timezone" : "Asia/Novosibirsk" + },{ + "name" : "(GMT+07:00) Bangkok", + "timezone" : "Asia/Bangkok" + },{ + "name" : "(GMT+07:00) Jakarta", + "timezone" : "Asia/Jakarta" + },{ + "name" : "(GMT+08:00) Krasnoyarsk", + "timezone" : "Asia/Krasnoyarsk" + },{ + "name" : "(GMT+08:00) Chongqing", + "timezone" : "Asia/Chongqing" + },{ + "name" : "(GMT+08:00) Hong Kong", + "timezone" : "Asia/Hong_Kong" + },{ + "name" : "(GMT+08:00) Kuala Lumpur", + "timezone" : "Asia/Kuala_Lumpur" + },{ + "name" : "(GMT+08:00) Perth", + "timezone" : "Australia/Perth" + },{ + "name" : "(GMT+08:00) Singapore", + "timezone" : "Asia/Singapore" + },{ + "name" : "(GMT+08:00) Taipei", + "timezone" : "Asia/Taipei" + },{ + "name" : "(GMT+08:00) Ulaan Bataar", + "timezone" : "Asia/Ulaanbaatar" + },{ + "name" : "(GMT+08:00) Urumqi", + "timezone" : "Asia/Urumqi" + },{ + "name" : "(GMT+09:00) Irkutsk", + "timezone" : "Asia/Irkutsk" + },{ + "name" : "(GMT+09:00) Seoul", + "timezone" : "Asia/Seoul" + },{ + "name" : "(GMT+09:00) Tokyo", + "timezone" : "Asia/Tokyo" + },{ + "name" : "(GMT+09:30) Adelaide", + "timezone" : "Australia/Adelaide" + },{ + "name" : "(GMT+09:30) Darwin", + "timezone" : "Australia/Darwin" + },{ + "name" : "(GMT+10:00) Yakutsk", + "timezone" : "Asia/Yakutsk" + },{ + "name" : "(GMT+10:00) Brisbane", + "timezone" : "Australia/Brisbane" + },{ + "name" : "(GMT+10:00) Canberra", + "timezone" : "Australia/Canberra" + },{ + "name" : "(GMT+10:00) Guam", + "timezone" : "Pacific/Guam" + },{ + "name" : "(GMT+10:00) Hobart", + "timezone" : "Australia/Hobart" + },{ + "name" : "(GMT+10:00) Melbourne", + "timezone" : "Australia/Melbourne" + },{ + "name" : "(GMT+10:00) Port Moresby", + "timezone" : "Pacific/Port_Moresby" + },{ + "name" : "(GMT+10:00) Sydney", + "timezone" : "Australia/Sydney" + },{ + "name" : "(GMT+11:00) Vladivostok", + "timezone" : "Asia/Vladivostok" + },{ + "name" : "(GMT+12:00) Magadan", + "timezone" : "Asia/Magadan" + },{ + "name" : "(GMT+12:00) Auckland", + "timezone" : "Pacific/Auckland" + },{ + "name" : "(GMT+12:00) Fiji", + "timezone" : "Pacific/Fiji" + }] \ No newline at end of file diff --git a/src/appmixer/utils/controls/bundle.json b/src/appmixer/utils/controls/bundle.json index 38fd89075..ed8db813c 100644 --- a/src/appmixer/utils/controls/bundle.json +++ b/src/appmixer/utils/controls/bundle.json @@ -1,7 +1,7 @@ { "name": "appmixer.utils.controls", "engine": ">=6.0", - "version": "1.8.3", + "version": "1.9.0", "changelog": { "1.0.0": [ "Initial version" @@ -71,6 +71,9 @@ ], "1.8.3": [ "Enhance OnError to handle large number of errors and added limit." + ], + "1.9.0": [ + "Added Digest utility to \"pile up\" entries until either a threshold is reached or a time interval is timed out at which point the Digest outputs all the entries as a batch." ] } }