@apollo-annotation/jbrowse-plugin-apollo 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +76 -0
  2. package/dist/index.esm.js +10248 -0
  3. package/dist/index.esm.js.map +1 -0
  4. package/dist/index.js +7 -0
  5. package/dist/jbrowse-plugin-apollo.cjs.development.js +10298 -0
  6. package/dist/jbrowse-plugin-apollo.cjs.development.js.map +1 -0
  7. package/dist/jbrowse-plugin-apollo.cjs.production.min.js +2 -0
  8. package/dist/jbrowse-plugin-apollo.cjs.production.min.js.map +1 -0
  9. package/dist/jbrowse-plugin-apollo.umd.development.js +46957 -0
  10. package/dist/jbrowse-plugin-apollo.umd.development.js.map +1 -0
  11. package/dist/jbrowse-plugin-apollo.umd.production.min.js +2 -0
  12. package/dist/jbrowse-plugin-apollo.umd.production.min.js.map +1 -0
  13. package/package.json +130 -0
  14. package/src/ApolloInternetAccount/addMenuItems.ts +94 -0
  15. package/src/ApolloInternetAccount/components/AuthTypeSelector.tsx +121 -0
  16. package/src/ApolloInternetAccount/components/LoginButtons.tsx +62 -0
  17. package/src/ApolloInternetAccount/components/LoginIcons.tsx +74 -0
  18. package/src/ApolloInternetAccount/configSchema.ts +26 -0
  19. package/src/ApolloInternetAccount/index.ts +2 -0
  20. package/src/ApolloInternetAccount/model.ts +448 -0
  21. package/src/ApolloJobModel.ts +117 -0
  22. package/src/ApolloSequenceAdapter/ApolloSequenceAdapter.ts +186 -0
  23. package/src/ApolloSequenceAdapter/configSchema.ts +12 -0
  24. package/src/ApolloSequenceAdapter/index.ts +21 -0
  25. package/src/ApolloSixFrameRenderer/ApolloSixFrameRenderer.tsx +12 -0
  26. package/src/ApolloSixFrameRenderer/components/ApolloRendering.tsx +692 -0
  27. package/src/ApolloSixFrameRenderer/configSchema.ts +7 -0
  28. package/src/ApolloSixFrameRenderer/index.ts +3 -0
  29. package/src/ApolloTextSearchAdapter/ApolloTextSearchAdapter.ts +64 -0
  30. package/src/ApolloTextSearchAdapter/configSchema.ts +24 -0
  31. package/src/ApolloTextSearchAdapter/index.ts +18 -0
  32. package/src/BackendDrivers/BackendDriver.ts +31 -0
  33. package/src/BackendDrivers/CollaborationServerDriver.ts +318 -0
  34. package/src/BackendDrivers/DesktopFileDriver.ts +170 -0
  35. package/src/BackendDrivers/InMemoryFileDriver.ts +76 -0
  36. package/src/BackendDrivers/index.ts +4 -0
  37. package/src/ChangeManager.ts +148 -0
  38. package/src/LinearApolloDisplay/components/LinearApolloDisplay.tsx +248 -0
  39. package/src/LinearApolloDisplay/components/index.ts +1 -0
  40. package/src/LinearApolloDisplay/configSchema.ts +16 -0
  41. package/src/LinearApolloDisplay/glyphs/BoxGlyph.ts +422 -0
  42. package/src/LinearApolloDisplay/glyphs/CanonicalGeneGlyph.ts +1191 -0
  43. package/src/LinearApolloDisplay/glyphs/GenericChildGlyph.ts +151 -0
  44. package/src/LinearApolloDisplay/glyphs/Glyph.ts +382 -0
  45. package/src/LinearApolloDisplay/glyphs/ImplicitExonGeneGlyph.ts +697 -0
  46. package/src/LinearApolloDisplay/glyphs/index.ts +4 -0
  47. package/src/LinearApolloDisplay/index.ts +2 -0
  48. package/src/LinearApolloDisplay/stateModel/base.ts +146 -0
  49. package/src/LinearApolloDisplay/stateModel/getGlyph.ts +39 -0
  50. package/src/LinearApolloDisplay/stateModel/glyphs.ts +45 -0
  51. package/src/LinearApolloDisplay/stateModel/index.ts +20 -0
  52. package/src/LinearApolloDisplay/stateModel/layouts.ts +230 -0
  53. package/src/LinearApolloDisplay/stateModel/mouseEvents.ts +513 -0
  54. package/src/LinearApolloDisplay/stateModel/rendering.ts +441 -0
  55. package/src/LinearApolloDisplay/stateModel/trackHeightMixin.ts +43 -0
  56. package/src/LinearApolloDisplay/types.ts +1 -0
  57. package/src/OntologyManager/OntologyStore/__snapshots__/fulltext.test.ts.snap +208 -0
  58. package/src/OntologyManager/OntologyStore/__snapshots__/index.test.ts.snap +18846 -0
  59. package/src/OntologyManager/OntologyStore/fulltext-stopwords.ts +137 -0
  60. package/src/OntologyManager/OntologyStore/fulltext.test.ts +94 -0
  61. package/src/OntologyManager/OntologyStore/fulltext.ts +264 -0
  62. package/src/OntologyManager/OntologyStore/index.test.ts +130 -0
  63. package/src/OntologyManager/OntologyStore/index.ts +526 -0
  64. package/src/OntologyManager/OntologyStore/indexeddb-schema.ts +89 -0
  65. package/src/OntologyManager/OntologyStore/indexeddb-storage.ts +180 -0
  66. package/src/OntologyManager/OntologyStore/obo-graph-json-schema.ts +110 -0
  67. package/src/OntologyManager/OntologyStore/prefixes.ts +35 -0
  68. package/src/OntologyManager/index.ts +173 -0
  69. package/src/SixFrameFeatureDisplay/components/TrackLines.tsx +19 -0
  70. package/src/SixFrameFeatureDisplay/components/index.ts +1 -0
  71. package/src/SixFrameFeatureDisplay/configSchema.ts +21 -0
  72. package/src/SixFrameFeatureDisplay/index.ts +2 -0
  73. package/src/SixFrameFeatureDisplay/stateModel.ts +413 -0
  74. package/src/TabularEditor/HybridGrid/ChangeHandling.ts +88 -0
  75. package/src/TabularEditor/HybridGrid/Feature.tsx +346 -0
  76. package/src/TabularEditor/HybridGrid/FeatureAttributes.tsx +34 -0
  77. package/src/TabularEditor/HybridGrid/Highlight.tsx +40 -0
  78. package/src/TabularEditor/HybridGrid/HybridGrid.tsx +138 -0
  79. package/src/TabularEditor/HybridGrid/NumberCell.tsx +77 -0
  80. package/src/TabularEditor/HybridGrid/ToolBar.tsx +59 -0
  81. package/src/TabularEditor/HybridGrid/featureContextMenuItems.ts +119 -0
  82. package/src/TabularEditor/HybridGrid/index.ts +1 -0
  83. package/src/TabularEditor/TabularEditorPane.tsx +34 -0
  84. package/src/TabularEditor/index.ts +3 -0
  85. package/src/TabularEditor/model.ts +44 -0
  86. package/src/TabularEditor/types.ts +3 -0
  87. package/src/components/AddAssembly.tsx +464 -0
  88. package/src/components/AddChildFeature.tsx +247 -0
  89. package/src/components/AddFeature.tsx +252 -0
  90. package/src/components/CopyFeature.tsx +328 -0
  91. package/src/components/DeleteAssembly.tsx +185 -0
  92. package/src/components/DeleteFeature.tsx +90 -0
  93. package/src/components/Dialog.tsx +47 -0
  94. package/src/components/DownloadGFF3.tsx +213 -0
  95. package/src/components/ImportFeatures.tsx +295 -0
  96. package/src/components/ManageChecks.tsx +280 -0
  97. package/src/components/ManageUsers.tsx +218 -0
  98. package/src/components/ModifyFeatureAttribute.tsx +457 -0
  99. package/src/components/OntologyTermAutocomplete.tsx +240 -0
  100. package/src/components/OntologyTermMultiSelect.tsx +349 -0
  101. package/src/components/OpenLocalFile.tsx +178 -0
  102. package/src/components/ViewChangeLog.tsx +208 -0
  103. package/src/components/ViewCheckResults.tsx +151 -0
  104. package/src/components/index.ts +12 -0
  105. package/src/config.ts +10 -0
  106. package/src/declare.d.ts +3 -0
  107. package/src/extensions/annotationFromPileup.ts +208 -0
  108. package/src/extensions/index.ts +1 -0
  109. package/src/index.ts +394 -0
  110. package/src/makeDisplayComponent.tsx +244 -0
  111. package/src/session/ClientDataStore.ts +282 -0
  112. package/src/session/index.ts +1 -0
  113. package/src/session/session.ts +373 -0
  114. package/src/types.ts +10 -0
  115. package/src/util/index.ts +31 -0
  116. package/src/util/loadAssemblyIntoClient.ts +291 -0
@@ -0,0 +1,282 @@
1
+ import { getConf, readConfObject } from '@jbrowse/core/configuration'
2
+ import { ConfigurationModel } from '@jbrowse/core/configuration/types'
3
+ import { Region, getSession, isElectron } from '@jbrowse/core/util'
4
+ import { LocalPathLocation, UriLocation } from '@jbrowse/core/util/types/mst'
5
+ import { ClientDataStore as ClientDataStoreType } from 'apollo-common'
6
+ import {
7
+ AnnotationFeature,
8
+ AnnotationFeatureSnapshot,
9
+ ApolloAssembly,
10
+ ApolloRefSeq,
11
+ CheckResult,
12
+ CheckResultSnapshot,
13
+ } from 'apollo-mst'
14
+ import {
15
+ Instance,
16
+ SnapshotOut,
17
+ flow,
18
+ getParentOfType,
19
+ getRoot,
20
+ resolveIdentifier,
21
+ types,
22
+ } from 'mobx-state-tree'
23
+
24
+ import {
25
+ ApolloInternetAccount,
26
+ CollaborationServerDriver,
27
+ DesktopFileDriver,
28
+ InMemoryFileDriver,
29
+ } from '../BackendDrivers'
30
+ import { ChangeManager } from '../ChangeManager'
31
+ import ApolloPluginConfigurationSchema from '../config'
32
+ import {
33
+ OntologyManagerType,
34
+ OntologyRecordConfiguration,
35
+ TextIndexFieldDefinition,
36
+ } from '../OntologyManager'
37
+ import { ApolloRootModel } from '../types'
38
+
39
+ export function clientDataStoreFactory(
40
+ AnnotationFeatureExtended: typeof AnnotationFeature,
41
+ ) {
42
+ const clientStoreType = types
43
+ .model('ClientDataStore', {
44
+ typeName: types.optional(types.literal('Client'), 'Client'),
45
+ assemblies: types.map(ApolloAssembly),
46
+ checkResults: types.map(CheckResult),
47
+ })
48
+ .views((self) => ({
49
+ get internetAccounts() {
50
+ return getRoot<ApolloRootModel>(self).internetAccounts
51
+ },
52
+
53
+ get pluginConfiguration() {
54
+ return getRoot<ApolloRootModel>(self).jbrowse.configuration
55
+ .ApolloPlugin as Instance<typeof ApolloPluginConfigurationSchema>
56
+ },
57
+ getFeature(featureId: string) {
58
+ return resolveIdentifier(
59
+ AnnotationFeatureExtended,
60
+ self.assemblies,
61
+ featureId,
62
+ )
63
+ },
64
+ }))
65
+ .actions((self) => ({
66
+ addAssembly(assemblyId: string) {
67
+ return self.assemblies.put({ _id: assemblyId, refSeqs: {} })
68
+ },
69
+ addFeature(assemblyId: string, feature: AnnotationFeatureSnapshot) {
70
+ const assembly = self.assemblies.get(assemblyId)
71
+ if (!assembly) {
72
+ throw new Error(
73
+ `Could not find assembly "${assemblyId}" to add feature "${feature._id}"`,
74
+ )
75
+ }
76
+ const ref = assembly.refSeqs.get(feature.refSeq)
77
+ if (!ref) {
78
+ throw new Error(
79
+ `Could not find refSeq "${feature.refSeq}" to add feature "${feature._id}"`,
80
+ )
81
+ }
82
+ ref.features.put(feature)
83
+ },
84
+ deleteFeature(featureId: string) {
85
+ const feature = self.getFeature(featureId)
86
+ if (!feature) {
87
+ throw new Error(`Could not find feature "${featureId}" to delete`)
88
+ }
89
+ const { _id, parent } = feature
90
+ if (parent) {
91
+ parent.deleteChild(featureId)
92
+ } else {
93
+ const refSeq = getParentOfType(feature, ApolloRefSeq)
94
+ refSeq.deleteFeature(_id)
95
+ }
96
+ },
97
+ deleteAssembly(assemblyId: string) {
98
+ self.assemblies.delete(assemblyId)
99
+ },
100
+ addCheckResult(checkResult: CheckResultSnapshot) {
101
+ self.checkResults.put(checkResult)
102
+ },
103
+ addCheckResults(checkResults: CheckResultSnapshot[]) {
104
+ for (const checkResult of checkResults) {
105
+ if (!self.checkResults.has(checkResult._id)) {
106
+ self.checkResults.put(checkResult)
107
+ }
108
+ }
109
+ },
110
+ deleteCheckResult(checkResultId: string) {
111
+ self.checkResults.delete(checkResultId)
112
+ },
113
+ }))
114
+ .volatile((self) => ({
115
+ changeManager: new ChangeManager(self as unknown as ClientDataStoreType),
116
+ collaborationServerDriver: new CollaborationServerDriver(
117
+ self as unknown as ClientDataStoreType,
118
+ ),
119
+ inMemoryFileDriver: new InMemoryFileDriver(
120
+ self as unknown as ClientDataStoreType,
121
+ ),
122
+ desktopFileDriver: isElectron
123
+ ? new DesktopFileDriver(self as unknown as ClientDataStoreType)
124
+ : undefined,
125
+ ontologyManager: OntologyManagerType.create(),
126
+ }))
127
+ .actions((self) => ({
128
+ afterCreate() {
129
+ // Merge in the ontologies from our plugin configuration.
130
+ // Ontologies of a given name that are already in the session
131
+ // take precedence over the ontologies in the configuration.
132
+ const { ontologyManager, pluginConfiguration } = self
133
+ const configuredOntologies =
134
+ pluginConfiguration.ontologies as ConfigurationModel<
135
+ typeof OntologyRecordConfiguration
136
+ >[]
137
+
138
+ for (const ont of configuredOntologies || []) {
139
+ const [name, version, source, indexFields] = [
140
+ readConfObject(ont, 'name') as string,
141
+ readConfObject(ont, 'version') as string,
142
+ readConfObject(ont, 'source') as
143
+ | Instance<typeof LocalPathLocation>
144
+ | Instance<typeof UriLocation>,
145
+ readConfObject(
146
+ ont,
147
+ 'textIndexFields',
148
+ ) as TextIndexFieldDefinition[],
149
+ ]
150
+ if (!ontologyManager.findOntology(name)) {
151
+ ontologyManager.addOntology(name, version, source, {
152
+ textIndexing: { indexFields },
153
+ })
154
+ }
155
+ }
156
+
157
+ // TODO: add in any configured ontology prefixes that we don't already
158
+ // have in the session (or hardcoded in the model)
159
+ },
160
+ }))
161
+ .views((self) => ({
162
+ getBackendDriver(assemblyId: string) {
163
+ if (!assemblyId) {
164
+ return self.collaborationServerDriver
165
+ }
166
+ const session = getSession(self)
167
+ const { assemblyManager } = session
168
+ const assembly = assemblyManager.get(assemblyId)
169
+ if (!assembly) {
170
+ return self.collaborationServerDriver
171
+ }
172
+ const { file, internetAccountConfigId } = getConf(assembly, [
173
+ 'sequence',
174
+ 'metadata',
175
+ ]) as { internetAccountConfigId?: string; file: string }
176
+ if (isElectron && file) {
177
+ return self.desktopFileDriver
178
+ }
179
+ if (internetAccountConfigId) {
180
+ return self.collaborationServerDriver
181
+ }
182
+ return self.inMemoryFileDriver
183
+ },
184
+ getInternetAccount(assemblyName?: string, internetAccountId?: string) {
185
+ if (!(assemblyName ?? internetAccountId)) {
186
+ throw new Error(
187
+ 'Must provide either assemblyName or internetAccountId',
188
+ )
189
+ }
190
+ let configId = internetAccountId
191
+ if (assemblyName && !configId) {
192
+ const { assemblyManager } = getSession(self)
193
+ const assembly = assemblyManager.get(assemblyName)
194
+ if (!assembly) {
195
+ throw new Error(`No assembly found with name ${assemblyName}`)
196
+ }
197
+ ;({ internetAccountConfigId: configId } = getConf(assembly, [
198
+ 'sequence',
199
+ 'metadata',
200
+ ]) as { internetAccountConfigId: string })
201
+ }
202
+ const { internetAccounts } = self
203
+ const internetAccount = internetAccounts.find(
204
+ (ia) => ia.internetAccountId === configId,
205
+ ) as ApolloInternetAccount | undefined
206
+ if (!internetAccount) {
207
+ throw new Error(
208
+ `No InternetAccount found with config id ${internetAccountId}`,
209
+ )
210
+ }
211
+ return internetAccount
212
+ },
213
+ }))
214
+ .actions((self) => ({
215
+ loadFeatures: flow(function* loadFeatures(regions: Region[]) {
216
+ for (const region of regions) {
217
+ const backendDriver = self.getBackendDriver(region.assemblyName)
218
+ if (!backendDriver) {
219
+ return
220
+ }
221
+ const [features, checkResults] = (yield backendDriver.getFeatures(
222
+ region,
223
+ )) as [AnnotationFeatureSnapshot[], CheckResultSnapshot[]]
224
+ if (features.length === 0) {
225
+ continue
226
+ }
227
+ const { assemblyName, refName } = region
228
+ let assembly = self.assemblies.get(assemblyName)
229
+ if (!assembly) {
230
+ assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} })
231
+ }
232
+ const [firstFeature] = features
233
+ let ref = assembly.refSeqs.get(firstFeature.refSeq)
234
+ if (!ref) {
235
+ ref = assembly.refSeqs.put({
236
+ _id: firstFeature.refSeq,
237
+ name: refName,
238
+ features: {},
239
+ })
240
+ }
241
+ for (const feature of features) {
242
+ if (!ref.features.has(feature._id)) {
243
+ ref.features.put(feature)
244
+ }
245
+ }
246
+ self.addCheckResults(checkResults)
247
+ }
248
+ }),
249
+ loadRefSeq: flow(function* loadRefSeq(regions: Region[]) {
250
+ for (const region of regions) {
251
+ const backendDriver = self.getBackendDriver(region.assemblyName)
252
+ if (!backendDriver) {
253
+ return
254
+ }
255
+ const { refSeq, seq } = yield backendDriver.getSequence(region)
256
+ const { assemblyName, end, refName, start } = region
257
+ let assembly = self.assemblies.get(assemblyName)
258
+ if (!assembly) {
259
+ assembly = self.assemblies.put({ _id: assemblyName, refSeqs: {} })
260
+ }
261
+ let ref = assembly.refSeqs.get(refSeq)
262
+ if (!ref) {
263
+ ref = assembly.refSeqs.put({
264
+ _id: refSeq,
265
+ name: refName,
266
+ sequence: [],
267
+ })
268
+ }
269
+ ref.addSequence({ start, stop: end, sequence: seq })
270
+ }
271
+ }),
272
+ }))
273
+
274
+ // assembly and feature data isn't actually reloaded on reload unless we delete it from the snap
275
+ return types.snapshotProcessor(clientStoreType, {
276
+ postProcessor(snap: SnapshotOut<typeof clientStoreType>) {
277
+ snap.assemblies = {}
278
+ snap.checkResults = {}
279
+ return snap
280
+ },
281
+ })
282
+ }
@@ -0,0 +1 @@
1
+ export * from './session'
@@ -0,0 +1,373 @@
1
+ import { AssemblyModel } from '@jbrowse/core/assemblyManager/assembly'
2
+ import { getConf } from '@jbrowse/core/configuration'
3
+ import { BaseInternetAccountModel } from '@jbrowse/core/pluggableElementTypes'
4
+ import PluginManager from '@jbrowse/core/PluginManager'
5
+ import {
6
+ AbstractSessionModel,
7
+ SessionWithConfigEditing,
8
+ } from '@jbrowse/core/util'
9
+ import { LinearGenomeViewModel } from '@jbrowse/plugin-linear-genome-view'
10
+ import { ClientDataStore as ClientDataStoreType } from 'apollo-common'
11
+ import { AnnotationFeature, AnnotationFeatureI } from 'apollo-mst'
12
+ import { UserLocation } from 'apollo-shared'
13
+ import { autorun, observable } from 'mobx'
14
+ import { Instance, flow, getRoot, types } from 'mobx-state-tree'
15
+
16
+ import { ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
17
+ import { ApolloJobModel } from '../ApolloJobModel'
18
+ import { ChangeManager } from '../ChangeManager'
19
+ import { ApolloRootModel } from '../types'
20
+ import { createFetchErrorMessage } from '../util'
21
+ import { clientDataStoreFactory } from './ClientDataStore'
22
+
23
+ export interface ApolloSession extends AbstractSessionModel {
24
+ apolloDataStore: ClientDataStoreType & { changeManager: ChangeManager }
25
+ apolloSelectedFeature?: AnnotationFeatureI
26
+ apolloSetSelectedFeature(feature?: AnnotationFeatureI): void
27
+ }
28
+
29
+ interface ApolloAssemblyResponse {
30
+ _id: string
31
+ name: string
32
+ displayName?: string
33
+ description?: string
34
+ aliases?: string[]
35
+ }
36
+
37
+ export interface ApolloRefSeqResponse {
38
+ _id: string
39
+ name: string
40
+ description?: string
41
+ length: string
42
+ assembly: string
43
+ }
44
+
45
+ export interface Collaborator {
46
+ name: string
47
+ id: string
48
+ locations: UserLocation[]
49
+ }
50
+
51
+ export function extendSession(
52
+ pluginManager: PluginManager,
53
+ sessionModel: ReturnType<typeof types.model>,
54
+ ) {
55
+ const aborter = new AbortController()
56
+ const { signal } = aborter
57
+ const AnnotationFeatureExtended = pluginManager.evaluateExtensionPoint(
58
+ 'Apollo-extendAnnotationFeature',
59
+ AnnotationFeature,
60
+ ) as typeof AnnotationFeature
61
+ const ClientDataStore = clientDataStoreFactory(AnnotationFeatureExtended)
62
+ return sessionModel
63
+ .props({
64
+ apolloDataStore: types.optional(ClientDataStore, { typeName: 'Client' }),
65
+ apolloSelectedFeature: types.safeReference(AnnotationFeatureExtended),
66
+ jobsManager: types.optional(ApolloJobModel, {}),
67
+ })
68
+ .extend(() => {
69
+ const collabs = observable.array<Collaborator>([])
70
+
71
+ return {
72
+ views: {
73
+ get collaborators() {
74
+ return collabs
75
+ },
76
+ },
77
+ actions: {
78
+ addOrUpdateCollaborator(collaborator: Collaborator) {
79
+ const existingCollaborator = collabs.find(
80
+ (obj: Collaborator) => obj.id === collaborator.id,
81
+ )
82
+ if (existingCollaborator) {
83
+ existingCollaborator.locations = collaborator.locations
84
+ } else {
85
+ collabs.push(collaborator)
86
+ }
87
+ },
88
+ },
89
+ }
90
+ })
91
+ .actions((self) => ({
92
+ apolloSetSelectedFeature(feature?: AnnotationFeatureI) {
93
+ self.apolloSelectedFeature = feature
94
+ },
95
+ addApolloTrackConfig(assembly: AssemblyModel, baseURL?: string) {
96
+ const trackId = `apollo_track_${assembly.name}`
97
+ const hasTrack = (self as unknown as AbstractSessionModel).tracks.some(
98
+ (track) => track.trackId === trackId,
99
+ )
100
+ if (!hasTrack) {
101
+ ;(self as unknown as SessionWithConfigEditing).addTrackConf({
102
+ type: 'ApolloTrack',
103
+ trackId,
104
+ name: `Annotations (${
105
+ // @ts-expect-error getConf types don't quite work here for some reason
106
+ getConf(assembly, 'displayName') || assembly.name
107
+ })`,
108
+ assemblyNames: [assembly.name],
109
+ textSearching: {
110
+ textSearchAdapter: {
111
+ type: 'ApolloTextSearchAdapter',
112
+ trackId,
113
+ assemblyNames: [assembly.name],
114
+ textSearchAdapterId: `apollo_search_${assembly.name}`,
115
+ ...(baseURL
116
+ ? { baseURL: { uri: baseURL, locationType: 'UriLocation' } }
117
+ : {}),
118
+ },
119
+ },
120
+ displays: [
121
+ {
122
+ type: 'LinearApolloDisplay',
123
+ displayId: `${trackId}-LinearApolloDisplay`,
124
+ },
125
+ {
126
+ type: 'SixFrameFeatureDisplay',
127
+ displayId: `${trackId}-SixFrameFeatureDisplay`,
128
+ },
129
+ ],
130
+ })
131
+ }
132
+ },
133
+ broadcastLocations() {
134
+ const { internetAccounts } = getRoot<ApolloRootModel>(self)
135
+ const locations: {
136
+ assemblyName: string
137
+ refName: string
138
+ start: number
139
+ end: number
140
+ }[] = []
141
+ for (const view of (self as unknown as AbstractSessionModel).views) {
142
+ if (view.type !== 'LinearGenomeView') {
143
+ return
144
+ }
145
+ const lgv = view as unknown as LinearGenomeViewModel
146
+ if (lgv.initialized) {
147
+ const { dynamicBlocks } = lgv
148
+ // eslint-disable-next-line unicorn/no-array-for-each
149
+ dynamicBlocks.forEach((block) => {
150
+ if (block.regionNumber !== undefined) {
151
+ const { assemblyName, end, refName, start } = block
152
+ locations.push({ assemblyName, refName, start, end })
153
+ }
154
+ })
155
+ }
156
+ }
157
+ if (locations.length === 0) {
158
+ for (const internetAccount of internetAccounts as (
159
+ | BaseInternetAccountModel
160
+ | ApolloInternetAccountModel
161
+ )[]) {
162
+ if ('baseURL' in internetAccount) {
163
+ internetAccount.postUserLocation([])
164
+ }
165
+ }
166
+ return
167
+ }
168
+
169
+ const allLocations: UserLocation[] = []
170
+ for (const internetAccount of internetAccounts as (
171
+ | BaseInternetAccountModel
172
+ | ApolloInternetAccountModel
173
+ )[]) {
174
+ if ('baseURL' in internetAccount) {
175
+ for (const location of locations) {
176
+ const tmpLoc: UserLocation = {
177
+ assemblyId: location.assemblyName,
178
+ refSeq: location.refName,
179
+ start: location.start,
180
+ end: location.end,
181
+ }
182
+ allLocations.push(tmpLoc)
183
+ }
184
+ internetAccount.postUserLocation(allLocations)
185
+ }
186
+ }
187
+ },
188
+ }))
189
+ .actions((self) => ({
190
+ afterCreate: flow(function* afterCreate() {
191
+ const { internetAccounts } = getRoot<ApolloRootModel>(self)
192
+ autorun(
193
+ () => {
194
+ // broadcastLocations() // **** This is not working and therefore we need to duplicate broadcastLocations() -method code here because autorun() does not observe changes otherwise
195
+ const locations: {
196
+ assemblyName: string
197
+ refName: string
198
+ start: number
199
+ end: number
200
+ }[] = []
201
+ for (const view of (self as unknown as AbstractSessionModel)
202
+ .views) {
203
+ if (view.type !== 'LinearGenomeView') {
204
+ return
205
+ }
206
+ const lgv = view as unknown as LinearGenomeViewModel
207
+ if (lgv.initialized) {
208
+ const { dynamicBlocks } = lgv as LinearGenomeViewModel
209
+ // eslint-disable-next-line unicorn/no-array-for-each
210
+ dynamicBlocks.forEach((block) => {
211
+ if (block.regionNumber !== undefined) {
212
+ const { assemblyName, end, refName, start } = block
213
+ locations.push({ assemblyName, refName, start, end })
214
+ }
215
+ })
216
+ }
217
+ }
218
+ if (locations.length === 0) {
219
+ for (const internetAccount of internetAccounts as (
220
+ | BaseInternetAccountModel
221
+ | ApolloInternetAccountModel
222
+ )[]) {
223
+ if ('baseURL' in internetAccount) {
224
+ internetAccount.postUserLocation([])
225
+ }
226
+ }
227
+ return
228
+ }
229
+
230
+ const allLocations: UserLocation[] = []
231
+ for (const internetAccount of internetAccounts as (
232
+ | BaseInternetAccountModel
233
+ | ApolloInternetAccountModel
234
+ )[]) {
235
+ if ('baseURL' in internetAccount) {
236
+ for (const location of locations) {
237
+ const tmpLoc: UserLocation = {
238
+ assemblyId: location.assemblyName,
239
+ refSeq: location.refName,
240
+ start: location.start,
241
+ end: location.end,
242
+ }
243
+ allLocations.push(tmpLoc)
244
+ }
245
+ internetAccount.postUserLocation(allLocations)
246
+ }
247
+ }
248
+ },
249
+ { name: 'ApolloSession' },
250
+ )
251
+ // END AUTORUN
252
+
253
+ // fetch and initialize assemblies for each of our Apollo internet accounts
254
+ for (const internetAccount of internetAccounts as ApolloInternetAccountModel[]) {
255
+ if (internetAccount.type !== 'ApolloInternetAccount') {
256
+ continue
257
+ }
258
+
259
+ const { baseURL, configuration } = internetAccount
260
+ const uri = new URL('assemblies', baseURL).href
261
+ const fetch = internetAccount.getFetcher({
262
+ locationType: 'UriLocation',
263
+ uri,
264
+ })
265
+ let response: Response
266
+ try {
267
+ response = yield fetch(uri, { signal })
268
+ } catch (error) {
269
+ console.error(error)
270
+ // setError(e instanceof Error ? e : new Error(String(e)))
271
+ continue
272
+ }
273
+ if (!response.ok) {
274
+ const errorMessage = yield createFetchErrorMessage(
275
+ response,
276
+ 'Failed to fetch assemblies',
277
+ )
278
+ console.error(errorMessage)
279
+ continue
280
+ }
281
+ let fetchedAssemblies
282
+ try {
283
+ fetchedAssemblies =
284
+ (yield response.json()) as ApolloAssemblyResponse[]
285
+ } catch (error) {
286
+ console.error(error)
287
+ continue
288
+ }
289
+ for (const assembly of fetchedAssemblies) {
290
+ const { addAssembly, addSessionAssembly, assemblyManager } =
291
+ self as unknown as AbstractSessionModel & {
292
+ // eslint-disable-next-line @typescript-eslint/ban-types
293
+ addSessionAssembly: Function
294
+ }
295
+ const selectedAssembly = assemblyManager.get(assembly.name)
296
+ if (selectedAssembly) {
297
+ // @ts-expect-error MST type coercion problem?
298
+ self.addApolloTrackConfig(selectedAssembly, baseURL)
299
+ continue
300
+ }
301
+ const url = new URL('refSeqs', baseURL)
302
+ const searchParams = new URLSearchParams({ assembly: assembly._id })
303
+ url.search = searchParams.toString()
304
+ const uri2 = url.toString()
305
+ const fetch2 = internetAccount.getFetcher({
306
+ locationType: 'UriLocation',
307
+ uri: uri2,
308
+ })
309
+ const response2 = (yield fetch2(uri2, {
310
+ signal,
311
+ })) as unknown as Response
312
+ if (!response2.ok) {
313
+ let errorMessage
314
+ try {
315
+ errorMessage = yield response2.text()
316
+ } catch {
317
+ errorMessage = ''
318
+ }
319
+ throw new Error(
320
+ `Failed to fetch fasta info — ${response2.status} (${
321
+ response2.statusText
322
+ })${errorMessage ? ` (${errorMessage})` : ''}`,
323
+ )
324
+ }
325
+ const f = (yield response2.json()) as ApolloRefSeqResponse[]
326
+ const ids: Record<string, string> = {}
327
+ const refNameAliasesFeatures = f.map((contig) => {
328
+ ids[contig.name] = contig._id
329
+ return {
330
+ refName: contig.name,
331
+ aliases: [contig._id],
332
+ uniqueId: `alias-${contig._id}`,
333
+ }
334
+ })
335
+ const assemblyConfig = {
336
+ name: assembly._id,
337
+ aliases: [assembly.name, ...(assembly.aliases ?? [])],
338
+ displayName: assembly.displayName ?? assembly.name,
339
+ sequence: {
340
+ trackId: `sequenceConfigId-${assembly.name}`,
341
+ type: 'ReferenceSequenceTrack',
342
+ adapter: {
343
+ type: 'ApolloSequenceAdapter',
344
+ assemblyId: assembly._id,
345
+ baseURL: { uri: baseURL, locationType: 'UriLocation' },
346
+ },
347
+ metadata: {
348
+ apollo: true,
349
+ internetAccountConfigId: configuration.internetAccountId,
350
+ ids,
351
+ },
352
+ },
353
+ refNameAliases: {
354
+ adapter: {
355
+ type: 'FromConfigAdapter',
356
+ features: refNameAliasesFeatures,
357
+ },
358
+ },
359
+ }
360
+ ;(addSessionAssembly || addAssembly)(assemblyConfig)
361
+ const a = yield assemblyManager.waitForAssembly(assemblyConfig.name)
362
+ self.addApolloTrackConfig(a, baseURL)
363
+ }
364
+ }
365
+ }),
366
+ beforeDestroy() {
367
+ aborter.abort()
368
+ },
369
+ }))
370
+ }
371
+
372
+ export type ApolloSessionStateModel = ReturnType<typeof extendSession>
373
+ export type ApolloSessionModel = Instance<ApolloSessionStateModel>
package/src/types.ts ADDED
@@ -0,0 +1,10 @@
1
+ import { BaseInternetAccountModel } from '@jbrowse/core/pluggableElementTypes'
2
+ import { AppRootModel } from '@jbrowse/core/util'
3
+
4
+ import { ApolloInternetAccountModel } from './ApolloInternetAccount/model'
5
+ import { ApolloSessionModel } from './session'
6
+
7
+ export interface ApolloRootModel extends Omit<AppRootModel, 'session'> {
8
+ session: ApolloSessionModel
9
+ internetAccounts: (BaseInternetAccountModel | ApolloInternetAccountModel)[]
10
+ }
@@ -0,0 +1,31 @@
1
+ import { getParent } from 'mobx-state-tree'
2
+
3
+ import { ApolloInternetAccountModel } from '../ApolloInternetAccount/model'
4
+ import { ApolloSessionModel } from '../session'
5
+ import { ApolloRootModel } from '../types'
6
+
7
+ export async function createFetchErrorMessage(
8
+ response: Response,
9
+ additionalText?: string,
10
+ ): Promise<string> {
11
+ let errorMessage
12
+ try {
13
+ errorMessage = await response.text()
14
+ } catch {
15
+ errorMessage = ''
16
+ }
17
+ const responseMessage = `${response.status} ${response.statusText}${
18
+ errorMessage ? ` (${errorMessage})` : ''
19
+ }`
20
+ return `${additionalText ? `${additionalText} — ` : ''}${responseMessage}`
21
+ }
22
+
23
+ /** given a session, get our ApolloInternetAccount */
24
+ export function getApolloInternetAccount(session: ApolloSessionModel) {
25
+ const { internetAccounts } = getParent<ApolloRootModel>(session)
26
+ return internetAccounts.find((ia) => ia.type === 'ApolloInternetAccount') as
27
+ | ApolloInternetAccountModel
28
+ | undefined
29
+ }
30
+
31
+ export * from './loadAssemblyIntoClient'