From 3f0a535bc44a5880b9221d1428ab6e0d608c140c Mon Sep 17 00:00:00 2001 From: Rostyslav Zatserkovnyi Date: Tue, 1 Oct 2024 17:30:07 +0300 Subject: [PATCH 1/6] Add persistence to API keys --- src/components/dialogs/ImportAPIDialog.svelte | 16 ++++++++++++++- src/components/dialogs/apiKeySelections.ts | 20 +++++++++++++++++++ src/components/dialogs/dataSources/CDC.svelte | 11 ++++++++-- .../dialogs/dataSources/FluView.svelte | 5 +++-- src/components/dialogs/dataSources/GHT.svelte | 11 ++++++++-- .../dialogs/dataSources/Quidel.svelte | 11 ++++++++-- .../dialogs/dataSources/Sensors.svelte | 11 ++++++++-- .../dialogs/dataSources/Twitter.svelte | 11 ++++++++-- src/store.ts | 13 ++++++++++++ 9 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 src/components/dialogs/apiKeySelections.ts diff --git a/src/components/dialogs/ImportAPIDialog.svelte b/src/components/dialogs/ImportAPIDialog.svelte index 2d48895..4ba46b7 100644 --- a/src/components/dialogs/ImportAPIDialog.svelte +++ b/src/components/dialogs/ImportAPIDialog.svelte @@ -19,7 +19,7 @@ import NowCast from './dataSources/Nowcast.svelte'; import CovidHosp from './dataSources/COVIDHosp.svelte'; import CoviDcast from './dataSources/COVIDcast.svelte'; - import { navMode } from '../../store'; + import { navMode, storeApiKeys } from '../../store'; import { NavMode } from '../chartUtils'; const dispatch = createEventDispatcher(); @@ -68,6 +68,14 @@
+
+
+ +
+
Data Source
+ + diff --git a/src/components/dialogs/apiKeySelections.ts b/src/components/dialogs/apiKeySelections.ts new file mode 100644 index 0000000..a3df7bc --- /dev/null +++ b/src/components/dialogs/apiKeySelections.ts @@ -0,0 +1,20 @@ +export default class ApiKeySelections { + cdc = ''; + fluView = ''; + ght = ''; + quidel = ''; + sensors = ''; + twitter = ''; +} + +export function getApiKeySelections() { + try { + if (localStorage.getItem('api')) { + return JSON.parse(localStorage.getItem('api')!) as ApiKeySelections; + } + return new ApiKeySelections(); + } catch { + localStorage.removeItem('api'); + return new ApiKeySelections(); + } +} diff --git a/src/components/dialogs/dataSources/CDC.svelte b/src/components/dialogs/dataSources/CDC.svelte index 3740151..5b78ede 100644 --- a/src/components/dialogs/dataSources/CDC.svelte +++ b/src/components/dialogs/dataSources/CDC.svelte @@ -3,16 +3,23 @@ import { cdcLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; + import { apiKeySelections } from '../../../store'; export let id: string; let locations = regions[0].value; - let auth = ''; + let auth = $apiKeySelections.cdc; export function importDataSet() { return importCDC({ locations, auth }); } - + diff --git a/src/components/dialogs/dataSources/FluView.svelte b/src/components/dialogs/dataSources/FluView.svelte index 00ce0ec..c74d97e 100644 --- a/src/components/dialogs/dataSources/FluView.svelte +++ b/src/components/dialogs/dataSources/FluView.svelte @@ -1,6 +1,7 @@ - + diff --git a/src/components/dialogs/dataSources/Quidel.svelte b/src/components/dialogs/dataSources/Quidel.svelte index dd79cca..8d267c4 100644 --- a/src/components/dialogs/dataSources/Quidel.svelte +++ b/src/components/dialogs/dataSources/Quidel.svelte @@ -1,18 +1,25 @@ - + diff --git a/src/components/dialogs/dataSources/Sensors.svelte b/src/components/dialogs/dataSources/Sensors.svelte index d47c9d1..3b6e944 100644 --- a/src/components/dialogs/dataSources/Sensors.svelte +++ b/src/components/dialogs/dataSources/Sensors.svelte @@ -1,13 +1,14 @@ - + diff --git a/src/components/dialogs/dataSources/Twitter.svelte b/src/components/dialogs/dataSources/Twitter.svelte index d3800d5..79f4a6f 100644 --- a/src/components/dialogs/dataSources/Twitter.svelte +++ b/src/components/dialogs/dataSources/Twitter.svelte @@ -1,13 +1,14 @@ - +
Temporal Resolution
diff --git a/src/store.ts b/src/store.ts index 7bcc73b..512ffce 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2,6 +2,7 @@ import { get, writable } from 'svelte/store'; import { NavMode } from './components/chartUtils'; import DataSet, { DataGroup } from './data/DataSet'; import deriveLinkDefaults, { getDirectLinkImpl } from './deriveLinkDefaults'; +import ApiKeySelections, { getApiKeySelections } from './components/dialogs/apiKeySelections'; declare const __VERSION__: string; @@ -17,6 +18,18 @@ export const isShowingPoints = writable(defaults.showPoints); export const initialViewport = writable(defaults.viewport); export const navMode = writable(NavMode.autofit); +export const storeApiKeys = writable(true); +storeApiKeys.subscribe((val) => { + if (!val) { + // reset local storage if user decides not to store API keys + localStorage.removeItem('api'); + } +}); +export const apiKeySelections = writable(getApiKeySelections()); +apiKeySelections.subscribe((val) => { + localStorage.setItem('api', JSON.stringify(val)); +}); + export function addDataSet(dataset: DataSet | DataGroup): void { const root = get(datasetTree); root.datasets.push(dataset); From f50c93e71cd4665df450fc089b224d6540a25a31 Mon Sep 17 00:00:00 2001 From: Rostyslav Zatserkovnyi Date: Tue, 1 Oct 2024 17:39:46 +0300 Subject: [PATCH 2/6] store preference in localhost --- src/components/dialogs/apiKeySelections.ts | 12 ++++++++++++ src/store.ts | 5 +++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/dialogs/apiKeySelections.ts b/src/components/dialogs/apiKeySelections.ts index a3df7bc..39809b4 100644 --- a/src/components/dialogs/apiKeySelections.ts +++ b/src/components/dialogs/apiKeySelections.ts @@ -7,6 +7,18 @@ export default class ApiKeySelections { twitter = ''; } +export function getStoreApiKeys() { + try { + if (localStorage.getItem('store-api')) { + return JSON.parse(localStorage.getItem('store-api')!) as boolean; + } + return true; + } catch { + localStorage.removeItem('store-api'); + return true; + } +} + export function getApiKeySelections() { try { if (localStorage.getItem('api')) { diff --git a/src/store.ts b/src/store.ts index 512ffce..ac32df6 100644 --- a/src/store.ts +++ b/src/store.ts @@ -2,7 +2,7 @@ import { get, writable } from 'svelte/store'; import { NavMode } from './components/chartUtils'; import DataSet, { DataGroup } from './data/DataSet'; import deriveLinkDefaults, { getDirectLinkImpl } from './deriveLinkDefaults'; -import ApiKeySelections, { getApiKeySelections } from './components/dialogs/apiKeySelections'; +import { getApiKeySelections, getStoreApiKeys } from './components/dialogs/apiKeySelections'; declare const __VERSION__: string; @@ -18,12 +18,13 @@ export const isShowingPoints = writable(defaults.showPoints); export const initialViewport = writable(defaults.viewport); export const navMode = writable(NavMode.autofit); -export const storeApiKeys = writable(true); +export const storeApiKeys = writable(getStoreApiKeys()); storeApiKeys.subscribe((val) => { if (!val) { // reset local storage if user decides not to store API keys localStorage.removeItem('api'); } + localStorage.setItem('store-api', val.toString()); }); export const apiKeySelections = writable(getApiKeySelections()); apiKeySelections.subscribe((val) => { From faf4ec5762fd553c48ee11bed98793a9c70bedb9 Mon Sep 17 00:00:00 2001 From: Rostyslav Zatserkovnyi Date: Wed, 9 Oct 2024 17:08:22 +0300 Subject: [PATCH 3/6] Update to single API key --- src/components/dialogs/ImportAPIDialog.svelte | 34 ++++++++++--------- src/components/dialogs/apiKeySelections.ts | 21 ++++-------- src/components/dialogs/dataSources/CDC.svelte | 6 ++-- .../dialogs/dataSources/FluView.svelte | 6 ++-- src/components/dialogs/dataSources/GHT.svelte | 6 ++-- .../dialogs/dataSources/Quidel.svelte | 6 ++-- .../dialogs/dataSources/Sensors.svelte | 6 ++-- .../dialogs/dataSources/Twitter.svelte | 6 ++-- src/store.ts | 8 ++--- 9 files changed, 46 insertions(+), 53 deletions(-) diff --git a/src/components/dialogs/ImportAPIDialog.svelte b/src/components/dialogs/ImportAPIDialog.svelte index 4ba46b7..07e1ed5 100644 --- a/src/components/dialogs/ImportAPIDialog.svelte +++ b/src/components/dialogs/ImportAPIDialog.svelte @@ -68,14 +68,6 @@
-
-
- -
-
Data Source
diff --git a/src/components/dialogs/apiKeySelections.ts b/src/components/dialogs/apiKeySelections.ts index 39809b4..7fffa01 100644 --- a/src/components/dialogs/apiKeySelections.ts +++ b/src/components/dialogs/apiKeySelections.ts @@ -1,32 +1,23 @@ -export default class ApiKeySelections { - cdc = ''; - fluView = ''; - ght = ''; - quidel = ''; - sensors = ''; - twitter = ''; -} - export function getStoreApiKeys() { try { if (localStorage.getItem('store-api')) { return JSON.parse(localStorage.getItem('store-api')!) as boolean; } - return true; + return false; } catch { localStorage.removeItem('store-api'); - return true; + return false; } } -export function getApiKeySelections() { +export function getApiKey() { try { if (localStorage.getItem('api')) { - return JSON.parse(localStorage.getItem('api')!) as ApiKeySelections; + return JSON.parse(localStorage.getItem('api')!) as string; } - return new ApiKeySelections(); + return ''; } catch { localStorage.removeItem('api'); - return new ApiKeySelections(); + return ''; } } diff --git a/src/components/dialogs/dataSources/CDC.svelte b/src/components/dialogs/dataSources/CDC.svelte index 5b78ede..184b971 100644 --- a/src/components/dialogs/dataSources/CDC.svelte +++ b/src/components/dialogs/dataSources/CDC.svelte @@ -3,12 +3,12 @@ import { cdcLocations as regions } from '../../../data/data'; import SelectField from '../inputs/SelectField.svelte'; import TextField from '../inputs/TextField.svelte'; - import { apiKeySelections } from '../../../store'; + import { apiKey } from '../../../store'; export let id: string; let locations = regions[0].value; - let auth = $apiKeySelections.cdc; + let auth = $apiKey; export function importDataSet() { return importCDC({ locations, auth }); @@ -19,7 +19,7 @@ id="{id}-auth" name="auth" label="Authorizaton Token" - bind:value={$apiKeySelections.cdc} + bind:value={$apiKey} placeholder="authorization token" /> diff --git a/src/components/dialogs/dataSources/FluView.svelte b/src/components/dialogs/dataSources/FluView.svelte index c74d97e..b871f1d 100644 --- a/src/components/dialogs/dataSources/FluView.svelte +++ b/src/components/dialogs/dataSources/FluView.svelte @@ -1,7 +1,7 @@ diff --git a/src/components/dialogs/dataSources/FluView.svelte b/src/components/dialogs/dataSources/FluView.svelte index b871f1d..3fc4cad 100644 --- a/src/components/dialogs/dataSources/FluView.svelte +++ b/src/components/dialogs/dataSources/FluView.svelte @@ -11,10 +11,9 @@ let regions = fluViewRegions[0].value; let issue = DEFAULT_ISSUE; - let auth = $apiKey; export function importDataSet() { - return importFluView({ regions, ...issue, auth }); + return importFluView({ regions, ...issue, auth: $apiKey }); } diff --git a/src/components/dialogs/dataSources/GHT.svelte b/src/components/dialogs/dataSources/GHT.svelte index 2bee80c..b0f12f7 100644 --- a/src/components/dialogs/dataSources/GHT.svelte +++ b/src/components/dialogs/dataSources/GHT.svelte @@ -8,11 +8,10 @@ export let id: string; let locations = regions[0].value; - let auth = $apiKey; let query = ''; export function importDataSet() { - return importGHT({ auth, locations, query }); + return importGHT({ auth: $apiKey, locations, query }); } diff --git a/src/components/dialogs/dataSources/Quidel.svelte b/src/components/dialogs/dataSources/Quidel.svelte index ee5bff0..f4b8ebf 100644 --- a/src/components/dialogs/dataSources/Quidel.svelte +++ b/src/components/dialogs/dataSources/Quidel.svelte @@ -8,10 +8,9 @@ export let id: string; let locations = regions[0].value; - let auth = $apiKey; export function importDataSet() { - return importQuidel({ auth, locations }); + return importQuidel({ auth: $apiKey, locations }); } diff --git a/src/components/dialogs/dataSources/Sensors.svelte b/src/components/dialogs/dataSources/Sensors.svelte index 4fa67a1..0a577ce 100644 --- a/src/components/dialogs/dataSources/Sensors.svelte +++ b/src/components/dialogs/dataSources/Sensors.svelte @@ -8,11 +8,10 @@ export let id: string; let locations = regions[0].value; - let auth = $apiKey; let names = sensorNames[0].value; export function importDataSet() { - return importSensors({ auth, names, locations }); + return importSensors({ auth: $apiKey, names, locations }); } diff --git a/src/components/dialogs/dataSources/Twitter.svelte b/src/components/dialogs/dataSources/Twitter.svelte index 21adc9d..950c58e 100644 --- a/src/components/dialogs/dataSources/Twitter.svelte +++ b/src/components/dialogs/dataSources/Twitter.svelte @@ -8,11 +8,10 @@ export let id: string; let locations = regions[0].value; - let auth = $apiKey; let resolution: 'daily' | 'weekly' = 'daily'; export function importDataSet() { - return importTwitter({ auth, locations, resolution }); + return importTwitter({ auth: $apiKey, locations, resolution }); } diff --git a/src/store.ts b/src/store.ts index 9a42495..9ea8339 100644 --- a/src/store.ts +++ b/src/store.ts @@ -17,55 +17,25 @@ export const isShowingPoints = writable(defaults.showPoints); export const initialViewport = writable(defaults.viewport); export const navMode = writable(NavMode.autofit); -export function getStoreApiKeys() { - if (localStorage.getItem('store-api-key')) { - try { - // if we saved it, return it (as a boolean) - return localStorage.getItem('store-api-key') === 'true'; - } catch { - // if parsing fails, saved value is bad, so clear it out - localStorage.removeItem('store-api-key'); - } - } - // if parsing fails, return default of 'false' - return false; -} - -export function getApiKey() { - if (localStorage.getItem('api-key')) { - try { - return localStorage.getItem('api-key')!; - } catch { - localStorage.removeItem('api-key'); - } - } - return ''; -} - -export const storeApiKeys = writable(getStoreApiKeys()); +export const storeApiKeys = writable(localStorage.getItem('store-api-key') === 'true'); storeApiKeys.subscribe((val) => { - if (!val) { - // reset local storage if user decides not to store API keys - localStorage.removeItem('api-key'); + localStorage.setItem('store-api-key', val.toString()); + if (val) { + // persist key from session to local storage + localStorage.setItem('api-key', sessionStorage.getItem('api-key') || ''); } else { - // persist API key if user decides to store API keys - const apiKey = sessionStorage.getItem('api-key')!; - if (apiKey) { - localStorage.setItem('api-key', apiKey); - } + // remove key from local storage + localStorage.removeItem('api-key'); } - // store the preference either way - localStorage.setItem('store-api-key', val.toString()); }); -export const apiKey = writable(getApiKey()); +export const apiKey = writable(localStorage.getItem('api-key')! || ''); apiKey.subscribe((val) => { + // always keep key around in session storage (resets on page refresh) + sessionStorage.setItem('api-key', val); if (localStorage.getItem('store-api-key') === 'true') { // store it in local storage (persistent) - localStorage.setItem('api-key', val.toString()); - } else { - // keep it around in session storage (resets on page refresh) - sessionStorage.setItem('api-key', val.toString()); + localStorage.setItem('api-key', val); } }); From 20fd21f6f74b15d4b121cff55ec3eb6ee0e32e41 Mon Sep 17 00:00:00 2001 From: Rostyslav Zatserkovnyi Date: Wed, 11 Dec 2024 18:45:33 +0200 Subject: [PATCH 6/6] Remove unnecessary variables --- src/components/dialogs/dataSources/COVIDcast.svelte | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/components/dialogs/dataSources/COVIDcast.svelte b/src/components/dialogs/dataSources/COVIDcast.svelte index 517fbe8..9da4bef 100644 --- a/src/components/dialogs/dataSources/COVIDcast.svelte +++ b/src/components/dialogs/dataSources/COVIDcast.svelte @@ -8,12 +8,10 @@ export let id: string; - let api_key = ''; let data_source = ''; let signal = ''; let geo_type = ''; let geo_value = ''; - let form_key = $apiKey; let valid_key = true; let dataSources: (LabelValue & { signals: string[] })[] = []; @@ -37,12 +35,11 @@ }; function fetchMetadata() { - fetchCOVIDcastMeta(form_key).then((res) => { + fetchCOVIDcastMeta($apiKey).then((res) => { if (res.length == 0) { valid_key = false; } else { valid_key = true; - api_key = form_key; // API key is valid -> use it to fetch data later on geoTypes = [...new Set(res.map((d) => d.geo_type))]; const byDataSource = new Map(); for (const row of res) { @@ -70,10 +67,10 @@ }); export function importDataSet() { - return fetchCOVIDcastMeta(api_key).then((res) => { + return fetchCOVIDcastMeta($apiKey).then((res) => { const meta = res.filter((row) => row.data_source === data_source && row.signal === signal); const time_type = meta[0].time_type; - return importCOVIDcast({ data_source, signal, geo_type, geo_value, time_type, api_key }); + return importCOVIDcast({ data_source, signal, geo_type, geo_value, time_type, api_key: $apiKey }); }); }