@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.
- package/dist/Changes/AddAssemblyAliasesChange.d.ts +16 -0
- package/dist/Changes/AddAssemblyAliasesChange.js +50 -0
- package/dist/Changes/AddAssemblyAliasesChange.js.map +1 -0
- package/dist/Changes/AddAssemblyAndFeaturesFromFileChange.d.ts +1 -1
- package/dist/Changes/AddAssemblyFromExternalChange.d.ts +1 -1
- package/dist/Changes/AddAssemblyFromFileChange.d.ts +1 -1
- package/dist/Changes/AddFeatureChange.d.ts +3 -2
- package/dist/Changes/AddFeatureChange.js +5 -25
- package/dist/Changes/AddFeatureChange.js.map +1 -1
- package/dist/Changes/AddFeaturesFromFileChange.d.ts +1 -1
- package/dist/Changes/AddRefSeqAliasesChange.d.ts +1 -1
- package/dist/Changes/DeleteAssemblyChange.d.ts +1 -1
- package/dist/Changes/DeleteFeatureChange.d.ts +12 -11
- package/dist/Changes/DeleteFeatureChange.js +37 -32
- package/dist/Changes/DeleteFeatureChange.js.map +1 -1
- package/dist/Changes/DeleteUserChange.d.ts +1 -1
- package/dist/Changes/FeatureAttributeChange.d.ts +3 -2
- package/dist/Changes/FeatureAttributeChange.js +14 -6
- package/dist/Changes/FeatureAttributeChange.js.map +1 -1
- package/dist/Changes/FromFileBaseChange.d.ts +3 -3
- package/dist/Changes/FromFileBaseChange.js.map +1 -1
- package/dist/Changes/ImportJBrowseConfigChange.d.ts +4 -1
- package/dist/Changes/ImportJBrowseConfigChange.js +17 -13
- package/dist/Changes/ImportJBrowseConfigChange.js.map +1 -1
- package/dist/Changes/LocationEndChange.d.ts +1 -1
- package/dist/Changes/LocationEndChange.js +40 -37
- package/dist/Changes/LocationEndChange.js.map +1 -1
- package/dist/Changes/LocationStartChange.d.ts +1 -1
- package/dist/Changes/LocationStartChange.js +40 -37
- package/dist/Changes/LocationStartChange.js.map +1 -1
- package/dist/Changes/MergeExonsChange.d.ts +30 -0
- package/dist/Changes/MergeExonsChange.js +130 -0
- package/dist/Changes/MergeExonsChange.js.map +1 -0
- package/dist/Changes/MergeTranscriptsChange.d.ts +36 -0
- package/dist/Changes/MergeTranscriptsChange.js +246 -0
- package/dist/Changes/MergeTranscriptsChange.js.map +1 -0
- package/dist/Changes/SplitExonChange.d.ts +33 -0
- package/dist/Changes/SplitExonChange.js +142 -0
- package/dist/Changes/SplitExonChange.js.map +1 -0
- package/dist/Changes/StrandChange.d.ts +1 -1
- package/dist/Changes/TypeChange.d.ts +1 -1
- package/dist/Changes/UndoMergeExonsChange.d.ts +27 -0
- package/dist/Changes/UndoMergeExonsChange.js +104 -0
- package/dist/Changes/UndoMergeExonsChange.js.map +1 -0
- package/dist/Changes/UndoMergeTranscriptsChange.d.ts +27 -0
- package/dist/Changes/UndoMergeTranscriptsChange.js +104 -0
- package/dist/Changes/UndoMergeTranscriptsChange.js.map +1 -0
- package/dist/Changes/UndoSplitExonChange.d.ts +32 -0
- package/dist/Changes/UndoSplitExonChange.js +111 -0
- package/dist/Changes/UndoSplitExonChange.js.map +1 -0
- package/dist/Changes/UserChange.d.ts +1 -1
- package/dist/Changes/index.d.ts +21 -0
- package/dist/Changes/index.js +21 -0
- package/dist/Changes/index.js.map +1 -1
- package/dist/Checks/CDSCheck.d.ts +1 -1
- package/dist/GFF3/annotationFeatureToGFF3.d.ts +2 -2
- package/dist/GFF3/annotationFeatureToGFF3.js +7 -1
- package/dist/GFF3/annotationFeatureToGFF3.js.map +1 -1
- package/dist/GFF3/annotationFeatureToGFF3.test.js +1 -1
- package/dist/GFF3/annotationFeatureToGFF3.test.js.map +1 -1
- package/dist/GFF3/gff3ToAnnotationFeature.d.ts +2 -2
- package/dist/GFF3/gff3ToAnnotationFeature.test.d.ts +1 -1
- package/dist/GFF3/gff3ToAnnotationFeature.test.js +1 -1
- package/dist/GFF3/gff3ToAnnotationFeature.test.js.map +1 -1
- package/dist/GFF3/gffReservedKeys.d.ts +7 -0
- package/dist/GFF3/gffReservedKeys.js +21 -1
- package/dist/GFF3/gffReservedKeys.js.map +1 -1
- package/dist/Messages.d.ts +2 -2
- package/dist/Operations/GetAssembliesOperation.d.ts +1 -1
- package/dist/Operations/GetFeaturesOperation.d.ts +1 -1
- package/dist/Validations/CoreValidation.d.ts +2 -2
- package/dist/Validations/CoreValidation.js +1 -1
- package/dist/Validations/CoreValidation.js.map +1 -1
- package/dist/Validations/ParentChildValidation.d.ts +5 -5
- package/dist/Validations/ParentChildValidation.js +3 -1
- package/dist/Validations/ParentChildValidation.js.map +1 -1
- package/dist/Validations/Validation.d.ts +5 -5
- package/dist/Validations/ValidationSet.d.ts +4 -4
- package/dist/Validations/ValidationSet.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/util.d.ts +2 -0
- package/dist/util.js +13 -0
- package/dist/util.js.map +1 -1
- package/package.json +4 -4
- package/src/Changes/AddAssemblyAliasesChange.ts +69 -0
- package/src/Changes/AddAssemblyAndFeaturesFromFileChange.ts +6 -6
- package/src/Changes/AddAssemblyFromExternalChange.ts +5 -5
- package/src/Changes/AddAssemblyFromFileChange.ts +5 -5
- package/src/Changes/AddFeatureChange.ts +11 -35
- package/src/Changes/AddFeaturesFromFileChange.ts +6 -6
- package/src/Changes/AddRefSeqAliasesChange.ts +6 -6
- package/src/Changes/DeleteAssemblyChange.ts +4 -4
- package/src/Changes/DeleteFeatureChange.ts +49 -43
- package/src/Changes/DeleteUserChange.ts +5 -5
- package/src/Changes/FeatureAttributeChange.ts +22 -13
- package/src/Changes/FromFileBaseChange.ts +6 -3
- package/src/Changes/ImportJBrowseConfigChange.ts +37 -22
- package/src/Changes/LocationEndChange.ts +47 -56
- package/src/Changes/LocationStartChange.ts +47 -56
- package/src/Changes/MergeExonsChange.ts +185 -0
- package/src/Changes/MergeTranscriptsChange.ts +379 -0
- package/src/Changes/SplitExonChange.ts +249 -0
- package/src/Changes/StrandChange.ts +6 -6
- package/src/Changes/TypeChange.ts +6 -6
- package/src/Changes/UndoMergeExonsChange.ts +149 -0
- package/src/Changes/UndoMergeTranscriptsChange.ts +153 -0
- package/src/Changes/UndoSplitExonChange.ts +174 -0
- package/src/Changes/UserChange.ts +5 -5
- package/src/Changes/index.ts +21 -0
- package/src/Checks/CDSCheck.ts +2 -2
- package/src/GFF3/annotationFeatureToGFF3.test.ts +4 -2
- package/src/GFF3/annotationFeatureToGFF3.ts +11 -5
- package/src/GFF3/gff3ToAnnotationFeature.test.ts +3 -3
- package/src/GFF3/gff3ToAnnotationFeature.ts +2 -2
- package/src/GFF3/gffReservedKeys.ts +25 -0
- package/src/Messages.ts +2 -2
- package/src/Operations/GetAssembliesOperation.ts +3 -3
- package/src/Operations/GetFeaturesOperation.ts +4 -4
- package/src/Validations/CoreValidation.ts +4 -3
- package/src/Validations/ParentChildValidation.ts +10 -8
- package/src/Validations/Validation.ts +5 -5
- package/src/Validations/ValidationSet.ts +8 -4
- package/src/util.ts +16 -0
- package/test_data/example01.json +3 -3
- package/test_data/example02.json +3 -3
- package/test_data/example04.json +3 -3
- 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
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
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
|
-
|
|
88
|
-
|
|
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(
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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
|
|
150
|
-
const
|
|
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 "${
|
|
143
|
+
throw new Error(`Could not find feature with identifier "${featureId}"`)
|
|
153
144
|
}
|
|
154
|
-
feature.setMax(
|
|
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
|
-
//
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
.
|
|
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
|
-
|
|
88
|
-
|
|
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(
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
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
|
|
150
|
-
const
|
|
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 "${
|
|
143
|
+
throw new Error(`Could not find feature with identifier "${featureId}"`)
|
|
153
144
|
}
|
|
154
|
-
feature.setMin(
|
|
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
|
+
}
|