From d03d1fbfcc3ffc6940f2d252b4113a3394ecdf16 Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Sun, 21 Aug 2022 23:05:51 -0400 Subject: [PATCH] Slightly more organized. Centralize API fetches (don't need to always specify backend URL) Add mock data to api fetches. Add dropdown for SASL config. Try to keep data view from rapidly resizing. Toying with error display in top-right corner (tentative) Rename package. Slightly better WebSocket error handling. --- package-lock.json | 10 +++----- package.json | 4 +-- src/app.css | 3 ++- src/lib/constants.js | 4 ++- src/lib/header/Header.svelte | 13 +++++++++- src/lib/state.js | 40 ++++++++++++++++++----------- src/routes/+page.svelte | 23 +++++++---------- src/routes/settings/+page.svelte | 19 ++++++++++---- src/utils.js | 44 ++++++++++++++++++++++++++++++++ 9 files changed, 115 insertions(+), 45 deletions(-) create mode 100644 src/utils.js diff --git a/package-lock.json b/package-lock.json index 74314ac..49016f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.0.1", "dependencies": { "@fontsource/fira-mono": "^4.5.0", + "@zerodevx/svelte-json-view": "^0.2.1", "cookie": "^0.4.1", "highlight.js": "^11.6.0", "svelte-highlight": "^6.2.1" @@ -17,8 +18,7 @@ "@sveltejs/adapter-auto": "next", "@sveltejs/kit": "next", "@types/cookie": "^0.5.1", - "@zerodevx/svelte-json-view": "^0.2.1", - "eslint": "^8.16.0", + "eslint": "^8.22.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^4.0.0", "prettier": "^2.6.2", @@ -395,8 +395,7 @@ "node_modules/@zerodevx/svelte-json-view": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@zerodevx/svelte-json-view/-/svelte-json-view-0.2.1.tgz", - "integrity": "sha512-yaLojLYTi08vccUKRg/XSRCCPoyzCZqrG+W8mVhJEGiOfFKAmWqNH6b+/il1gG3V1UaEe7amj2mzmo1mo4q1iA==", - "dev": true + "integrity": "sha512-yaLojLYTi08vccUKRg/XSRCCPoyzCZqrG+W8mVhJEGiOfFKAmWqNH6b+/il1gG3V1UaEe7amj2mzmo1mo4q1iA==" }, "node_modules/abbrev": { "version": "1.1.1", @@ -3515,8 +3514,7 @@ "@zerodevx/svelte-json-view": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/@zerodevx/svelte-json-view/-/svelte-json-view-0.2.1.tgz", - "integrity": "sha512-yaLojLYTi08vccUKRg/XSRCCPoyzCZqrG+W8mVhJEGiOfFKAmWqNH6b+/il1gG3V1UaEe7amj2mzmo1mo4q1iA==", - "dev": true + "integrity": "sha512-yaLojLYTi08vccUKRg/XSRCCPoyzCZqrG+W8mVhJEGiOfFKAmWqNH6b+/il1gG3V1UaEe7amj2mzmo1mo4q1iA==" }, "abbrev": { "version": "1.1.1", diff --git a/package.json b/package.json index f06807c..a49c17e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "my-app", + "name": "kafka-dance-frontend", "version": "0.0.1", "scripts": { "dev": "vite dev", @@ -15,7 +15,7 @@ "@sveltejs/adapter-auto": "next", "@sveltejs/kit": "next", "@types/cookie": "^0.5.1", - "eslint": "^8.16.0", + "eslint": "^8.22.0", "eslint-config-prettier": "^8.3.0", "eslint-plugin-svelte3": "^4.0.0", "prettier": "^2.6.2", diff --git a/src/app.css b/src/app.css index e0c17f0..a4cb029 100644 --- a/src/app.css +++ b/src/app.css @@ -144,7 +144,8 @@ button { padding: 1em; background-color: #e2eaff; height: 86vh; - max-width: 70vw; + max-width: 65vw; + min-width: 65vw; overflow-y: scroll; } diff --git a/src/lib/constants.js b/src/lib/constants.js index 55854c7..cd1f009 100644 --- a/src/lib/constants.js +++ b/src/lib/constants.js @@ -5,4 +5,6 @@ export const queryMode = { export const backendAddressAndPort = 'localhost:3000' -export const backendUrl = `http://${backendAddressAndPort}` \ No newline at end of file +export const backendUrl = `http://${backendAddressAndPort}` + +export const saslAuthMethods = ['PLAIN', 'SCRAM-SHA-256', 'SCRAM-SHA-512', 'AWS'] \ No newline at end of file diff --git a/src/lib/header/Header.svelte b/src/lib/header/Header.svelte index aced15c..ee57d05 100644 --- a/src/lib/header/Header.svelte +++ b/src/lib/header/Header.svelte @@ -1,6 +1,7 @@
@@ -13,7 +14,16 @@
- + {#if testMode} +
TEST MODE ENABLED
+ {/if} +
@@ -28,6 +38,7 @@ font-size: 18px; color: black; height: 3em; + padding: 1em; } .corner a { diff --git a/src/lib/state.js b/src/lib/state.js index 676a4ae..6c01296 100644 --- a/src/lib/state.js +++ b/src/lib/state.js @@ -17,15 +17,15 @@ const mockItems = [ { "eventType": "Movie", "broughtToYouBy": "20th Century Fox", - "title": "Star Wars 2" + "title": "Star Wars 2: The Star Warsening, brought to you by the Big Stink Corporation, a division of PepsiCo and somehow also Disney." }, ].map(o => ({ value: o, timestamp: new Date().getTime() })) -const testMode = false +export const testMode = true export const state = writable({ items: [], itemCount: undefined, - error: null, + error: 'Connecting to WebSocket...', }) const updateClearError = updater => state.update(s => { s.error = null @@ -38,6 +38,7 @@ let disconnected = false const getRandomFromArray = array => array[Math.floor(Math.random() * array.length)] +const testTimeout = 200 const testQuery = (mode, jsFilter, queryCode) => { try { const f = new Function('message', 'value', queryCode) @@ -46,16 +47,17 @@ const testQuery = (mode, jsFilter, queryCode) => { const item = getRandomFromArray(mockItems) if (!jsFilter || f(item, item.value)) { item.timestamp = new Date().getTime() - updateClearError(s => ({ ...s, items: [item, ...s.items].slice(0, itemLimit), itemCount: 0 })) + state.update(s => ({ ...s, items: [item, ...s.items].slice(0, itemLimit), itemCount: 0 })) } - setTimeout(addItem, 2000) + setTimeout(addItem, testTimeout) } - setTimeout(addItem, 2000) + setTimeout(addItem, testTimeout) } else { - updateClearError(s => ({ ...s, items: mockItems.filter(item => !jsFilter || f(item, item.value)), itemCount: 0 })) + state.update(s => ({ ...s, items: mockItems.filter(item => !jsFilter || f(item, item.value)), itemCount: 0 })) } } catch (e) { - updateClearError(s => ({ ...s, error: e.toString() })) + console.log('Caught an error:', e.toString()) + state.update(s => ({ ...s, error: e.toString() })) } } @@ -83,16 +85,16 @@ export const query = async ({ cluster, topic, mode, jsFilter, queryCode, maxItem } export const connect = () => { - ws = new WebSocket(`ws://${backendAddressAndPort}`) + try { + ws = new WebSocket(`ws://${backendAddressAndPort}`) + } catch (e) { + console.error(e.toString()) + } if (!ws) { updateClearError(s => ({ ...s, error: 'Unable to connect to websocket.' })) return } - ws.addEventListener('close', () => { - disconnected = true - }) - ws.addEventListener('open', () => { console.log('WebSocket opened') }) @@ -101,7 +103,6 @@ export const connect = () => { let data; try { data = JSON.parse(message.data) - console.log('WebSocket message received', data) switch (data?.type.toLowerCase()) { case 'complete': updateClearError(s => ({ @@ -128,6 +129,15 @@ export const connect = () => { }) ws.addEventListener('close', () => { - console.log('WebSocket closed') + disconnected = true + state.update(s => ({ ...s, error: 'WebSocket connection closed.' })) }) + + ws.addEventListener('error', () => { + state.update(s => ({ ...s, error: 'WebSocket connection error.' })) + }) + + if (ws.readyState === ws.CLOSED || ws.readyState === ws.CLOSING) { + state.update(s => ({ ...s, error: 'WebSocket connection closed.' })) + } } diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index bbc1a94..e901c83 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -2,7 +2,8 @@ import { onMount } from 'svelte' import { JsonView } from '@zerodevx/svelte-json-view' import { state, connect, query } from '$lib/state'; - import { queryMode, backendUrl } from "../lib/constants.js"; + import { queryMode } from "../lib/constants.js"; + import { apiFetch } from "../utils.js"; let expandAll = true let showMetadata = true @@ -24,28 +25,22 @@ return message.value.eventType === "Television";` let topics = [] let clusters = [] const updateTopics = async () => { - topics = [] - try { - const response = await fetch(`${backendUrl}/topics/${querySettings.cluster}`) - topics = await response.json() - topics.sort() - console.log('topics', topics) - querySettings.topic = topics[0] - } catch (e) { - console.log('fetch error:', e.toString()) - } + topics = await apiFetch(`/topics/${querySettings.cluster}`) + topics ??= [] + topics.sort() + console.log('topics', topics) + querySettings.topic = topics[0] } onMount(async () => { connect(); - const response = await fetch(`${backendUrl}/clusters`) - clusters = Object.keys(await response.json()) + clusters = Object.keys((await apiFetch('/clusters')) || {}) querySettings.cluster = clusters[0] await updateTopics() }) - {querySettings.topic} - Kafka Dance + {querySettings.topic ? `${querySettings.topic} - ` : ''}Kafka Dance diff --git a/src/routes/settings/+page.svelte b/src/routes/settings/+page.svelte index 3774399..4282d21 100644 --- a/src/routes/settings/+page.svelte +++ b/src/routes/settings/+page.svelte @@ -1,6 +1,7 @@