diff --git a/app/package.json b/app/package.json index 6bed374..84d044a 100644 --- a/app/package.json +++ b/app/package.json @@ -6,7 +6,9 @@ "scripts": { "build": "yarn rebuild && webpack --mode production", "dev": "node_modules/.bin/webpack-dev-server --mode development --progress", - "rebuild": "cd node_modules/heapdump && node-gyp rebuild --target=7.1.1 --arch=x64 --dist-url=https://atom.io/download/electron || echo Could not build heapdump; cd -" + "rebuild": "cd node_modules/heapdump && node-gyp rebuild --target=7.1.1 --arch=x64 --dist-url=https://atom.io/download/electron || echo Could not build heapdump; cd -", + "test": "TS_NODE_PROJECT=test/tsconfig.json yarn mochatest", + "mochatest": "mocha --require ts-node/register src/**/*.spec.ts" }, "author": "", "license": "CC-BY-ND-4.0", @@ -68,10 +70,12 @@ "@types/uuid": "^7.0.2", "@types/vis": "^4.21.9", "awesome-typescript-loader": "^5.2.1", + "chai": "^4.2.0", "css-loader": "^3.0.0", "hard-source-webpack-plugin": "^0.13.1", "heapdump": "^0.3.12", "html-webpack-plugin": "^4.0.0-beta.5", + "mocha": "^7.1.1", "node-loader": "^0.6.0", "source-map-loader": "^0.2.4", "style-loader": "^1", diff --git a/app/src/actions/ConnectionManager.ts b/app/src/actions/ConnectionManager.ts index f11e850..3239253 100644 --- a/app/src/actions/ConnectionManager.ts +++ b/app/src/actions/ConnectionManager.ts @@ -12,10 +12,11 @@ import { showError } from './Global' import { remote } from 'electron' import { promises as fsPromise } from 'fs' import * as path from 'path' - import { ActionTypes, Action } from '../reducers/ConnectionManager' +import { Subscription } from '../../../backend/src/DataSource/MqttSource' +import { connectionsMigrator } from './migrations/Connection' -interface ConnectionDictionary { +export interface ConnectionDictionary { [s: string]: ConnectionOptions } const storedConnectionsIdentifier: StorageIdentifier = { @@ -27,6 +28,12 @@ export const loadConnectionSettings = () => async (dispatch: Dispatch, getS try { await ensureConnectionsHaveBeenInitialized() connections = await persistentStorage.load(storedConnectionsIdentifier) + + // Apply migrations + if (connections && connectionsMigrator.isMigrationNecessary(connections)) { + connections = connectionsMigrator.applyMigrations(connections) + await persistentStorage.store(storedConnectionsIdentifier, connections) + } } catch (error) { dispatch(showError(error)) } @@ -101,13 +108,13 @@ export const updateConnection = (connectionId: string, changeSet: Partial ({ +export const addSubscription = (subscription: Subscription, connectionId: string): Action => ({ connectionId, subscription, type: ActionTypes.CONNECTION_MANAGER_ADD_SUBSCRIPTION, }) -export const deleteSubscription = (subscription: string, connectionId: string): Action => ({ +export const deleteSubscription = (subscription: Subscription, connectionId: string): Action => ({ connectionId, subscription, type: ActionTypes.CONNECTION_MANAGER_DELETE_SUBSCRIPTION, @@ -174,31 +181,4 @@ async function ensureConnectionsHaveBeenInitialized() { clearLegacyConnectionOptions() } - - // Migrate connections, rewrite dictionary to "keep" it "ordered" (dictionaries do not have a guaranteed order) - const mayNeedMigrations = connections && connections['iot.eclipse.org'] - if (connections && mayNeedMigrations) { - const newConnections = {} - for (const connection of Object.values(connections)) { - addMigratedConnection(newConnections, connection) - } - - await persistentStorage.store(storedConnectionsIdentifier, newConnections) - } -} - -function addMigratedConnection(newConnections: { [key: string]: ConnectionOptions }, connection: ConnectionOptions) { - // The host has been renamed, only change the host if it has not been changed - // Also check for ssl since SSL is not yet working - if ( - connection.id === 'iot.eclipse.org' && - connection.host === 'iot.eclipse.org' && - connection.port === 1883 && - !connection.encryption - ) { - connection.id = 'mqtt.eclipse.org' - connection.host = 'mqtt.eclipse.org' - connection.name = 'mqtt.eclipse.org' - } - newConnections[connection.id] = connection } diff --git a/app/src/actions/migrations/Connection.ts b/app/src/actions/migrations/Connection.ts new file mode 100644 index 0000000..9fc8209 --- /dev/null +++ b/app/src/actions/migrations/Connection.ts @@ -0,0 +1,94 @@ +import { ConfigMigrator, Migration } from '../../utils/ConfigMigrator' +import { ConnectionDictionary } from '../ConnectionManager' +import { ConnectionOptions } from '../../model/ConnectionOptions' + +export interface ConnectionOptionsV0 { + type: 'mqtt' + id: string + host: string + protocol: 'mqtt' | 'ws' + basePath?: string + port: number + name: string + username?: string + password?: string + encryption: boolean + certValidation: boolean + // selfSignedCertificate?: CertificateParameters + // clientCertificate?: CertificateParameters + // clientKey?: CertificateParameters + clientId?: string + subscriptions: Array +} + +let migrations: Migration[] = [ + // iot.eclipse.org ha moved to mqtt.eclipse.org + { + from: undefined, + apply: (connection: ConnectionOptionsV0): ConnectionOptionsV0 => { + if (connection.id == 'iot.eclipse.org' && connection.host == 'iot.eclipse.org' && connection.port == 1883) { + return { + ...connection, + id: 'mqtt.eclipse.org', + host: 'mqtt.eclipse.org', + name: 'mqtt.eclipse.org', + } + } + + return { + ...connection, + } + }, + }, + // Remove stored clientId if it is the default generated client id. This allows to connect multiple instances of mqtt explorer to the same broker. + // A randomly generated clientId will be used if no clientId is set. + { + from: undefined, + apply: (connection: ConnectionOptionsV0): ConnectionOptionsV0 => { + if (connection.clientId && /mqtt-explorer-[0-9a-f]{8}/.test(connection.clientId)) { + return { + ...connection, + clientId: undefined, + } + } + + return { + ...connection, + } + }, + }, + // Added QoS level to subscription options + { + from: undefined, + apply: (connection: ConnectionOptionsV0): ConnectionOptions => { + return { + ...connection, + configVersion: 1, + subscriptions: connection.subscriptions.map(topic => ({ topic, qos: 0 })), + } + }, + }, +] + +const connectionMigrator = new ConfigMigrator(migrations) + +function isMigrationNecessary(connections: ConnectionDictionary): boolean { + return Object.values(connections) + .map(connection => connectionMigrator.isMigrationNecessary(connection)) + .reduce((a, b) => a || b, false) +} + +function applyMigrations(connections: ConnectionDictionary): ConnectionDictionary { + let newConnectionDictionary: ConnectionDictionary = {} + Object.keys(connections).forEach(key => { + let newConnection = connectionMigrator.applyMigrations(connections[key]) as any + newConnectionDictionary[newConnection.id] = newConnection + }) + + return newConnectionDictionary +} + +export const connectionsMigrator = { + isMigrationNecessary, + applyMigrations, +} diff --git a/app/src/components/ConnectionSetup/AdvancedConnectionSettings.tsx b/app/src/components/ConnectionSetup/AdvancedConnectionSettings.tsx index 250c37b..44d2e5a 100644 --- a/app/src/components/ConnectionSetup/AdvancedConnectionSettings.tsx +++ b/app/src/components/ConnectionSetup/AdvancedConnectionSettings.tsx @@ -1,7 +1,6 @@ import * as React from 'react' +import { useState, useCallback, memo } from 'react' import Add from '@material-ui/icons/Add' -import ClearAdornment from '../helper/ClearAdornment' -import Delete from '@material-ui/icons/Delete' import Lock from '@material-ui/icons/Lock' import Undo from '@material-ui/icons/Undo' import { bindActionCreators } from 'redux' @@ -9,8 +8,10 @@ import { connect } from 'react-redux' import { connectionManagerActions } from '../../actions' import { ConnectionOptions } from '../../model/ConnectionOptions' import { Theme, withStyles } from '@material-ui/core/styles' - -import { Button, Grid, IconButton, TextField, List, ListItem, ListItemText, Tooltip } from '@material-ui/core' +import { Button, Grid, TextField, Tooltip } from '@material-ui/core' +import { QosSelect } from '../QosSelect' +import { QoS } from '../../../../backend/src/DataSource/MqttSource' +import Subscriptions from './Subscriptions' interface Props { connection: ConnectionOptions @@ -18,116 +19,93 @@ interface Props { managerActions: typeof connectionManagerActions } -interface State { - subscription: string -} +const ConnectionSettings = memo(function ConnectionSettings(props: Props) { + const [qos, setQos] = useState(0) + const [topic, setTopic] = useState('') + const { classes } = props -class ConnectionSettings extends React.Component { - constructor(props: any) { - super(props) - this.state = { subscription: '' } - } - - private handleChange = (name: string) => (event: any) => { - this.props.managerActions.updateConnection(this.props.connection.id, { - [name]: event.target.value, - }) - } - - private renderSubscriptions() { - const connection = this.props.connection - return connection.subscriptions.map(subscription => ( - this.props.managerActions.deleteSubscription(subscription, connection.id)} - subscription={subscription} - key={subscription} - /> - )) - } - - public render() { - const { classes } = this.props - return ( -
-
- - - ) => - this.setState({ subscription: event.target.value }) - } - /> - - - - - - -
{this.renderSubscriptions()}
-
-
- - - - -
- - - -
-
- - - -
-
-
- ) - } -} - -const Subscription = (props: { subscription: string; deleteAction: any }) => { - return ( - - - - - - {props.subscription} - - + const updateSubscription = useCallback( + (event: React.ChangeEvent) => setTopic(event.target.value), + [] ) -} + + const handleChange = useCallback( + (name: string) => (event: any) => { + props.managerActions.updateConnection(props.connection.id, { + [name]: event.target.value, + }) + }, + [] + ) + + return ( +
+
+ + + + + +
+ +
+
+ + + + + + + + + + +
+ + + +
+
+ + + +
+
+
+ ) +}) const mapDispatchToProps = (dispatch: any) => { return { @@ -142,16 +120,13 @@ const styles = (theme: Theme) => ({ gridPadding: { padding: '0 12px !important', }, - topicList: { - height: '180px', - overflowY: 'scroll' as 'scroll', - margin: '8px 16px', - backgroundColor: theme.palette.background.default, - }, button: { marginTop: theme.spacing(3), float: 'right' as 'right', }, + qos: { + marginTop: theme.spacing(1), + }, }) export default connect(undefined, mapDispatchToProps)(withStyles(styles)(ConnectionSettings)) diff --git a/app/src/components/ConnectionSetup/ConnectionSettings.tsx b/app/src/components/ConnectionSetup/ConnectionSettings.tsx index 8c0506e..c4e67c8 100644 --- a/app/src/components/ConnectionSetup/ConnectionSettings.tsx +++ b/app/src/components/ConnectionSetup/ConnectionSettings.tsx @@ -1,7 +1,7 @@ import ConnectButton from './ConnectButton' -import Delete from '@material-ui/icons/Delete' import React, { useCallback, useState } from 'react' import Save from '@material-ui/icons/Save' +import Delete from '@material-ui/icons/Delete' import Settings from '@material-ui/icons/Settings' import Visibility from '@material-ui/icons/Visibility' import VisibilityOff from '@material-ui/icons/VisibilityOff' diff --git a/app/src/components/ConnectionSetup/Subscriptions.tsx b/app/src/components/ConnectionSetup/Subscriptions.tsx new file mode 100644 index 0000000..e210e28 --- /dev/null +++ b/app/src/components/ConnectionSetup/Subscriptions.tsx @@ -0,0 +1,90 @@ +import React, { useCallback, useState } from 'react' +import Delete from '@material-ui/icons/Delete' +import { connectionManagerActions } from '../../actions' +import { ConnectionOptions } from '../../model/ConnectionOptions' +import { + IconButton, + TableContainer, + Table, + TableHead, + TableRow, + TableCell, + TableBody, + Paper, + Theme, +} from '@material-ui/core' +import { bindActionCreators } from 'redux' +import { withStyles } from '@material-ui/styles' +import { connect } from 'react-redux' + +function Subscriptions(props: { + classes: any + connection: ConnectionOptions + managerActions: typeof connectionManagerActions +}) { + const { classes, connection, managerActions } = props + + return ( + + + + + + Topic + + QoS + + + + + {connection.subscriptions.map(subscription => ( + + + managerActions.deleteSubscription(subscription, connection.id)} + style={{ padding: '6px' }} + > + + + + + + {subscription.topic} + + + {subscription.qos} + + + ))} + +
+
+ ) +} + +const mapDispatchToProps = (dispatch: any) => { + return { + managerActions: bindActionCreators(connectionManagerActions, dispatch), + } +} + +const styles = (theme: Theme) => ({ + tableCell: { + paddingTop: 0, + paddingBottom: 0, + wordBbreak: 'break-word', + }, + tableTitleCell: { + paddingTop: `${theme.spacing(0.5)}px`, + paddingBottom: `${theme.spacing(0.5)}px`, + }, + topicList: { + height: '196px', + overflowY: 'scroll' as 'scroll', + margin: `${theme.spacing(1)}px ${theme.spacing(1)}px 0 ${theme.spacing(1)}px`, + backgroundColor: theme.palette.background.default, + width: 'auto', + }, +}) + +export default connect(undefined, mapDispatchToProps)(withStyles(styles)(Subscriptions)) diff --git a/app/src/components/Sidebar/Publish/QosSelect.tsx b/app/src/components/QosSelect.tsx similarity index 61% rename from app/src/components/Sidebar/Publish/QosSelect.tsx rename to app/src/components/QosSelect.tsx index d0adce3..9653e7a 100644 --- a/app/src/components/Sidebar/Publish/QosSelect.tsx +++ b/app/src/components/QosSelect.tsx @@ -1,18 +1,8 @@ import * as React from 'react' import { TextField, MenuItem, Tooltip } from '@material-ui/core' -import { connect } from 'react-redux' -import { AppState } from '../../../reducers' -import { bindActionCreators } from 'redux' -import { publishActions } from '../../../actions' +import { QoS } from '../../../backend/src/DataSource/MqttSource' -interface Props { - qos: 0 | 1 | 2 - actions: { - publish: typeof publishActions - } -} - -function QosSelect(props: Props) { +export function QosSelect(props: { selected: QoS; onChange: (value: QoS) => void; label?: string }) { const tooltipStyle = { textAlign: 'center' as 'center', width: '100%' } const itemStyle = { padding: '0' } @@ -22,16 +12,16 @@ function QosSelect(props: Props) { if (value !== 0 && value !== 1 && value !== 2) { return } - - props.actions.publish.setQoS(value) + props.onChange(value) }, - [props.actions.publish] + [props.onChange] ) return ( { - return { - actions: { - publish: bindActionCreators(publishActions, dispatch), - }, - } -} - -const mapStateToProps = (state: AppState) => { - return { - qos: state.publish.qos, - } -} - -export default connect(mapStateToProps, mapDispatchToProps)(QosSelect) +export default React.memo(QosSelect) diff --git a/app/src/components/Sidebar/Publish/QosPublishOption.tsx b/app/src/components/Sidebar/Publish/QosPublishOption.tsx new file mode 100644 index 0000000..14097cb --- /dev/null +++ b/app/src/components/Sidebar/Publish/QosPublishOption.tsx @@ -0,0 +1,34 @@ +import * as React from 'react' +import { connect } from 'react-redux' +import { AppState } from '../../../reducers' +import { bindActionCreators } from 'redux' +import { publishActions } from '../../../actions' +import { QosSelect } from '../../QosSelect' +import { QoS } from '../../../../../backend/src/DataSource/MqttSource' + +interface Props { + qos: QoS + actions: { + publish: typeof publishActions + } +} + +function QosPublishOption(props: Props) { + return +} + +const mapDispatchToProps = (dispatch: any) => { + return { + actions: { + publish: bindActionCreators(publishActions, dispatch), + }, + } +} + +const mapStateToProps = (state: AppState) => { + return { + qos: state.publish.qos, + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(QosPublishOption) diff --git a/app/src/components/Sidebar/Publish/RetainSwitch.tsx b/app/src/components/Sidebar/Publish/RetainSwitch.tsx index 6f6a953..fd975f5 100644 --- a/app/src/components/Sidebar/Publish/RetainSwitch.tsx +++ b/app/src/components/Sidebar/Publish/RetainSwitch.tsx @@ -1,4 +1,4 @@ -import QosSelect from './QosSelect' +import QosSelect from './QosPublishOption' import React from 'react' import { Checkbox, FormControlLabel, Tooltip } from '@material-ui/core' import { publishActions } from '../../../actions' diff --git a/app/src/model/ConnectionOptions.ts b/app/src/model/ConnectionOptions.ts index 46beba7..8eb9871 100644 --- a/app/src/model/ConnectionOptions.ts +++ b/app/src/model/ConnectionOptions.ts @@ -1,5 +1,6 @@ import { MqttOptions } from '../../../backend/src/DataSource' import { v4 } from 'uuid' +import { Subscription } from '../../../backend/src/DataSource/MqttSource' const sha1 = require('sha1') export interface CertificateParameters { @@ -7,7 +8,9 @@ export interface CertificateParameters { /** @property data base64 encoded data */ data: string } + export interface ConnectionOptions { + configVersion: 1 type: 'mqtt' id: string host: string @@ -23,7 +26,7 @@ export interface ConnectionOptions { clientCertificate?: CertificateParameters clientKey?: CertificateParameters clientId?: string - subscriptions: Array + subscriptions: Array } export function toMqttConnection(options: ConnectionOptions): MqttOptions | undefined { @@ -52,6 +55,7 @@ function generateClientId() { export function createEmptyConnection(): ConnectionOptions { return { + configVersion: 1, certValidation: true, clientId: generateClientId(), id: v4() as string, @@ -59,7 +63,10 @@ export function createEmptyConnection(): ConnectionOptions { encryption: false, password: undefined, username: undefined, - subscriptions: ['#', '$SYS/#'], + subscriptions: [ + { topic: '#', qos: 0 }, + { topic: '$SYS/#', qos: 0 }, + ], type: 'mqtt', host: '', port: 1883, diff --git a/app/src/reducers/ConnectionManager.ts b/app/src/reducers/ConnectionManager.ts index 5a8f331..a16a746 100644 --- a/app/src/reducers/ConnectionManager.ts +++ b/app/src/reducers/ConnectionManager.ts @@ -1,5 +1,6 @@ import { ConnectionOptions } from '../model/ConnectionOptions' import { createReducer } from './lib' +import { Subscription } from '../../../backend/src/DataSource/MqttSource' export interface ConnectionManagerState { connections: { [s: string]: ConnectionOptions } @@ -50,13 +51,13 @@ export interface SelectConnection { export interface AddSubscription { type: ActionTypes.CONNECTION_MANAGER_ADD_SUBSCRIPTION - subscription: string + subscription: Subscription connectionId: string } export interface DeleteSubscription { type: ActionTypes.CONNECTION_MANAGER_DELETE_SUBSCRIPTION - subscription: string + subscription: Subscription connectionId: string } @@ -159,8 +160,12 @@ function addSubscription(state: ConnectionManagerState, action: AddSubscription) } function deleteSubscription(state: ConnectionManagerState, action: AddSubscription): ConnectionManagerState { + function subscriptionsEqual(v1: Subscription, v2: Subscription): boolean { + return v1.topic == v2.topic && v1.qos == v2.qos + } + const connection = state.connections[action.connectionId] - const newSubscriptions = connection.subscriptions.filter(s => s !== action.subscription) + const newSubscriptions = connection.subscriptions.filter(s => !subscriptionsEqual(s, action.subscription)) return { ...state, diff --git a/app/src/utils/ConfigMigrator.ts b/app/src/utils/ConfigMigrator.ts new file mode 100644 index 0000000..7663ae4 --- /dev/null +++ b/app/src/utils/ConfigMigrator.ts @@ -0,0 +1,34 @@ +export interface Migration { + from: number | undefined + apply: (config: any) => any +} + +interface MigrationSubject { + configVersion: number | undefined +} + +export class ConfigMigrator { + private migrations: Migration[] + + constructor(migrations: Migration[]) { + this.migrations = migrations + } + + public applyMigrations(subject: MigrationSubject): Partial { + while (this.isMigrationNecessary(subject)) { + subject = this.applyableMigrations(subject) + .filter(migration => migration.from === subject.configVersion) + .reduce((currentSubject, migration) => migration.apply(currentSubject as any), subject) + } + + return subject + } + + public isMigrationNecessary(subject: MigrationSubject): boolean { + return this.applyableMigrations(subject).length > 0 + } + + private applyableMigrations(subject: MigrationSubject): Migration[] { + return this.migrations.filter(migration => migration.from === subject.configVersion) + } +} diff --git a/app/src/utils/spec/ConfigMigrator.spec.ts b/app/src/utils/spec/ConfigMigrator.spec.ts new file mode 100644 index 0000000..bce94f2 --- /dev/null +++ b/app/src/utils/spec/ConfigMigrator.spec.ts @@ -0,0 +1,115 @@ +import { expect } from 'chai' +import { ConfigMigrator } from '../ConfigMigrator' +import 'mocha' + +describe('ConfigMigrator', () => { + it('applies migrations to a subject with undefined configVersion', () => { + let migrator = new ConfigMigrator([ + { + from: undefined, + apply: subject => { + return { + ...subject, + configVersion: 1, + drink: 'Beer', + } + }, + }, + ]) + + let config = { + drink: 'water', + } + + let migratedConfig = migrator.applyMigrations(config as any) as any + + expect(migratedConfig.drink).to.eq('Beer') + expect(migratedConfig.configVersion).to.eq(1) + }) + + it('applies multiple migrations from the same version', () => { + let migrator = new ConfigMigrator([ + { + from: undefined, + apply: subject => { + return { + ...subject, + drink: 'Beer', + } + }, + }, + { + from: undefined, + apply: subject => { + return { + ...subject, + configVersion: 1, + } + }, + }, + ]) + + let config = { + drink: 'water', + } + + let migratedConfig = migrator.applyMigrations(config as any) as any + + expect(migratedConfig.drink).to.eq('Beer') + expect(migratedConfig.configVersion).to.eq(1) + }) + + it('applies multiple migrations with ascending versions', () => { + let migrator = new ConfigMigrator([ + { + from: undefined, + apply: subject => { + return { + ...subject, + configVersion: 1, + } + }, + }, + { + from: 1, + apply: subject => { + return { + ...subject, + configVersion: 2, + } + }, + }, + ]) + + let config = { + drink: 'water', + } + + let migratedConfig = migrator.applyMigrations(config as any) + + expect(migratedConfig.configVersion).to.eq(2) + }) + + it('does not apply non-matching migrations', () => { + let migrator = new ConfigMigrator([ + { + from: 2, + apply: subject => { + return { + ...subject, + configVersion: 3, + } + }, + }, + ]) + + let config = { + configVersion: 10, + drink: 'water', + } + + let migratedConfig = migrator.applyMigrations(config as any) + + expect(migratedConfig.configVersion).to.eq(10) + }) +}) diff --git a/app/src/utils/spec/ConnectionsMigration.spec.ts b/app/src/utils/spec/ConnectionsMigration.spec.ts new file mode 100644 index 0000000..800d9f2 --- /dev/null +++ b/app/src/utils/spec/ConnectionsMigration.spec.ts @@ -0,0 +1,71 @@ +import 'mocha' +import { expect } from 'chai' +import { connectionsMigrator } from '../../actions/migrations/Connection' + +describe('ConnectionsMigrator', () => { + it('applies migrations', () => { + let connections: any = { + '763b2e5c-c1ed-4c9b-ac9a-0970b3be29a7': { + certValidation: true, + clientId: 'mqtt-explorer-2783f48c', + encryption: false, + host: 'nodered', + id: '763b2e5c-c1ed-4c9b-ac9a-0970b3be29a7', + name: 'nodered', + port: 1883, + protocol: 'mqtt', + subscriptions: ['#', '$SYS/#'], + type: 'mqtt', + }, + 'iot.eclipse.org': { + certValidation: true, + clientId: 'mqtt-explorer-d913aad3', + encryption: false, + host: 'iot.eclipse.org', + id: 'iot.eclipse.org', + name: 'iot.eclipse.org', + port: 1883, + protocol: 'mqtt', + subscriptions: ['#', '$SYS/#'], + type: 'mqtt', + }, + } + + let migratedConnections: any = { + '763b2e5c-c1ed-4c9b-ac9a-0970b3be29a7': { + configVersion: 1, + certValidation: true, + clientId: undefined, + encryption: false, + host: 'nodered', + id: '763b2e5c-c1ed-4c9b-ac9a-0970b3be29a7', + name: 'nodered', + port: 1883, + protocol: 'mqtt', + subscriptions: [ + { topic: '#', qos: 0 }, + { topic: '$SYS/#', qos: 0 }, + ], + type: 'mqtt', + }, + 'mqtt.eclipse.org': { + configVersion: 1, + certValidation: true, + clientId: undefined, + encryption: false, + host: 'mqtt.eclipse.org', + id: 'mqtt.eclipse.org', + name: 'mqtt.eclipse.org', + port: 1883, + protocol: 'mqtt', + subscriptions: [ + { topic: '#', qos: 0 }, + { topic: '$SYS/#', qos: 0 }, + ], + type: 'mqtt', + }, + } + + expect(connectionsMigrator.applyMigrations(connections)).to.deep.eq(migratedConnections) + }) +}) diff --git a/app/test/mocha.opts b/app/test/mocha.opts new file mode 100644 index 0000000..a1b1c79 --- /dev/null +++ b/app/test/mocha.opts @@ -0,0 +1,3 @@ +--require ts-node/register +--require source-map-support/register +--recursive ./src/**/*.spec.ts diff --git a/app/test/tsconfig.json b/app/test/tsconfig.json new file mode 100644 index 0000000..09479fd --- /dev/null +++ b/app/test/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "../tsconfig", + "compilerOptions": { + "module": "commonjs" + } +} diff --git a/app/yarn.lock b/app/yarn.lock index d17ba63..d29195f 100644 --- a/app/yarn.lock +++ b/app/yarn.lock @@ -736,6 +736,11 @@ ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.0: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ansi-colors@3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" + integrity sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw== + ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" @@ -781,6 +786,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -791,6 +804,13 @@ archy@1.0.0: resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + arr-diff@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" @@ -860,6 +880,11 @@ assert@^1.1.1: object-assign "^4.1.1" util "0.10.3" +assertion-error@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" + integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== + assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -1003,6 +1028,11 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -1089,11 +1119,23 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -1263,6 +1305,18 @@ capture-stack-trace@^1.0.0: resolved "https://registry.yarnpkg.com/capture-stack-trace/-/capture-stack-trace-1.0.1.tgz#a6c0bbe1f38f3aa0b92238ecb6ff42c344d4135d" integrity sha512-mYQLZnx5Qt1JgB1WEiMCf2647plpGeQ2NMR/5L0HNZzGQo4fuSPnK+wjfPnKZV0aiJDgzmWqqkV/g7JD+DW0qw== +chai@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" + integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + dependencies: + assertion-error "^1.1.0" + check-error "^1.0.2" + deep-eql "^3.0.1" + get-func-name "^2.0.0" + pathval "^1.1.0" + type-detect "^4.0.5" + chalk@2.4.2, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" @@ -1277,11 +1331,31 @@ chalk@2.4.2, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= +check-error@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" + integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= + check-types@^8.0.3: version "8.0.3" resolved "https://registry.yarnpkg.com/check-types/-/check-types-8.0.3.tgz#3356cca19c889544f2d7a95ed49ce508a0ecf552" integrity sha512-YpeKZngUmG65rLudJ4taU7VLkOCTMhNl/u4ctNC56LQS/zJTyNH0Lrtwm1tfTsbLlwvlfsA2d1c8vCf/Kh2KwQ== +chokidar@3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.0.tgz#12c0714668c55800f659e262d4962a97faf554a6" + integrity sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.2.0" + optionalDependencies: + fsevents "~2.1.1" + chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" @@ -1975,6 +2049,13 @@ debug@2.2.0: dependencies: ms "0.7.1" +debug@3.2.6, debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: + version "3.2.6" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" + integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== + dependencies: + ms "^2.1.1" + debug@=3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -1982,13 +2063,6 @@ debug@=3.1.0, debug@~3.1.0: dependencies: ms "2.0.0" -debug@^3.0.0, debug@^3.1.1, debug@^3.2.5: - version "3.2.6" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" - integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== - dependencies: - ms "^2.1.1" - debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" @@ -2011,6 +2085,13 @@ decode-uri-component@^0.2.0: resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= +deep-eql@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" + integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== + dependencies: + type-detect "^4.0.0" + deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -2124,6 +2205,11 @@ diff-match-patch@^1.0.4: resolved "https://registry.yarnpkg.com/diff-match-patch/-/diff-match-patch-1.0.4.tgz#6ac4b55237463761c4daf0dc603eb869124744b1" integrity sha512-Uv3SW8bmH9nAtHKaKSanOQmj2DnlH65fUpcrMdfdaOxUG02QQ4YGZ8AE7kKOMisF7UqvOlGKVYWRvezdncW9lg== +diff@3.5.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== + diff@^4.0.1: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" @@ -2463,7 +2549,7 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-string-regexp@^1.0.5: +escape-string-regexp@1.0.5, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= @@ -2476,6 +2562,11 @@ eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esrecurse@^4.1.0: version "4.2.1" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" @@ -2685,6 +2776,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -2707,7 +2805,7 @@ find-cache-dir@^2.0.0, find-cache-dir@^2.1.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-up@^3.0.0: +find-up@3.0.0, find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== @@ -2724,6 +2822,13 @@ findup-sync@3.0.0: micromatch "^3.0.4" resolve-dir "^1.0.1" +flat@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/flat/-/flat-4.1.0.tgz#090bec8b05e39cba309747f1d588f04dbaf98db2" + integrity sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw== + dependencies: + is-buffer "~2.0.3" + flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" @@ -2820,6 +2925,11 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" +fsevents@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + fstream@^1.0.2: version "1.0.12" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" @@ -2845,6 +2955,11 @@ get-caller-file@^2.0.1: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-func-name@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" + integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -2872,6 +2987,25 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@~5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + +glob@7.1.3: + version "7.1.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.3.tgz#3960832d3f1574108342dafd3a67b332c0969df1" + integrity sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^5.0.3: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -2988,6 +3122,11 @@ grapheme-splitter@^1.0.4: resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== +growl@1.10.5: + version "1.10.5" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" + integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== + gunzip-maybe@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/gunzip-maybe/-/gunzip-maybe-1.2.1.tgz#9aa3dce53c272516c40b2a610a25529bf64ee1bc" @@ -3106,7 +3245,7 @@ hash.js@^1.0.0, hash.js@^1.0.3: inherits "^2.0.3" minimalistic-assert "^1.0.1" -he@^1.2.0: +he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -3464,12 +3603,19 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.2: +is-buffer@^2.0.2, is-buffer@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== @@ -3552,7 +3698,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -3571,6 +3717,11 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" @@ -3683,6 +3834,14 @@ js-base64@^2.5.1: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@3.13.1: + version "3.13.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" + integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -3909,6 +4068,13 @@ lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17. resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +log-symbols@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4" + integrity sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ== + dependencies: + chalk "^2.4.2" + log-symbols@^2.1.0: version "2.2.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" @@ -4109,7 +4275,7 @@ minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= -"minimatch@2 || 3", minimatch@^3.0.4: +"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== @@ -4158,6 +4324,13 @@ mkdirp-then@1.2.0: any-promise "^1.1.0" mkdirp "^0.5.0" +mkdirp@0.5.3: + version "0.5.3" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c" + integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== + dependencies: + minimist "^1.2.5" + "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" @@ -4165,6 +4338,36 @@ mkdirp-then@1.2.0: dependencies: minimist "^1.2.5" +mocha@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-7.1.1.tgz#89fbb30d09429845b1bb893a830bf5771049a441" + integrity sha512-3qQsu3ijNS3GkWcccT5Zw0hf/rWvu1fTN9sPvEd81hlwsr30GX2GcDSSoBxo24IR8FelmrAydGC6/1J5QQP4WA== + dependencies: + ansi-colors "3.2.3" + browser-stdout "1.3.1" + chokidar "3.3.0" + debug "3.2.6" + diff "3.5.0" + escape-string-regexp "1.0.5" + find-up "3.0.0" + glob "7.1.3" + growl "1.10.5" + he "1.2.0" + js-yaml "3.13.1" + log-symbols "3.0.0" + minimatch "3.0.4" + mkdirp "0.5.3" + ms "2.1.1" + node-environment-flags "1.0.6" + object.assign "4.1.0" + strip-json-comments "2.0.1" + supports-color "6.0.0" + which "1.3.1" + wide-align "1.1.3" + yargs "13.3.2" + yargs-parser "13.1.2" + yargs-unparser "1.6.0" + moment@>=2.13.0, moment@^2.24.0: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" @@ -4284,6 +4487,14 @@ no-case@^3.0.3: lower-case "^2.0.1" tslib "^1.10.0" +node-environment-flags@1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" + integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== + dependencies: + object.getownpropertydescriptors "^2.0.3" + semver "^5.7.0" + node-forge@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" @@ -4355,7 +4566,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -4454,7 +4665,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: +object.assign@4.1.0, object.assign@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== @@ -4718,6 +4929,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= +pathval@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" + integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + pbkdf2@^3.0.3: version "3.0.17" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.17.tgz#976c206530617b14ebb32114239f7b09336e93a6" @@ -4748,6 +4964,11 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picomatch@^2.0.4: + version "2.2.2" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" + integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== + pify@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -5234,6 +5455,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" + integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== + dependencies: + picomatch "^2.0.4" + redux-batched-actions@0.5: version "0.5.0" resolved "https://registry.yarnpkg.com/redux-batched-actions/-/redux-batched-actions-0.5.0.tgz#d3f0e359b2a95c7d80bab442df450bfafd57d122" @@ -5467,7 +5695,7 @@ selfsigned@^1.10.7: dependencies: node-forge "0.9.0" -"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@4 || 5", semver@^5.1.0, semver@^5.5.0, semver@^5.6.0: +"semver@2 || 3 || 4 || 5", "semver@2.x || 3.x || 4 || 5", "semver@4 || 5", semver@^5.1.0, semver@^5.5.0, semver@^5.6.0, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -5786,6 +6014,11 @@ split-string@^3.0.1, split-string@^3.0.2: dependencies: extend-shallow "^3.0.0" +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + ssri@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" @@ -5847,7 +6080,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -5943,6 +6176,11 @@ strip-eof@^1.0.0: resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= +strip-json-comments@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= + style-loader@^1: version "1.1.4" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.1.4.tgz#1ad81283cefe51096756fd62697258edad933230" @@ -5951,6 +6189,13 @@ style-loader@^1: loader-utils "^2.0.0" schema-utils "^2.6.5" +supports-color@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.0.0.tgz#76cfe742cf1f41bb9b1c29ad03068c05b4c0e40a" + integrity sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg== + dependencies: + has-flag "^3.0.0" + supports-color@6.1.0, supports-color@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" @@ -6090,6 +6335,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -6120,6 +6372,11 @@ tty-browserify@0.0.0: resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= +type-detect@^4.0.0, type-detect@^4.0.5: + version "4.0.8" + resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" + integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== + type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -6493,13 +6750,20 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@^1.2.14, which@^1.2.9, which@^1.3.1: +which@1.3.1, which@^1.2.14, which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" +wide-align@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + integrity sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA== + dependencies: + string-width "^1.0.2 || 2" + worker-farm@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" @@ -6591,6 +6855,14 @@ yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yargs-parser@13.1.2, yargs-parser@^13.1.0, yargs-parser@^13.1.2: + version "13.1.2" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" + integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^11.1.1: version "11.1.1" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.1.tgz#879a0865973bca9f6bab5cbdf3b1c67ec7d3bcf4" @@ -6599,13 +6871,14 @@ yargs-parser@^11.1.1: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^13.1.0: - version "13.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== +yargs-unparser@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-1.6.0.tgz#ef25c2c769ff6bd09e4b0f9d7c605fb27846ea9f" + integrity sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw== dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" + flat "^4.1.0" + lodash "^4.17.15" + yargs "^13.3.0" yargs@12.0.5: version "12.0.5" @@ -6642,6 +6915,22 @@ yargs@13.2.4: y18n "^4.0.0" yargs-parser "^13.1.0" +yargs@13.3.2, yargs@^13.3.0: + version "13.3.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" + integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + dependencies: + cliui "^5.0.0" + find-up "^3.0.0" + get-caller-file "^2.0.1" + require-directory "^2.1.1" + require-main-filename "^2.0.0" + set-blocking "^2.0.0" + string-width "^3.0.0" + which-module "^2.0.0" + y18n "^4.0.0" + yargs-parser "^13.1.2" + yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" diff --git a/appveyor.yml b/appveyor.yml index 2da5ba5..f84d609 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -21,4 +21,3 @@ test_script: artifacts: - path: 'build\clean\build\*.appx' - diff --git a/backend/src/DataSource/MqttSource.ts b/backend/src/DataSource/MqttSource.ts index a7a5ea7..57ac218 100644 --- a/backend/src/DataSource/MqttSource.ts +++ b/backend/src/DataSource/MqttSource.ts @@ -12,12 +12,19 @@ export interface MqttOptions { tls: boolean certValidation: boolean clientId?: string - subscriptions: Array + subscriptions: Array certificateAuthority?: string clientCertificate?: string clientKey?: string } +export interface Subscription { + topic: string + qos: QoS +} + +export type QoS = 0 | 1 | 2 + export class MqttSource implements DataSource { public stateMachine: DataSourceStateMachine = new DataSourceStateMachine() private client: Client | undefined @@ -74,7 +81,7 @@ export class MqttSource implements DataSource { client.on('connect', () => { this.stateMachine.setConnected(true) options.subscriptions.forEach(subscription => { - client.subscribe(subscription, (err: Error) => { + client.subscribe(subscription.topic, { qos: subscription.qos }, (err: Error) => { if (err) { this.stateMachine.setError(err) }