Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to load a ?hubURL= from URL bar #4745

Merged
merged 5 commits into from
Jan 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 25 additions & 21 deletions packages/web-core/src/SessionConnections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,40 +32,44 @@ export function WebSessionConnectionsMixin(pluginManager: PluginManager) {
const superDeleteConnection = self.deleteConnection
const superAddConnectionConf = self.addConnectionConf
return {
/**
* #action
*/
addConnectionConf(connectionConf: BaseConnectionConfigModel) {
if (self.adminMode) {
return superAddConnectionConf(connectionConf)
} else {
const { connectionId, type } = connectionConf
if (!type) {
throw new Error(`unknown connection type ${type}`)
}
const connection = self.sessionTracks.find(
c => c.connectionId === connectionId,
)
if (connection) {
return connection
} else {
const length = self.sessionConnections.push(connectionConf)
return self.sessionConnections[length - 1]
}
}
const { connectionId, type } = connectionConf
if (!type) {
throw new Error(`unknown connection type ${type}`)
}
const connection = self.sessionTracks.find(
c => c.connectionId === connectionId,
)
if (connection) {
return connection
}
const length = self.sessionConnections.push(connectionConf)
return self.sessionConnections[length - 1]
},

/**
* #action
*/
deleteConnection(configuration: AnyConfigurationModel) {
let deletedConn: unknown
if (self.adminMode) {
deletedConn = superDeleteConnection(configuration)
}
if (!deletedConn) {
return superDeleteConnection(configuration)
} else {
const { connectionId } = configuration
const idx = self.sessionConnections.findIndex(
c => c.connectionId === connectionId,
)
if (idx === -1) {
return undefined
}
return self.sessionConnections.splice(idx, 1)
return idx === -1
? undefined
: self.sessionConnections.splice(idx, 1)
}
return deletedConn
},
}
})
Expand Down
117 changes: 75 additions & 42 deletions products/jbrowse-web/src/SessionLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,10 @@ import { addDisposer, types } from 'mobx-state-tree'
import { readSessionFromDynamo } from './sessionSharing'
import { addRelativeUris, checkPlugins, fromUrlSafeB64, readConf } from './util'

import type { SessionTriagedInfo } from './types'
import type { PluginDefinition, PluginRecord } from '@jbrowse/core/PluginLoader'
import type { Instance } from 'mobx-state-tree'

export interface SessionTriagedInfo {
snap: unknown
origin: string
reason: PluginDefinition[]
}

const SessionLoader = types
.model({
/**
Expand Down Expand Up @@ -66,6 +61,11 @@ const SessionLoader = types
* #property
*/
initialTimestamp: types.number,

/**
* #property
*/
hubURL: types.maybe(types.array(types.string)),
})
.volatile(() => ({
/**
Expand All @@ -84,6 +84,10 @@ const SessionLoader = types
* #volatile
*/
sessionSpec: undefined as Record<string, unknown> | undefined,
/**
* #volatile
*/
hubSpec: undefined as Record<string, unknown> | undefined,
/**
* #volatile
*/
Expand Down Expand Up @@ -134,6 +138,12 @@ const SessionLoader = types
get isSpecSession() {
return !!self.sessionQuery?.startsWith('spec-')
},
/**
* #getter
*/
get isHubSession() {
return !!self.hubURL
},
/**
* #getter
*/
Expand Down Expand Up @@ -275,8 +285,7 @@ const SessionLoader = types
try {
const pluginLoader = new PluginLoader(snap.sessionPlugins || [], {
fetchESM: url => import(/* webpackIgnore:true */ url),
})
pluginLoader.installGlobalReExports(window)
}).installGlobalReExports(window)
const plugins = await pluginLoader.load(window.location.href)
self.setSessionPlugins([...plugins])
} catch (e) {
Expand Down Expand Up @@ -315,38 +324,38 @@ const SessionLoader = types
*/
async fetchConfig() {
// @ts-expect-error
const path = window.__jbrowseConfigPath
const { hubURL, configPath = path || 'config.json' } = self
if (!hubURL) {
const text = await openLocation({
uri:
configPath +
// @ts-expect-error
(window.__jbrowseCacheBuster ? `?rand=${Math.random()}` : ''),
locationType: 'UriLocation',
}).readFile('utf8')
const config = JSON.parse(text)
const configUri = new URL(configPath, window.location.href)
addRelativeUris(config, configUri)

let { configPath = window.__jbrowseConfigPath || 'config.json' } = self

// @ts-expect-error

if (window.__jbrowseCacheBuster) {
configPath += `?rand=${Math.random()}`
}

const text = await openLocation({
uri: configPath,
locationType: 'UriLocation',
}).readFile('utf8')
const config = JSON.parse(text)
const configUri = new URL(configPath, window.location.href)
addRelativeUris(config, configUri)

// cross origin config check
if (configUri.hostname !== window.location.hostname) {
const configPlugins = config.plugins || []
const configPluginsAllowed = await checkPlugins(configPlugins)
if (!configPluginsAllowed) {
self.setSessionTriaged({
snap: config,
origin: 'config',
reason: configPlugins,
})
return
// cross origin config check
if (configUri.hostname !== window.location.hostname) {
const configPlugins = config.plugins || []
const configPluginsAllowed = await checkPlugins(configPlugins)
if (!configPluginsAllowed) {
self.setSessionTriaged({
snap: config,
origin: 'config',
reason: configPlugins,
})
return
}
}
await this.fetchPlugins(config)
self.setConfigSnapshot(config)
} else {
self.setConfigSnapshot({})
}
await this.fetchPlugins(config)
self.setConfigSnapshot(config)
},
/**
* #action
Expand Down Expand Up @@ -399,7 +408,10 @@ const SessionLoader = types
)

const session = JSON.parse(await fromUrlSafeB64(decryptedSession))
await this.setSessionSnapshot({ ...session, id: nanoid() })
await this.setSessionSnapshot({
...session,
id: nanoid(),
})
},
/**
* #action
Expand All @@ -409,7 +421,10 @@ const SessionLoader = types
// @ts-expect-error
await fromUrlSafeB64(self.sessionQuery.replace('encoded-', '')),
)
await this.setSessionSnapshot({ ...session, id: nanoid() })
await this.setSessionSnapshot({
...session,
id: nanoid(),
})
},
/**
* #action
Expand Down Expand Up @@ -451,13 +466,28 @@ const SessionLoader = types
}
}
},

/**
* #action
*/
decodeHubSpec() {
const { hubURL, sessionTracksParsed: sessionTracks } = self

self.hubSpec = {
sessionTracks,
hubURL,
}
},
/**
* #action
*/
async decodeJsonUrlSession() {
// @ts-expect-error
const session = JSON.parse(self.sessionQuery.replace('json-', ''))
await this.setSessionSnapshot({ ...session.session, id: nanoid() })
const { session } = JSON.parse(self.sessionQuery.replace(/^json-/, ''))
await this.setSessionSnapshot({
...session,
id: nanoid(),
})
},
/**
* #aftercreate
Expand All @@ -479,6 +509,7 @@ const SessionLoader = types
isSharedSession,
isJsonSession,
isJb1StyleSession,
isHubSession,
sessionQuery,
configSnapshot,
} = self
Expand All @@ -505,6 +536,9 @@ const SessionLoader = types
this.decodeJb1StyleSession()
} else if (isEncodedSession) {
await this.decodeEncodedUrlSession()
} else if (isHubSession) {
this.decodeHubSpec()
self.setBlankSession(true)
} else if (isJsonSession) {
await this.decodeJsonUrlSession()
} else if (isLocalSession) {
Expand All @@ -525,7 +559,6 @@ const SessionLoader = types
} catch (e) {
console.error(e)
self.setConfigError(e)
return
}
})()
},
Expand Down
31 changes: 1 addition & 30 deletions products/jbrowse-web/src/components/ConfigWarningDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { pluginDescriptionString } from '@jbrowse/core/PluginLoader'
import { Dialog } from '@jbrowse/core/ui'
import { nanoid } from '@jbrowse/core/util/nanoid'
import WarningIcon from '@mui/icons-material/Warning'
import {
Button,
Expand All @@ -9,12 +8,9 @@ import {
DialogContentText,
} from '@mui/material'

import factoryReset from '../factoryReset'

import type { SessionLoaderModel } from '../SessionLoader'
import type { PluginDefinition } from '@jbrowse/core/PluginLoader'

function ConfigWarningDialog({
export default function ConfigWarningDialog({
onConfirm,
onCancel,
reason,
Expand Down Expand Up @@ -61,28 +57,3 @@ function ConfigWarningDialog({
</Dialog>
)
}

export default function ConfigTriaged({
loader,
handleClose,
}: {
loader: SessionLoaderModel
handleClose: () => void
}) {
const { sessionTriaged } = loader
return sessionTriaged ? (
<ConfigWarningDialog
onConfirm={async () => {
const session = JSON.parse(JSON.stringify(sessionTriaged.snap))
await loader.fetchPlugins(session)
loader.setConfigSnapshot({ ...session, id: nanoid() })
handleClose()
}}
onCancel={async () => {
await factoryReset()
handleClose()
}}
reason={sessionTriaged.reason}
/>
) : null
}
Loading
Loading