@apollo-annotation/shared 0.3.5 → 0.3.7

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 (127) hide show
  1. package/dist/Changes/AddAssemblyAliasesChange.d.ts +16 -0
  2. package/dist/Changes/AddAssemblyAliasesChange.js +50 -0
  3. package/dist/Changes/AddAssemblyAliasesChange.js.map +1 -0
  4. package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.d.ts +1 -1
  5. package/dist/Changes/AddAssemblyFromExternalChange.d.ts +1 -1
  6. package/dist/Changes/AddAssemblyFromFileChange.d.ts +1 -1
  7. package/dist/Changes/AddFeatureChange.d.ts +3 -2
  8. package/dist/Changes/AddFeatureChange.js +5 -25
  9. package/dist/Changes/AddFeatureChange.js.map +1 -1
  10. package/dist/Changes/AddFeaturesFromFileChange.d.ts +1 -1
  11. package/dist/Changes/AddRefSeqAliasesChange.d.ts +1 -1
  12. package/dist/Changes/DeleteAssemblyChange.d.ts +1 -1
  13. package/dist/Changes/DeleteFeatureChange.d.ts +12 -11
  14. package/dist/Changes/DeleteFeatureChange.js +37 -32
  15. package/dist/Changes/DeleteFeatureChange.js.map +1 -1
  16. package/dist/Changes/DeleteUserChange.d.ts +1 -1
  17. package/dist/Changes/FeatureAttributeChange.d.ts +3 -2
  18. package/dist/Changes/FeatureAttributeChange.js +14 -6
  19. package/dist/Changes/FeatureAttributeChange.js.map +1 -1
  20. package/dist/Changes/FromFileBaseChange.d.ts +3 -3
  21. package/dist/Changes/FromFileBaseChange.js.map +1 -1
  22. package/dist/Changes/ImportJBrowseConfigChange.d.ts +4 -1
  23. package/dist/Changes/ImportJBrowseConfigChange.js +17 -13
  24. package/dist/Changes/ImportJBrowseConfigChange.js.map +1 -1
  25. package/dist/Changes/LocationEndChange.d.ts +1 -1
  26. package/dist/Changes/LocationEndChange.js +40 -37
  27. package/dist/Changes/LocationEndChange.js.map +1 -1
  28. package/dist/Changes/LocationStartChange.d.ts +1 -1
  29. package/dist/Changes/LocationStartChange.js +40 -37
  30. package/dist/Changes/LocationStartChange.js.map +1 -1
  31. package/dist/Changes/MergeExonsChange.d.ts +30 -0
  32. package/dist/Changes/MergeExonsChange.js +130 -0
  33. package/dist/Changes/MergeExonsChange.js.map +1 -0
  34. package/dist/Changes/MergeTranscriptsChange.d.ts +36 -0
  35. package/dist/Changes/MergeTranscriptsChange.js +246 -0
  36. package/dist/Changes/MergeTranscriptsChange.js.map +1 -0
  37. package/dist/Changes/SplitExonChange.d.ts +33 -0
  38. package/dist/Changes/SplitExonChange.js +142 -0
  39. package/dist/Changes/SplitExonChange.js.map +1 -0
  40. package/dist/Changes/StrandChange.d.ts +1 -1
  41. package/dist/Changes/TypeChange.d.ts +1 -1
  42. package/dist/Changes/UndoMergeExonsChange.d.ts +27 -0
  43. package/dist/Changes/UndoMergeExonsChange.js +104 -0
  44. package/dist/Changes/UndoMergeExonsChange.js.map +1 -0
  45. package/dist/Changes/UndoMergeTranscriptsChange.d.ts +27 -0
  46. package/dist/Changes/UndoMergeTranscriptsChange.js +104 -0
  47. package/dist/Changes/UndoMergeTranscriptsChange.js.map +1 -0
  48. package/dist/Changes/UndoSplitExonChange.d.ts +32 -0
  49. package/dist/Changes/UndoSplitExonChange.js +111 -0
  50. package/dist/Changes/UndoSplitExonChange.js.map +1 -0
  51. package/dist/Changes/UserChange.d.ts +1 -1
  52. package/dist/Changes/index.d.ts +21 -0
  53. package/dist/Changes/index.js +21 -0
  54. package/dist/Changes/index.js.map +1 -1
  55. package/dist/Checks/CDSCheck.d.ts +1 -1
  56. package/dist/GFF3/annotationFeatureToGFF3.d.ts +2 -2
  57. package/dist/GFF3/annotationFeatureToGFF3.js +7 -1
  58. package/dist/GFF3/annotationFeatureToGFF3.js.map +1 -1
  59. package/dist/GFF3/annotationFeatureToGFF3.test.js +1 -1
  60. package/dist/GFF3/annotationFeatureToGFF3.test.js.map +1 -1
  61. package/dist/GFF3/gff3ToAnnotationFeature.d.ts +2 -2
  62. package/dist/GFF3/gff3ToAnnotationFeature.test.d.ts +1 -1
  63. package/dist/GFF3/gff3ToAnnotationFeature.test.js +1 -1
  64. package/dist/GFF3/gff3ToAnnotationFeature.test.js.map +1 -1
  65. package/dist/GFF3/gffReservedKeys.d.ts +7 -0
  66. package/dist/GFF3/gffReservedKeys.js +21 -1
  67. package/dist/GFF3/gffReservedKeys.js.map +1 -1
  68. package/dist/Messages.d.ts +2 -2
  69. package/dist/Operations/GetAssembliesOperation.d.ts +1 -1
  70. package/dist/Operations/GetFeaturesOperation.d.ts +1 -1
  71. package/dist/Validations/CoreValidation.d.ts +2 -2
  72. package/dist/Validations/CoreValidation.js +1 -1
  73. package/dist/Validations/CoreValidation.js.map +1 -1
  74. package/dist/Validations/ParentChildValidation.d.ts +5 -5
  75. package/dist/Validations/ParentChildValidation.js +3 -1
  76. package/dist/Validations/ParentChildValidation.js.map +1 -1
  77. package/dist/Validations/Validation.d.ts +5 -5
  78. package/dist/Validations/ValidationSet.d.ts +4 -4
  79. package/dist/Validations/ValidationSet.js.map +1 -1
  80. package/dist/tsconfig.tsbuildinfo +1 -1
  81. package/dist/util.d.ts +2 -0
  82. package/dist/util.js +13 -0
  83. package/dist/util.js.map +1 -1
  84. package/package.json +4 -4
  85. package/src/Changes/AddAssemblyAliasesChange.ts +69 -0
  86. package/src/Changes/AddAssemblyAndFeaturesFromFileChange.ts +6 -6
  87. package/src/Changes/AddAssemblyFromExternalChange.ts +5 -5
  88. package/src/Changes/AddAssemblyFromFileChange.ts +5 -5
  89. package/src/Changes/AddFeatureChange.ts +11 -35
  90. package/src/Changes/AddFeaturesFromFileChange.ts +6 -6
  91. package/src/Changes/AddRefSeqAliasesChange.ts +6 -6
  92. package/src/Changes/DeleteAssemblyChange.ts +4 -4
  93. package/src/Changes/DeleteFeatureChange.ts +49 -43
  94. package/src/Changes/DeleteUserChange.ts +5 -5
  95. package/src/Changes/FeatureAttributeChange.ts +22 -13
  96. package/src/Changes/FromFileBaseChange.ts +6 -3
  97. package/src/Changes/ImportJBrowseConfigChange.ts +37 -22
  98. package/src/Changes/LocationEndChange.ts +47 -56
  99. package/src/Changes/LocationStartChange.ts +47 -56
  100. package/src/Changes/MergeExonsChange.ts +185 -0
  101. package/src/Changes/MergeTranscriptsChange.ts +379 -0
  102. package/src/Changes/SplitExonChange.ts +249 -0
  103. package/src/Changes/StrandChange.ts +6 -6
  104. package/src/Changes/TypeChange.ts +6 -6
  105. package/src/Changes/UndoMergeExonsChange.ts +149 -0
  106. package/src/Changes/UndoMergeTranscriptsChange.ts +153 -0
  107. package/src/Changes/UndoSplitExonChange.ts +174 -0
  108. package/src/Changes/UserChange.ts +5 -5
  109. package/src/Changes/index.ts +21 -0
  110. package/src/Checks/CDSCheck.ts +2 -2
  111. package/src/GFF3/annotationFeatureToGFF3.test.ts +4 -2
  112. package/src/GFF3/annotationFeatureToGFF3.ts +11 -5
  113. package/src/GFF3/gff3ToAnnotationFeature.test.ts +3 -3
  114. package/src/GFF3/gff3ToAnnotationFeature.ts +2 -2
  115. package/src/GFF3/gffReservedKeys.ts +25 -0
  116. package/src/Messages.ts +2 -2
  117. package/src/Operations/GetAssembliesOperation.ts +3 -3
  118. package/src/Operations/GetFeaturesOperation.ts +4 -4
  119. package/src/Validations/CoreValidation.ts +4 -3
  120. package/src/Validations/ParentChildValidation.ts +10 -8
  121. package/src/Validations/Validation.ts +5 -5
  122. package/src/Validations/ValidationSet.ts +8 -4
  123. package/src/util.ts +16 -0
  124. package/test_data/example01.json +3 -3
  125. package/test_data/example02.json +3 -3
  126. package/test_data/example04.json +3 -3
  127. package/test_data/gene_representations.gff3 +686 -1686
@@ -2,14 +2,14 @@
2
2
  /* eslint-disable @typescript-eslint/require-await */
3
3
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
4
4
  import {
5
- ChangeOptions,
6
- ClientDataStore,
5
+ type ChangeOptions,
6
+ type ClientDataStore,
7
7
  FeatureChange,
8
- LocalGFF3DataStore,
9
- SerializedFeatureChange,
10
- ServerDataStore,
8
+ type LocalGFF3DataStore,
9
+ type SerializedFeatureChange,
10
+ type ServerDataStore,
11
11
  } from '@apollo-annotation/common'
12
- import { Feature, FeatureDocument } from '@apollo-annotation/schemas'
12
+ import { type Feature, type FeatureDocument } from '@apollo-annotation/schemas'
13
13
 
14
14
  interface SerializedLocationEndChangeBase extends SerializedFeatureChange {
15
15
  typeName: 'LocationEndChange'
@@ -60,19 +60,32 @@ export class LocationEndChange extends FeatureChange {
60
60
  async executeOnServer(backend: ServerDataStore) {
61
61
  const { featureModel, session } = backend
62
62
  const { changes, logger } = this
63
- const featuresForChanges: {
64
- feature: Feature
65
- topLevelFeature: FeatureDocument
66
- }[] = []
67
63
  // Let's first check that all features are found and those old values match with expected ones. We do this just to be sure that all changes can be done.
64
+ const topLevelFeatures: FeatureDocument[] = []
68
65
  for (const change of changes) {
69
- const { featureId, oldEnd } = change
70
-
71
- // Search correct feature
72
- const topLevelFeature = await featureModel
73
- .findOne({ allIds: featureId })
74
- .session(session)
75
- .exec()
66
+ const { featureId, oldEnd, newEnd } = change
67
+
68
+ // See if we already have top-level feature for this feature
69
+ let topLevelFeature: FeatureDocument | undefined | null
70
+ let feature: Feature | undefined | null
71
+ for (const tlv of topLevelFeatures) {
72
+ const childFeature = this.getFeatureFromId(tlv, featureId)
73
+ if (childFeature) {
74
+ topLevelFeature = tlv
75
+ feature = childFeature
76
+ break
77
+ }
78
+ }
79
+ if (!topLevelFeature) {
80
+ // Don't already have top-level feature, so let's query it
81
+ topLevelFeature = await featureModel
82
+ .findOne({ allIds: featureId })
83
+ .session(session)
84
+ .exec()
85
+ if (topLevelFeature) {
86
+ topLevelFeatures.push(topLevelFeature)
87
+ }
88
+ }
76
89
 
77
90
  if (!topLevelFeature) {
78
91
  const errMsg = `*** ERROR: The following featureId was not found in database ='${featureId}'`
@@ -84,57 +97,34 @@ export class LocationEndChange extends FeatureChange {
84
97
  `*** TOP level feature found: ${JSON.stringify(topLevelFeature)}`,
85
98
  )
86
99
 
87
- const foundFeature = this.getFeatureFromId(topLevelFeature, featureId)
88
- if (!foundFeature) {
100
+ if (!feature) {
101
+ feature = this.getFeatureFromId(topLevelFeature, featureId)
102
+ }
103
+ if (!feature) {
89
104
  const errMsg = 'ERROR when searching feature by featureId'
90
105
  logger.error(errMsg)
91
106
  throw new Error(errMsg)
92
107
  }
93
- logger.debug?.(`*** Found feature: ${JSON.stringify(foundFeature)}`)
94
- if (foundFeature.max === oldEnd) {
95
- featuresForChanges.push({ feature: foundFeature, topLevelFeature })
96
- } else {
97
- if (foundFeature.children) {
98
- for (const [, childFeature] of foundFeature.children) {
99
- if (childFeature.max === oldEnd) {
100
- logger.debug?.(
101
- `*************** UPDATE CHILD FEATURE ID= ${featureId}, CHILD: ${JSON.stringify(
102
- childFeature,
103
- )}`,
104
- )
105
- featuresForChanges.push({
106
- feature: childFeature,
107
- topLevelFeature,
108
- })
109
- break
110
- }
111
- }
112
- }
108
+ logger.debug?.(`*** Found feature: ${JSON.stringify(feature)}`)
109
+ if (feature.max !== oldEnd) {
110
+ const errMsg = 'Expected previous max does not match'
111
+ logger.error(errMsg)
112
+ throw new Error(errMsg)
113
113
  }
114
- }
115
-
116
- // Let's update objects.
117
- for (const [idx, change] of changes.entries()) {
118
- const { newEnd } = change
119
- const { feature, topLevelFeature } = featuresForChanges[idx]
120
114
  feature.max = newEnd
121
115
  if (topLevelFeature._id.equals(feature._id)) {
122
116
  topLevelFeature.markModified('end') // Mark as modified. Without this save() -method is not updating data in database
123
117
  } else {
124
118
  topLevelFeature.markModified('children') // Mark as modified. Without this save() -method is not updating data in database
125
119
  }
126
-
120
+ }
121
+ for (const tlv of topLevelFeatures) {
127
122
  try {
128
- await topLevelFeature.save()
123
+ await tlv.save()
129
124
  } catch (error) {
130
125
  logger.debug?.(`*** FAILED: ${error}`)
131
126
  throw error
132
127
  }
133
- logger.debug?.(
134
- `*** Object updated in Mongo. New object: ${JSON.stringify(
135
- topLevelFeature,
136
- )}`,
137
- )
138
128
  }
139
129
  }
140
130
 
@@ -146,12 +136,13 @@ export class LocationEndChange extends FeatureChange {
146
136
  if (!dataStore) {
147
137
  throw new Error('No data store')
148
138
  }
149
- for (const [idx, changedId] of this.changedIds.entries()) {
150
- const feature = dataStore.getFeature(changedId)
139
+ for (const change of this.changes) {
140
+ const { featureId, newEnd } = change
141
+ const feature = dataStore.getFeature(featureId)
151
142
  if (!feature) {
152
- throw new Error(`Could not find feature with identifier "${changedId}"`)
143
+ throw new Error(`Could not find feature with identifier "${featureId}"`)
153
144
  }
154
- feature.setMax(this.changes[idx].newEnd)
145
+ feature.setMax(newEnd)
155
146
  }
156
147
  }
157
148
 
@@ -2,14 +2,14 @@
2
2
  /* eslint-disable @typescript-eslint/require-await */
3
3
  /* eslint-disable @typescript-eslint/no-unnecessary-condition */
4
4
  import {
5
- ChangeOptions,
6
- ClientDataStore,
5
+ type ChangeOptions,
6
+ type ClientDataStore,
7
7
  FeatureChange,
8
- LocalGFF3DataStore,
9
- SerializedFeatureChange,
10
- ServerDataStore,
8
+ type LocalGFF3DataStore,
9
+ type SerializedFeatureChange,
10
+ type ServerDataStore,
11
11
  } from '@apollo-annotation/common'
12
- import { Feature, FeatureDocument } from '@apollo-annotation/schemas'
12
+ import { type Feature, type FeatureDocument } from '@apollo-annotation/schemas'
13
13
 
14
14
  interface SerializedLocationStartChangeBase extends SerializedFeatureChange {
15
15
  typeName: 'LocationStartChange'
@@ -60,19 +60,32 @@ export class LocationStartChange extends FeatureChange {
60
60
  async executeOnServer(backend: ServerDataStore) {
61
61
  const { featureModel, session } = backend
62
62
  const { changes, logger } = this
63
- const featuresForChanges: {
64
- feature: Feature
65
- topLevelFeature: FeatureDocument
66
- }[] = []
67
63
  // Let's first check that all features are found and those old values match with expected ones. We do this just to be sure that all changes can be done.
64
+ const topLevelFeatures: FeatureDocument[] = []
68
65
  for (const change of changes) {
69
- const { featureId, oldStart } = change
70
-
71
- // Search correct feature
72
- const topLevelFeature = await featureModel
73
- .findOne({ allIds: featureId })
74
- .session(session)
75
- .exec()
66
+ const { featureId, oldStart, newStart } = change
67
+
68
+ // See if we already have top-level feature for this feature
69
+ let topLevelFeature: FeatureDocument | undefined | null
70
+ let feature: Feature | undefined | null
71
+ for (const tlv of topLevelFeatures) {
72
+ const childFeature = this.getFeatureFromId(tlv, featureId)
73
+ if (childFeature) {
74
+ topLevelFeature = tlv
75
+ feature = childFeature
76
+ break
77
+ }
78
+ }
79
+ if (!topLevelFeature) {
80
+ // Don't already have top-level feature, so let's query it
81
+ topLevelFeature = await featureModel
82
+ .findOne({ allIds: featureId })
83
+ .session(session)
84
+ .exec()
85
+ if (topLevelFeature) {
86
+ topLevelFeatures.push(topLevelFeature)
87
+ }
88
+ }
76
89
 
77
90
  if (!topLevelFeature) {
78
91
  const errMsg = `*** ERROR: The following featureId was not found in database ='${featureId}'`
@@ -84,57 +97,34 @@ export class LocationStartChange extends FeatureChange {
84
97
  `*** TOP level feature found: ${JSON.stringify(topLevelFeature)}`,
85
98
  )
86
99
 
87
- const foundFeature = this.getFeatureFromId(topLevelFeature, featureId)
88
- if (!foundFeature) {
100
+ if (!feature) {
101
+ feature = this.getFeatureFromId(topLevelFeature, featureId)
102
+ }
103
+ if (!feature) {
89
104
  const errMsg = 'ERROR when searching feature by featureId'
90
105
  logger.error(errMsg)
91
106
  throw new Error(errMsg)
92
107
  }
93
- logger.debug?.(`*** Found feature: ${JSON.stringify(foundFeature)}`)
94
- if (foundFeature.min === oldStart) {
95
- featuresForChanges.push({ feature: foundFeature, topLevelFeature })
96
- } else {
97
- if (foundFeature.children) {
98
- for (const [, childFeature] of foundFeature.children) {
99
- if (childFeature.min === oldStart) {
100
- logger.debug?.(
101
- `*** UPDATE CHILD FEATURE ID= ${featureId}, CHILD: ${JSON.stringify(
102
- childFeature,
103
- )}`,
104
- )
105
- featuresForChanges.push({
106
- feature: childFeature,
107
- topLevelFeature,
108
- })
109
- break
110
- }
111
- }
112
- }
108
+ logger.debug?.(`*** Found feature: ${JSON.stringify(feature)}`)
109
+ if (feature.min !== oldStart) {
110
+ const errMsg = 'Expected previous max does not match'
111
+ logger.error(errMsg)
112
+ throw new Error(errMsg)
113
113
  }
114
- }
115
-
116
- // Let's update objects.
117
- for (const [idx, change] of changes.entries()) {
118
- const { newStart } = change
119
- const { feature, topLevelFeature } = featuresForChanges[idx]
120
114
  feature.min = newStart
121
115
  if (topLevelFeature._id.equals(feature._id)) {
122
116
  topLevelFeature.markModified('start') // Mark as modified. Without this save() -method is not updating data in database
123
117
  } else {
124
118
  topLevelFeature.markModified('children') // Mark as modified. Without this save() -method is not updating data in database
125
119
  }
126
-
120
+ }
121
+ for (const tlv of topLevelFeatures) {
127
122
  try {
128
- await topLevelFeature.save()
123
+ await tlv.save()
129
124
  } catch (error) {
130
125
  logger.debug?.(`*** FAILED: ${error}`)
131
126
  throw error
132
127
  }
133
- logger.debug?.(
134
- `*** Object updated in Mongo. New object: ${JSON.stringify(
135
- topLevelFeature,
136
- )}`,
137
- )
138
128
  }
139
129
  }
140
130
 
@@ -146,12 +136,13 @@ export class LocationStartChange extends FeatureChange {
146
136
  if (!dataStore) {
147
137
  throw new Error('No data store')
148
138
  }
149
- for (const [idx, changedId] of this.changedIds.entries()) {
150
- const feature = dataStore.getFeature(changedId)
139
+ for (const change of this.changes) {
140
+ const { featureId, newStart } = change
141
+ const feature = dataStore.getFeature(featureId)
151
142
  if (!feature) {
152
- throw new Error(`Could not find feature with identifier "${changedId}"`)
143
+ throw new Error(`Could not find feature with identifier "${featureId}"`)
153
144
  }
154
- feature.setMin(this.changes[idx].newStart)
145
+ feature.setMin(newStart)
155
146
  }
156
147
  }
157
148
 
@@ -0,0 +1,185 @@
1
+ /* eslint-disable @typescript-eslint/require-await */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
3
+
4
+ import {
5
+ type ChangeOptions,
6
+ type ClientDataStore,
7
+ FeatureChange,
8
+ type LocalGFF3DataStore,
9
+ type SerializedFeatureChange,
10
+ type ServerDataStore,
11
+ } from '@apollo-annotation/common'
12
+ import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst'
13
+
14
+ import { findAndDeleteChildFeature } from './DeleteFeatureChange'
15
+ import { UndoMergeExonsChange } from './UndoMergeExonsChange'
16
+
17
+ interface SerializedMergeExonsChangeBase extends SerializedFeatureChange {
18
+ typeName: 'MergeExonsChange'
19
+ }
20
+
21
+ export interface MergeExonsChangeDetails {
22
+ firstExon: AnnotationFeatureSnapshot
23
+ secondExon: AnnotationFeatureSnapshot
24
+ parentFeatureId: string
25
+ }
26
+
27
+ interface SerializedMergeExonsChangeSingle
28
+ extends SerializedMergeExonsChangeBase,
29
+ MergeExonsChangeDetails {}
30
+
31
+ interface SerializedMergeExonsChangeMultiple
32
+ extends SerializedMergeExonsChangeBase {
33
+ changes: MergeExonsChangeDetails[]
34
+ }
35
+
36
+ export type SerializedMergeExonsChange =
37
+ | SerializedMergeExonsChangeSingle
38
+ | SerializedMergeExonsChangeMultiple
39
+
40
+ export class MergeExonsChange extends FeatureChange {
41
+ typeName = 'MergeExonsChange' as const
42
+ changes: MergeExonsChangeDetails[]
43
+
44
+ constructor(json: SerializedMergeExonsChange, options?: ChangeOptions) {
45
+ super(json, options)
46
+ this.changes = 'changes' in json ? json.changes : [json]
47
+ }
48
+ // eslint-disable-next-line @typescript-eslint/class-literal-property-style
49
+ get notification() {
50
+ return 'Exons successfully merged'
51
+ }
52
+
53
+ toJSON(): SerializedMergeExonsChange {
54
+ const { assembly, changedIds, changes, typeName } = this
55
+ if (changes.length === 1) {
56
+ const [{ firstExon, secondExon, parentFeatureId }] = changes
57
+
58
+ return {
59
+ typeName,
60
+ changedIds,
61
+ assembly,
62
+ firstExon,
63
+ secondExon,
64
+ parentFeatureId,
65
+ }
66
+ }
67
+ return { typeName, changedIds, assembly, changes }
68
+ }
69
+
70
+ async executeOnServer(backend: ServerDataStore) {
71
+ const { featureModel, session } = backend
72
+ const { changes, logger } = this
73
+ for (const change of changes) {
74
+ const { firstExon, secondExon } = change
75
+ const topLevelFeature = await featureModel
76
+ .findOne({ allIds: firstExon._id })
77
+ .session(session)
78
+ .exec()
79
+ if (!topLevelFeature) {
80
+ const errMsg = `*** ERROR: The following featureId was not found in database ='${firstExon._id}'`
81
+ logger.error(errMsg)
82
+ throw new Error(errMsg)
83
+ }
84
+ const mergedExon = this.getFeatureFromId(topLevelFeature, firstExon._id)
85
+ if (!mergedExon) {
86
+ const errMsg = 'ERROR when searching feature by featureId'
87
+ logger.error(errMsg)
88
+ throw new Error(errMsg)
89
+ }
90
+ mergedExon.min = Math.min(firstExon.min, secondExon.min)
91
+ mergedExon.max = Math.max(firstExon.max, secondExon.max)
92
+ mergedExon.attributes = this.mergeAttributes(firstExon, secondExon)
93
+ const deletedIds = findAndDeleteChildFeature(
94
+ topLevelFeature,
95
+ secondExon._id,
96
+ this,
97
+ )
98
+ deletedIds.push(secondExon._id)
99
+ topLevelFeature.allIds = topLevelFeature.allIds.filter(
100
+ (id) => !deletedIds.includes(id),
101
+ )
102
+ await topLevelFeature.save()
103
+ }
104
+ }
105
+
106
+ async executeOnLocalGFF3(_backend: LocalGFF3DataStore) {
107
+ throw new Error('executeOnLocalGFF3 not implemented')
108
+ }
109
+
110
+ async executeOnClient(dataStore: ClientDataStore) {
111
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
112
+ if (!dataStore) {
113
+ throw new Error('No data store')
114
+ }
115
+
116
+ for (const change of this.changes) {
117
+ const { firstExon, secondExon } = change
118
+ const mergedExon = dataStore.getFeature(firstExon._id)
119
+ if (!mergedExon) {
120
+ throw new Error(
121
+ `Could not find feature with identifier "${firstExon._id}"`,
122
+ )
123
+ }
124
+ mergedExon.setMin(Math.min(firstExon.min, secondExon.min))
125
+ mergedExon.setMax(Math.max(firstExon.max, secondExon.max))
126
+ const mergedAttrs = this.mergeAttributes(firstExon, secondExon)
127
+ mergedExon.setAttributes(new Map())
128
+ for (const key of Object.keys(mergedAttrs)) {
129
+ mergedExon.setAttribute(key, mergedAttrs[key])
130
+ }
131
+ mergedExon.parent?.deleteChild(secondExon._id)
132
+ }
133
+ }
134
+
135
+ getInverse() {
136
+ const { assembly, changedIds, changes, logger } = this
137
+ const inverseChangedIds = [...changedIds].reverse()
138
+ const inverseChanges = [...changes].reverse().map((mergeExonChange) => ({
139
+ exonsToRestore: [mergeExonChange.firstExon, mergeExonChange.secondExon],
140
+ parentFeatureId: mergeExonChange.parentFeatureId,
141
+ }))
142
+ logger.debug?.(`INVERSE CHANGE '${JSON.stringify(inverseChanges)}'`)
143
+ return new UndoMergeExonsChange(
144
+ {
145
+ changedIds: inverseChangedIds,
146
+ typeName: 'UndoMergeExonsChange',
147
+ changes: inverseChanges,
148
+ assembly,
149
+ },
150
+ { logger },
151
+ )
152
+ }
153
+
154
+ mergeAttributes(
155
+ firstExon: AnnotationFeatureSnapshot,
156
+ secondExon: AnnotationFeatureSnapshot,
157
+ ): Record<string, string[]> {
158
+ let mergedAttrs: Record<string, string[]> = {}
159
+ if (firstExon.attributes) {
160
+ // eslint-disable-next-line unicorn/prefer-structured-clone
161
+ mergedAttrs = JSON.parse(JSON.stringify(firstExon.attributes))
162
+ }
163
+
164
+ if (secondExon.attributes) {
165
+ // eslint-disable-next-line unicorn/prefer-structured-clone
166
+ const attrs: Record<string, string[]> = JSON.parse(
167
+ JSON.stringify(secondExon.attributes),
168
+ )
169
+ for (const key of Object.keys(attrs)) {
170
+ if (key === '_id' || key === 'gff_id') {
171
+ continue
172
+ }
173
+ if (!Object.keys(mergedAttrs).includes(key)) {
174
+ mergedAttrs[key] = []
175
+ }
176
+ attrs[key].map((x) => {
177
+ if (!mergedAttrs[key].includes(x)) {
178
+ mergedAttrs[key].push(x)
179
+ }
180
+ })
181
+ }
182
+ }
183
+ return mergedAttrs
184
+ }
185
+ }