Skip to content

Commit

Permalink
Create ApolloRefNameAliasAdapter (GMOD#417)
Browse files Browse the repository at this point in the history
* ref name alias apollo configuration

* Fix error message

---------

Co-authored-by: Garrett Stevens <stevens.garrett.j@gmail.com>
  • Loading branch information
shashankbrgowda and garrettjstevens authored Jul 23, 2024
1 parent 4ecf6e3 commit cb1aced
Show file tree
Hide file tree
Showing 9 changed files with 234 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { RefNameAliases } from './../BackendDrivers/BackendDriver'
import {
BaseRefNameAliasAdapter,
BaseAdapter,
} from '@jbrowse/core/data_adapters/BaseAdapter'
import { readConfObject } from '@jbrowse/core/configuration'
import { ApolloSessionModel } from '../session'
import { BackendDriver } from '../BackendDrivers'
import { nanoid } from 'nanoid'
import RpcServer from 'librpc-web-mod/dist/server'

declare global {
const rpcServer: RpcServer
}

interface ApolloRefNameAliasMessage {
apollo: true
messageId: string
refNameAliases: RefNameAliases[]
}

function isApolloRefNameAliasMessage(
data?: unknown,
): data is ApolloRefNameAliasMessage {
return (
typeof data === 'object' &&
data !== null &&
'apollo' in data &&
data.apollo === true &&
'refNameAliases' in data
)
}

const isInWebWorker = typeof sessionStorage === 'undefined'

export default class RefNameAliasAdapter
extends BaseAdapter
implements BaseRefNameAliasAdapter
{
private refNameAliases: RefNameAliases[] | undefined

async getRefNameAliases() {
const assemblyId = readConfObject(this.config, 'assemblyId') as string
if (!isInWebWorker) {
const dataStore = (
this.pluginManager?.rootModel?.session as ApolloSessionModel | undefined
)?.apolloDataStore
if (!dataStore) {
throw new Error('No Apollo data store found')
}
const backendDriver = dataStore.getBackendDriver(
assemblyId,
) as BackendDriver
const refNameAliases = await backendDriver.getRefNameAliases(assemblyId)
return refNameAliases
}
const refNameAliases = await new Promise(
(
resolve: (refNameAliases: RefNameAliases[]) => void,
reject: (reason: Error) => void,
) => {
const timeoutId = setTimeout(() => {
reject(new Error('timeout'))
}, 20_000)
const messageId = nanoid()
const messageListener = (event: MessageEvent) => {
const data = event.data as ApolloRefNameAliasMessage
if (!isApolloRefNameAliasMessage(data)) {
return
}
if (data.messageId !== messageId) {
return
}
clearTimeout(timeoutId)
removeEventListener('message', messageListener)
resolve(data.refNameAliases)
}
addEventListener('message', messageListener)
rpcServer.emit('apollo', {
apollo: true,
method: 'getRefNameAliases',
assembly: assemblyId,
messageId,
})
},
)
this.refNameAliases = refNameAliases
return refNameAliases
}

async freeResources() {
// no resources to free
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { ConfigurationSchema } from '@jbrowse/core/configuration'

export default ConfigurationSchema(
'ApolloRefNameAliasAdapter',
{
assemblyId: {
type: 'string',
defaultValue: '',
},
},
{ explicitlyTyped: true },
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import AdapterType from '@jbrowse/core/pluggableElementTypes/AdapterType'
import PluginManager from '@jbrowse/core/PluginManager'

import configSchema from './configSchema'
import ApolloRefNameAliasAdapter from './ApolloRefNameAliasAdapter'

export function installApolloRefNameAliasAdapter(pluginManager: PluginManager) {
pluginManager.addAdapterType(
() =>
new AdapterType({
name: 'ApolloRefNameAliasAdapter',
configSchema,
adapterMetadata: {
category: undefined,
hiddenFromGUI: true,
description: undefined,
},
AdapterClass: ApolloRefNameAliasAdapter,
}),
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ import { Region } from '@jbrowse/core/util'

import { SubmitOpts } from '../ChangeManager'

export interface RefNameAliases {
refName: string
aliases: string[]
uniqueId: string
}

export abstract class BackendDriver {
constructor(protected clientStore: ClientDataStore) {}

Expand All @@ -22,6 +28,8 @@ export abstract class BackendDriver {

abstract getAssemblies(internetAccountConfigId?: string): Assembly[]

abstract getRefNameAliases(assemblyName: string): Promise<RefNameAliases[]>

abstract submitChange(
change: Change,
opts: SubmitOpts,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ import { Socket } from 'socket.io-client'

import { ChangeManager, SubmitOpts } from '../ChangeManager'
import { createFetchErrorMessage } from '../util'
import { BackendDriver } from './BackendDriver'
import { BackendDriver, RefNameAliases } from './BackendDriver'
import { ApolloRefSeqResponse } from '../session'

export interface ApolloInternetAccount extends BaseInternetAccountModel {
baseURL: string
Expand Down Expand Up @@ -235,6 +236,45 @@ export class CollaborationServerDriver extends BackendDriver {
return seq
}

async getRefNameAliases(assemblyName: string): Promise<RefNameAliases[]> {
const { assemblyManager } = getSession(this.clientStore)
const assembly = assemblyManager.get(assemblyName)
if (!assembly) {
throw new Error(`Could not find assembly with name "${assemblyName}"`)
}
const internetAccount = this.clientStore.getInternetAccount(
assemblyName,
) as ApolloInternetAccount
const { baseURL } = internetAccount
const url = new URL('refSeqs', baseURL)
const searchParams = new URLSearchParams({ assembly: assemblyName })
url.search = searchParams.toString()
const uri = url.toString()

const response = await this.fetch(internetAccount, uri)
if (!response.ok) {
let errorMessage
try {
errorMessage = await response.text()
} catch {
errorMessage = ''
}
throw new Error(
`getRefNameAliases failed: ${response.status} (${response.statusText})${
errorMessage ? ` (${errorMessage})` : ''
}`,
)
}
const refSeqs = (await response.json()) as ApolloRefSeqResponse[]
return refSeqs.map((refSeq) => {
return {
refName: refSeq.name,
aliases: [refSeq._id],
uniqueId: `alias-${refSeq._id}`,
}
}) as RefNameAliases[]
}

async getRegions(assemblyName: string): Promise<Region[]> {
const { assemblyManager } = getSession(this.clientStore)
const assembly = assemblyManager.get(assemblyName)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { Region, getSession } from '@jbrowse/core/util'
import { getSnapshot } from 'mobx-state-tree'

import { checkFeatures, loadAssemblyIntoClient } from '../util'
import { BackendDriver } from './BackendDriver'
import { BackendDriver, RefNameAliases } from './BackendDriver'

export class DesktopFileDriver extends BackendDriver {
async loadAssembly(assemblyName: string) {
Expand All @@ -45,6 +45,19 @@ export class DesktopFileDriver extends BackendDriver {
return assembly
}

async getRefNameAliases(assemblyName: string): Promise<RefNameAliases[]> {
const assembly = await this.getAssembly(assemblyName)
const refNameAliases: RefNameAliases[] = []
for (const [, refSeq] of assembly.refSeqs) {
refNameAliases.push({
refName: refSeq.name,
aliases: [refSeq._id],
uniqueId: `alias-${refSeq._id}`,
})
}
return refNameAliases
}

async getFeatures(
region: Region,
): Promise<[AnnotationFeatureSnapshot[], CheckResultSnapshot[]]> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getConf } from '@jbrowse/core/configuration'
import { Region, getSession } from '@jbrowse/core/util'

import { SubmitOpts } from '../ChangeManager'
import { BackendDriver } from './BackendDriver'
import { BackendDriver, RefNameAliases } from './BackendDriver'

export class InMemoryFileDriver extends BackendDriver {
async getFeatures(): Promise<
Expand All @@ -32,6 +32,22 @@ export class InMemoryFileDriver extends BackendDriver {
return { seq, refSeq: refName }
}

async getRefNameAliases(assemblyName: string): Promise<RefNameAliases[]> {
const assembly = this.clientStore.assemblies.get(assemblyName)
const refNameAliases: RefNameAliases[] = []
if (!assembly) {
return refNameAliases
}
for (const [, refSeq] of assembly.refSeqs) {
refNameAliases.push({
refName: refSeq.name,
aliases: [refSeq._id],
uniqueId: `alias-${refSeq._id}`,
})
}
return refNameAliases
}

async getRegions(assemblyName: string): Promise<Region[]> {
const assembly = this.clientStore.assemblies.get(assemblyName)
if (!assembly) {
Expand Down
24 changes: 24 additions & 0 deletions packages/jbrowse-plugin-apollo/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import {
stateModelFactory as SixFrameFeatureDisplayStateModelFactory,
configSchemaFactory as sixFrameFeatureDisplayConfigSchemaFactory,
} from './SixFrameFeatureDisplay'
import { installApolloRefNameAliasAdapter } from './ApolloRefNameAliasAdapter'

interface RpcHandle {
on(event: string, listener: (event: MessageEvent) => void): this
Expand Down Expand Up @@ -114,6 +115,7 @@ export default class ApolloPlugin extends Plugin {

install(pluginManager: PluginManager) {
installApolloSequenceAdapter(pluginManager)
installApolloRefNameAliasAdapter(pluginManager)
installApolloTextSearchAdapter(pluginManager)

pluginManager.addWidgetType(() => {
Expand Down Expand Up @@ -317,6 +319,28 @@ export default class ApolloPlugin extends Plugin {
})
break
}
case 'getRefNameAliases': {
const { assembly } = event
const dataStore = (
pluginManager.rootModel?.session as
| ApolloSessionModel
| undefined
)?.apolloDataStore
if (!dataStore) {
break
}
const backendDriver = dataStore.getBackendDriver(
assembly,
) as BackendDriver
const refNameAliases =
await backendDriver.getRefNameAliases(assembly)
handle.workers[0].postMessage({
apollo,
messageId,
refNameAliases,
})
break
}
default: {
break
}
Expand Down
10 changes: 3 additions & 7 deletions packages/jbrowse-plugin-apollo/src/session/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,6 @@ export function extendSession(
const ids: Record<string, string> = {}
const refNameAliasesFeatures = f.map((contig) => {
ids[contig.name] = contig._id
return {
refName: contig.name,
aliases: [contig._id],
uniqueId: `alias-${contig._id}`,
}
})
const assemblyConfig = {
name: assembly._id,
Expand All @@ -344,8 +339,9 @@ export function extendSession(
},
refNameAliases: {
adapter: {
type: 'FromConfigAdapter',
features: refNameAliasesFeatures,
type: 'ApolloRefNameAliasAdapter',
assemblyId: assembly._id,
baseURL: { uri: baseURL, locationType: 'UriLocation' },
},
},
}
Expand Down

0 comments on commit cb1aced

Please sign in to comment.