Skip to content
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
85 changes: 36 additions & 49 deletions packages/apollo-collaboration-server/src/jbrowse/jbrowse.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Injectable, Logger } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { InjectModel } from '@nestjs/mongoose'
import merge from 'deepmerge'
import { Model, Types } from 'mongoose'
import { Model } from 'mongoose'

import { AssembliesService } from '../assemblies/assemblies.service'
import { RefSeqsService } from '../refSeqs/refSeqs.service'
Expand Down Expand Up @@ -138,58 +138,45 @@ export class JBrowseService {
async getAssemblies() {
const url = this.configService.get('URL', { infer: true })
const assemblies = await this.assembliesService.findAll()
return Promise.all(
assemblies.map(async (assembly) => {
const assemblyId = assembly._id.toHexString()
const refSeqs = await this.refSeqsService.findAll({
assembly: assemblyId,
})
const ids: Record<string, string> = {}
refSeqs.map((refSeq) => {
ids[refSeq.name] = (refSeq._id as Types.ObjectId).toHexString()
})
this.logger.debug(`generating assembly ${assemblyId}`)
const trackId = `sequenceConfigId-${assembly.name}`
return {
name: assemblyId,
aliases:
assembly.aliases.length > 0
? [...assembly.aliases]
: [assembly.name],
displayName: assembly.displayName || assembly.name,
sequence: {
trackId,
type: 'ReferenceSequenceTrack',
adapter: {
type: 'ApolloSequenceAdapter',
assemblyId,
baseURL: {
uri: url,
locationType: 'UriLocation',
},
},
displays: [
{
type: 'LinearApolloReferenceSequenceDisplay',
displayId: `${trackId}-LinearApolloReferenceSequenceDisplay`,
},
],
metadata: {
apollo: true,
internetAccountConfigId: this.internetAccountId,
ids,
return assemblies.map((assembly) => {
const assemblyId = assembly._id.toHexString()
const trackId = `sequenceConfigId-${assembly.name}`
return {
name: assemblyId,
aliases:
assembly.aliases.length > 0 ? [...assembly.aliases] : [assembly.name],
displayName: assembly.displayName || assembly.name,
sequence: {
trackId,
type: 'ReferenceSequenceTrack',
adapter: {
type: 'ApolloSequenceAdapter',
assemblyId,
baseURL: {
uri: url,
locationType: 'UriLocation',
},
},
refNameAliases: {
adapter: {
type: 'ApolloRefNameAliasAdapter',
assemblyId,
baseURL: { uri: url, locationType: 'UriLocation' },
displays: [
{
type: 'LinearApolloReferenceSequenceDisplay',
displayId: `${trackId}-LinearApolloReferenceSequenceDisplay`,
},
],
metadata: {
apollo: true,
internetAccountConfigId: this.internetAccountId,
},
},
refNameAliases: {
adapter: {
type: 'ApolloRefNameAliasAdapter',
assemblyId,
baseURL: { uri: url, locationType: 'UriLocation' },
},
}
}),
)
},
}
})
}

async getTracks() {
Expand Down
2 changes: 1 addition & 1 deletion packages/apollo-common/src/Change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface ClientDataStore {
assemblyName?: string,
internetAccountId?: string,
): AppRootModel['internetAccounts'][0]
loadFeatures(regions: Region[]): void
loadFeatures(regions: Region[]): Promise<void>
loadRefSeq(regions: Region[]): void
getFeature(featureId: string): AnnotationFeature | undefined
addFeature(assemblyId: string, feature: AnnotationFeatureSnapshot): void
Expand Down
18 changes: 16 additions & 2 deletions packages/apollo-shared/src/Changes/AddFeatureChange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,23 @@ export class AddFeatureChange extends FeatureChange {
for (const change of changes) {
const { addedFeature, parentFeatureId } = change
if (parentFeatureId) {
const parentFeature = dataStore.getFeature(parentFeatureId)
let parentFeature = dataStore.getFeature(parentFeatureId)
// maybe the parent feature hasn't been loaded yet
if (!parentFeature) {
throw new Error(`Could not find parent feature "${parentFeatureId}"`)
await dataStore.loadFeatures([
{
assemblyName: assembly,
refName: addedFeature.refSeq,
start: addedFeature.min,
end: addedFeature.max,
},
])
parentFeature = dataStore.getFeature(parentFeatureId)
if (!parentFeature) {
throw new Error(
`Could not find parent feature "${parentFeatureId}"`,
)
}
}
// create an ID for the parent feature if it does not have one
if (!parentFeature.attributes.get('_id')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
import type RpcServer from 'librpc-web-mod/dist/server'
import { nanoid } from 'nanoid'

import { type BackendDriver } from '../BackendDrivers'
import { type ApolloSessionModel } from '../session'

import { type RefNameAliases } from './../BackendDrivers/BackendDriver'
Expand Down Expand Up @@ -50,9 +49,10 @@ export default class RefNameAliasAdapter
if (!dataStore) {
throw new Error('No Apollo data store found')
}
const backendDriver = dataStore.getBackendDriver(
assemblyId,
) as BackendDriver
const backendDriver = dataStore.getBackendDriver(assemblyId)
if (!backendDriver) {
throw new Error('No backend driver found')
}
const refNameAliases = await backendDriver.getRefNameAliases(assemblyId)
return refNameAliases
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import SimpleFeature, { type Feature } from '@jbrowse/core/util/simpleFeature'
import { type NoAssemblyRegion, type Region } from '@jbrowse/core/util/types'
import { nanoid } from 'nanoid'

import { type BackendDriver } from '../BackendDrivers'
import { type ApolloSessionModel } from '../session'

// declare global {
Expand Down Expand Up @@ -62,9 +61,10 @@ export class ApolloSequenceAdapter extends BaseSequenceAdapter {
if (!dataStore) {
throw new Error('No Apollo data store found')
}
const backendDriver = dataStore.getBackendDriver(
assemblyId,
) as BackendDriver
const backendDriver = dataStore.getBackendDriver(assemblyId)
if (!backendDriver) {
throw new Error('No backend driver found')
}
const regions = await backendDriver.getRegions(assemblyId)
this.regions = regions
return regions
Expand Down Expand Up @@ -124,9 +124,11 @@ export class ApolloSequenceAdapter extends BaseSequenceAdapter {
observer.error('No Apollo data store found')
return
}
const backendDriver = dataStore.getBackendDriver(
assemblyId,
) as BackendDriver
const backendDriver = dataStore.getBackendDriver(assemblyId)
if (!backendDriver) {
observer.error('No backend driver found')
return
}
const regions = await backendDriver.getRegions(
regionWithAssemblyName.assemblyName,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ export interface ApolloRefSeqResponse {
assembly: string
}

interface RefSeq {
refName: string
id: string
aliases: string[]
}

type RefSeqMap = Map<string, RefSeq>

export interface ApolloInternetAccount extends BaseInternetAccountModel {
baseURL: string
socket: Socket
Expand All @@ -48,6 +56,8 @@ export interface ApolloInternetAccount extends BaseInternetAccountModel {
export class CollaborationServerDriver extends BackendDriver {
private inFlight = new Map<string, Promise<string>>()

private refSeqMaps = new Map<string, RefSeqMap>()

private async fetch(
internetAccount: ApolloInternetAccount,
info: RequestInfo,
Expand Down Expand Up @@ -97,13 +107,12 @@ export class CollaborationServerDriver extends BackendDriver {
if (!assembly) {
throw new Error(`Could not find assembly with name "${assemblyName}"`)
}
const { ids } = getConf(assembly, ['sequence', 'metadata']) as {
ids: Record<string, string>
}
const refSeq = ids[refName]
if (!refSeq) {
const refSeqMap = await this.getRefSeqMapping(assemblyName)
const refSeqEntry = refSeqMap.get(refName)
if (!refSeqEntry) {
throw new Error(`Could not find refSeq "${refName}"`)
}
const refSeq = refSeqEntry.id
const internetAccount = this.clientStore.getInternetAccount(
assemblyName,
) as ApolloInternetAccount
Expand Down Expand Up @@ -192,13 +201,12 @@ export class CollaborationServerDriver extends BackendDriver {
if (!assembly) {
throw new Error(`Could not find assembly with name "${assemblyName}"`)
}
const { ids } = getConf(assembly, ['sequence', 'metadata']) as {
ids: Record<string, string>
}
const refSeq = ids[refName]
if (!refSeq) {
const refSeqMap = await this.getRefSeqMapping(assemblyName)
const refSeqEntry = refSeqMap.get(refName)
if (!refSeqEntry) {
throw new Error(`Could not find refSeq "${refName}"`)
}
const refSeq = refSeqEntry.id
if (inFlightPromise) {
const seq = await inFlightPromise
return { seq, refSeq }
Expand Down Expand Up @@ -269,7 +277,11 @@ export class CollaborationServerDriver extends BackendDriver {
return seq
}

async getRefNameAliases(assemblyName: string): Promise<RefNameAliases[]> {
async getRefSeqMapping(assemblyName: string): Promise<RefSeqMap> {
const cachedRefSeqMap = this.refSeqMaps.get(assemblyName)
if (cachedRefSeqMap) {
return cachedRefSeqMap
}
const { assemblyManager } = getSession(this.clientStore)
const assembly = assemblyManager.get(assemblyName)
if (!assembly) {
Expand Down Expand Up @@ -299,13 +311,32 @@ export class CollaborationServerDriver extends BackendDriver {
)
}
const refSeqs = (await response.json()) as ApolloRefSeqResponse[]
return refSeqs.map((refSeq) => {
return {
refName: refSeq.name,
aliases: [...new Set([refSeq._id, ...refSeq.aliases])],
uniqueId: `alias-${refSeq._id}`,
}
}) as RefNameAliases[]
const refSeqMap = new Map<string, RefSeq>(
refSeqs.map((refSeq) => [
refSeq.name,
{ refName: refSeq.name, id: refSeq._id, aliases: refSeq.aliases },
]),
)
this.refSeqMaps.set(assemblyName, refSeqMap)
return refSeqMap
}

async getRefNameAliases(assemblyName: string): Promise<RefNameAliases[]> {
const refSeqMap = await this.getRefSeqMapping(assemblyName)
return [...refSeqMap.values()].map((refSeq) => ({
refName: refSeq.refName,
aliases: [...new Set([refSeq.id, ...refSeq.aliases])],
uniqueId: `alias-${refSeq.id}`,
}))
}

async getRefSeqId(assemblyName: string, refName: string) {
const refSeqMap = await this.getRefSeqMapping(assemblyName)
if (!refSeqMap) {
return
}
const refSeq = refSeqMap.get(refName)
return refSeq?.id
}

async getRegions(assemblyName: string): Promise<Region[]> {
Expand Down
4 changes: 3 additions & 1 deletion packages/jbrowse-plugin-apollo/src/ChangeManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ export class ChangeManager {
// submit to driver
const { collaborationServerDriver, getBackendDriver } = this.dataStore
const backendDriver = isAssemblySpecificChange(change)
? getBackendDriver(change.assembly)
? // for assembly-specific change, fall back in case it's an
// add-assembly change, since that won't exist in the driver yet
getBackendDriver(change.assembly) ?? collaborationServerDriver
: collaborationServerDriver
let backendResult: ValidationResultSet
try {
Expand Down
5 changes: 2 additions & 3 deletions packages/jbrowse-plugin-apollo/src/components/AddAssembly.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,12 @@ export function AddAssembly({
const formData = new FormData()
let filename = file.name
const isGzip =
fileType === FileType.BGZIP_FASTA ||
(fileType === FileType.FASTA &&
(!sequenceIsEditable || fastaGzipChecked)) ||
(fileType === FileType.GFF3 && gff3GzipChecked)

if (fileType === FileType.FAI || fileType === FileType.GZI) {
filename = `${filename}.txt`
} else if (isGzip && !file.name.toLocaleLowerCase().endsWith('.gz')) {
if (isGzip && !file.name.toLocaleLowerCase().endsWith('.gz')) {
filename = `${filename}.gz`
} else if (!isGzip && file.name.toLocaleLowerCase().endsWith('.gz')) {
filename = `${filename}.txt`
Expand Down
Loading