Skip to content

Commit

Permalink
Add support for hiding and unhiding exchanges.
Browse files Browse the repository at this point in the history
  • Loading branch information
cr0wst committed Mar 30, 2024
1 parent 618fb18 commit fed6bd2
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 15 deletions.
2 changes: 2 additions & 0 deletions src/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export type Connection = {
}

export type Exchange = {
connectionId: string
name: string
hidden: boolean
}

export type Queue = {
Expand Down
37 changes: 31 additions & 6 deletions src/main/RabbitConnection.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Connection, Queue } from '@common/types'
import { Connection, Exchange, Queue } from '@common/types'
import { AMQPChannel, AMQPClient } from '@cloudamqp/amqp-client'
import { ulid } from 'ulid'
import axios from 'axios'
Expand All @@ -9,6 +9,7 @@ export class RabbitConnection {
private client: AMQPClient
private channel: AMQPChannel | null = null
private queues: Queue[] = []
private exchanges: Exchange[] = []

constructor(private connection: Connection) {
this.client = new AMQPClient(
Expand Down Expand Up @@ -44,17 +45,22 @@ export class RabbitConnection {
}
})

const exchanges = response.data
// this.exchanges might already have exchanges, so we need to merge some of them.
// But, we want to re-create the list of exchanges from the response.
this.exchanges = response.data
.filter((exchange: any) => {
return exchange.internal === false && exchange.name !== ''
})
.map((exchange: any) => {
return {
name: exchange.name
name: exchange.name,
connectionId: this.connection.id,
// Look up the hidden state from the existing exchanges
hidden: this.exchanges.find((e) => e.name === exchange.name)?.hidden || false
}
})

return exchanges
return this.exchanges
}

public async createQueue({ name, exchange, bindOptions }) {
Expand Down Expand Up @@ -112,17 +118,36 @@ export class RabbitConnection {
this.queues = this.queues.filter((q) => q.id !== queue.id)
}

public async loadState(state: { queues: Queue[] }) {
public async loadState(state: { queues: Queue[], exchanges: Exchange[] }) {
for (const queue of state.queues) {
await this.createQueue(queue)
}

this.exchanges = state.exchanges || []

return this.getState()
}

public hideExchange(exchange: Exchange) {
const existingExchange = this.exchanges.find((e) => e.name === exchange.name)
if (!existingExchange) {
throw new Error('Exchange not found')
}
existingExchange.hidden = true
}

public unhideExchange(exchange: Exchange) {
const existingExchange = this.exchanges.find((e) => e.name === exchange.name)
if (!existingExchange) {
throw new Error('Exchange not found')
}
existingExchange.hidden = false
}

public getState() {
return {
queues: this.queues
queues: this.queues,
exchanges: this.exchanges
}
}
}
16 changes: 16 additions & 0 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,19 @@ ipcMain.handle('rabbit-delete-queue', async (_, queue) => {

return await rabbitConnection.deleteQueue(queue)
})

ipcMain.handle('rabbit-exchange-hide', async (_, exchange) => {
if (!rabbitConnection) {
throw new Error('Not connected to RabbitMQ')
}

return rabbitConnection.hideExchange(exchange)
})

ipcMain.handle('rabbit-exchange-unhide', async (_, exchange) => {
if (!rabbitConnection) {
throw new Error('Not connected to RabbitMQ')
}

return rabbitConnection.unhideExchange(exchange)
})
3 changes: 3 additions & 0 deletions src/preload/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ declare global {
connect(options: RabbitOptions): Promise<{ queues: Queue[] }>
disconnect(): Promise<void>
listExchanges(): Promise<Exchange[]>
hideExchange(exchange: Exchange): Promise<void>
unHideExchange(exchange: Exchange): Promise<void>
addQueue(name: string, exchange: string, bindOptions: any): Promise<Queue>
deleteQueue(queue: Queue): Promise<void>
onMessage(callback: (message: Message) => void): void
removeMessageListener(): void
}
}
}
Expand Down
11 changes: 9 additions & 2 deletions src/preload/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { contextBridge } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
import { Connection, Queue, RabbitOptions } from '../common/types'
import { Connection, Exchange, Queue, RabbitOptions } from '../common/types'

// Custom APIs for renderer
const api = {
Expand All @@ -25,6 +25,12 @@ const api = {
async listExchanges() {
return await electronAPI.ipcRenderer.invoke('rabbit-list-exchanges')
},
async hideExchange(exchange: Exchange) {
return await electronAPI.ipcRenderer.invoke('rabbit-exchange-hide', exchange)
},
async unHideExchange(exchange: Exchange) {
return await electronAPI.ipcRenderer.invoke('rabbit-exchange-unhide', exchange)
},
async addQueue(name: string, exchange: string, bindOptions: any) {
return await electronAPI.ipcRenderer.invoke('rabbit-add-queue', { name, exchange, bindOptions })
},
Expand All @@ -34,7 +40,8 @@ const api = {
onMessage: (callback) =>
electronAPI.ipcRenderer.on('rabbit-message-received', (_, args) => {
callback(args)
})
}),
removeMessageListener: () => electronAPI.ipcRenderer.removeAllListeners('rabbit-message-received')
}
}

Expand Down
23 changes: 20 additions & 3 deletions src/renderer/src/components/ExchangeList.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,31 @@
<script>
<script lang="ts">
import { exchanges, selectedExchange } from '../stores/exchange'
import type { Exchange } from '@common/types'
import { EyeSlash, Icon } from 'svelte-hero-icons'
const api = window.api
function hideExchange(exchange: Exchange) {
// Set the store state for immediate result
exchange.hidden = true
// Update the rabbit state so it persists
api.rabbit.hideExchange(exchange)
// Update the exchange store to trigger reactivity
$exchanges = $exchanges
}
</script>

{#each $exchanges as exchange}
{#each $exchanges.filter((e) => !e.hidden) as exchange}
<button
class="bg-primary-700 p-2 border-b text-left border-b-primary-800 text-primary-200 hover:cursor-pointer hover:bg-primary-300 hover:text-primary-50 text-xs font-light transition-all"
class="group bg-primary-700 p-2 border-b text-left border-b-primary-800 text-primary-200 hover:cursor-pointer hover:bg-primary-300 hover:text-primary-50 text-xs font-light transition-all flex justify-between"
on:click={() => ($selectedExchange = exchange)}
class:selected={$selectedExchange === exchange}
>
{exchange.name}
<button on:click={() => hideExchange(exchange)}>
<Icon src="{EyeSlash}" class="w-4 h-4 invisible group-hover:visible" />
</button>
</button>
{/each}

Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/routes/Connections.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@
// await api.rabbit.disconnect(connection)
$selectedConnection = null
}
$selectedExchange = $exchanges[0] || null
$selectedExchange = $exchanges.find((e) => !e.hidden) || null
} catch (e) {
error = `Failed to open connection '${connection.name}'. Check your details and try again.`
}
Expand Down
33 changes: 30 additions & 3 deletions src/renderer/src/routes/QueueView.svelte
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<script lang="ts">
import { selectedExchange } from '../stores/exchange'
import { exchanges, selectedExchange } from '../stores/exchange'
import { messages, selectedMessage } from '../stores/message'
import ExchangeList from '../components/ExchangeList.svelte'
import moment from 'moment'
import JsonBlock from '../components/JsonBlock.svelte'
import { queues, selectedQueue } from '../stores/queues'
import { onMount } from 'svelte'
import { onDestroy, onMount } from 'svelte'
import AddQueueButton from '../components/AddQueueButton.svelte'
import { Trash, Icon } from 'svelte-hero-icons'
const api = window.api
$: queueMessages = $messages
Expand Down Expand Up @@ -48,13 +49,37 @@
})
})
})
onDestroy(() => {
api.rabbit.removeMessageListener()
})
function unhideExchanges() {
exchanges.update((value) => {
return value.map((e) => {
if (e.hidden) {
api.rabbit.unHideExchange(e)
}
e.hidden = false
return e
})
})
}
$: hiddenExchangeCount = $exchanges.filter((e) => e.hidden).length
</script>

<div class="flex h-full w-full">
<!-- exchange list -->
<div class="bg-primary-900 w-1/5 flex flex-col">
<h2 class="text-primary-50 p-2 font-medium text-lg">Exchanges</h2>
<ExchangeList />
{#if hiddenExchangeCount > 0}
<div class="text-primary-200 text-xs font-extralight text-center p-2 italic">{hiddenExchangeCount} Exchanges
Hidden.
<button on:click={unhideExchanges} class="text-primary-50 hover:font-bold">Unhide All</button>
</div>
{/if}
</div>

<!-- message section -->
Expand All @@ -79,7 +104,9 @@
</div>
</button>
{/each}
<AddQueueButton/>
{#if $selectedExchange}
<AddQueueButton />
{/if}
</div>

<!-- message list -->
Expand Down
1 change: 1 addition & 0 deletions src/renderer/src/stores/exchange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ import { writable } from 'svelte/store'
export const exchanges = writable<Exchange[]>([])

export const selectedExchange = writable<Exchange | null>(null)

0 comments on commit fed6bd2

Please sign in to comment.