Add quality of service option to subscriptions

Fixes #323, #14, #334
Fixes #132
This commit is contained in:
Thomas Nordquist
2020-04-20 12:18:24 +02:00
parent e87a7115c5
commit b72fc48bdb
19 changed files with 904 additions and 215 deletions

View File

@@ -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<MigrationSubject> {
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)
}
}

View File

@@ -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)
})
})

View File

@@ -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)
})
})