From 8fd4079533b212469256e29c70b573523f519677 Mon Sep 17 00:00:00 2001 From: Oliver Stolpe Date: Wed, 4 Sep 2024 13:56:14 +0200 Subject: [PATCH] fix: most recent mehari rest service causes unknown fields error (#249) --- package.json | 3 +- protos/mehari/txs.proto | 10 +++ src/api/annonars/client.ts | 6 +- src/api/annonars/types.ts | 20 ++++-- src/api/mehari/client.ts | 2 +- .../ClinvarFreqPlot.stories.ts | 8 ++- .../GeneClinvarCard.stories.ts | 28 ++++++--- src/pbs/mehari/txs.ts | 62 ++++++++++++++++++- 8 files changed, 117 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 40d0f26..c3d8379 100644 --- a/package.json +++ b/package.json @@ -106,5 +106,6 @@ "vitest-canvas-mock": "^0.3.3", "vitest-fetch-mock": "^0.2.2", "vue-tsc": "^2.0.19" - } + }, + "packageManager": "yarn@1.22.22+sha512.a6b2f7906b721bba3d67d4aff083df04dad64c399707841b7acf00f6b133b7ac24255f2652fa22ae3534329dc6180534e98d17432037ff6fd140556e2bb3137e" } diff --git a/protos/mehari/txs.proto b/protos/mehari/txs.proto index 50b897d..0bb390a 100644 --- a/protos/mehari/txs.proto +++ b/protos/mehari/txs.proto @@ -23,6 +23,10 @@ message GeneToTxId { string gene_id = 1; // Vector of all transcript IDs. repeated string tx_ids = 2; + // Whether this gene has been filtered out because of missing transcripts. + optional bool filtered = 3; + // Reason for filtering. + optional uint32 filter_reason = 4; } // Container for the transcript-related database. @@ -59,6 +63,8 @@ enum TranscriptTag { TRANSCRIPT_TAG_REF_SEQ_SELECT = 5; // Flagged as being a selenoprotein (UGA => selenon). TRANSCRIPT_TAG_SELENOPROTEIN = 6; + // Member of GENCODE Primary + TRANSCRIPT_TAG_GENCODE_PRIMARY = 7; } // Store information about a transcript. @@ -81,6 +87,10 @@ message Transcript { optional int32 stop_codon = 8; // Alignments on the different genome builds. repeated GenomeAlignment genome_alignments = 9; + // Whether this transcript has an issue (e.g. MissingStopCodon), cf. `mehari::db::create::mod::Reason`. + optional bool filtered = 10; + // Reason for filtering. + optional uint32 filter_reason = 11; } // Enumeration for the known genome builds. diff --git a/src/api/annonars/client.ts b/src/api/annonars/client.ts index 0bf9f9e..91dddb5 100644 --- a/src/api/annonars/client.ts +++ b/src/api/annonars/client.ts @@ -112,7 +112,9 @@ export class AnnonarsClient { const responseJson = await response.json() if (responseJson?.genes && responseJson?.genes[hgncId]) { try { - return ClinvarPerGeneRecord.fromJson(responseJson.genes[hgncId]) + return ClinvarPerGeneRecord.fromJson(responseJson.genes[hgncId], { + ignoreUnknownFields: true + }) } catch (e) { throw new InvalidResponseContent(`failed to parse gene clinvar info response: ${e}`) } @@ -176,7 +178,7 @@ export class AnnonarsClient { resultJsons.forEach((chunk: any) => { for (const value of Object.values(chunk.genes)) { // @ts-ignore - result.push(GeneInfoRecord.fromJson(value as JsonValue)) + result.push(GeneInfoRecord.fromJson(value as JsonValue, { ignoreUnknownFields: true })) } }) return result diff --git a/src/api/annonars/types.ts b/src/api/annonars/types.ts index a8a41e3..4718017 100644 --- a/src/api/annonars/types.ts +++ b/src/api/annonars/types.ts @@ -32,7 +32,9 @@ class ClinvarSvQueryResponse$Type { fromJson(apiResponse: ClinvarSvQueryResponse$Api): ClinvarSvQueryResponse { const records = apiResponse.records ?? [] return { - records: records.map((value) => ClinvarStrucvarResponseRecord.fromJson(value)) + records: records.map((value) => + ClinvarStrucvarResponseRecord.fromJson(value, { ignoreUnknownFields: true }) + ) } } } @@ -176,7 +178,7 @@ class GeneInfoResult$Type { return { genes: Object.values(apiResult.genes).map((gene$Api) => // @ts-ignore - GeneInfoRecord.fromJson(gene$Api as JsonValue) + GeneInfoRecord.fromJson(gene$Api as JsonValue, { ignoreUnknownFields: true }) ) } } @@ -278,27 +280,33 @@ class SeqvarInfoResult$Type { // @ts-ignore apiResult.dbsnp === null ? undefined // @ts-ignore - : DbsnpRecord.fromJson(apiResult.dbsnp as JsonValue), + : DbsnpRecord.fromJson(apiResult.dbsnp as JsonValue, { ignoreUnknownFields: true }), dbnsfp: apiResult.dbnsfp === null ? undefined : apiResult.dbnsfp, dbscsnv: apiResult.dbscsnv === null ? undefined : apiResult.dbscsnv, gnomadMtdna: apiResult.gnomad_mtdna === null ? undefined : // @ts-ignore - GnomadMtdnaRecord.fromJson(apiResult.gnomad_mtdna as JsonValue), + GnomadMtdnaRecord.fromJson(apiResult.gnomad_mtdna as JsonValue, { + ignoreUnknownFields: true + }), gnomadExomes: apiResult.gnomad_exomes === null ? undefined : apiResult.gnomad_exomes, gnomadGenomes: apiResult.gnomad_genomes === null ? undefined : apiResult.gnomad_genomes, helixmtdb: apiResult.helixmtdb === null ? undefined : // @ts-ignore - HelixmtdbRecord.fromJson(apiResult.helixmtdb as JsonValue), + HelixmtdbRecord.fromJson(apiResult.helixmtdb as JsonValue, { + ignoreUnknownFields: true + }), ucscConservation: apiResult.ucsc_conservation.map((cons) => cons.records), clinvar: apiResult.clinvar === null ? undefined : // @ts-ignore - ExtractedVcvRecordList.fromJson(apiResult.clinvar as JsonValue) + ExtractedVcvRecordList.fromJson(apiResult.clinvar as JsonValue, { + ignoreUnknownFields: true + }) } } } diff --git a/src/api/mehari/client.ts b/src/api/mehari/client.ts index 21c5252..de79408 100644 --- a/src/api/mehari/client.ts +++ b/src/api/mehari/client.ts @@ -132,7 +132,7 @@ export class MehariClient { } try { const responseJson = await response.json() - return GeneTranscriptsResponse.fromJson(responseJson) + return GeneTranscriptsResponse.fromJson(responseJson, { ignoreUnknownFields: true }) } catch (e) { throw new InvalidResponseContent(`Failed to parse transcript response: ${e}`) } diff --git a/src/components/GeneClinvarCard/ClinvarFreqPlot.stories.ts b/src/components/GeneClinvarCard/ClinvarFreqPlot.stories.ts index 27203a8..2aa9f15 100644 --- a/src/components/GeneClinvarCard/ClinvarFreqPlot.stories.ts +++ b/src/components/GeneClinvarCard/ClinvarFreqPlot.stories.ts @@ -10,9 +10,13 @@ import geneClinvarTgdsJson from './fixture.clinvarPerGene.TGDS.json' // as in the tests fails with "process is not defined" error in the browser. // @ts-ignore -const clinvarPerGeneTgds = ClinvarPerGeneRecord.fromJson(geneClinvarTgdsJson as JsonValue) +const clinvarPerGeneTgds = ClinvarPerGeneRecord.fromJson(geneClinvarTgdsJson as JsonValue, { + ignoreUnknownFields: true +}) // @ts-ignore -const clinvarPerGeneBrca1 = ClinvarPerGeneRecord.fromJson(geneClinvarBrca1Json as JsonValue) +const clinvarPerGeneBrca1 = ClinvarPerGeneRecord.fromJson(geneClinvarBrca1Json as JsonValue, { + ignoreUnknownFields: true +}) const meta = { title: 'Gene/Clinvar/ClinvarFreqPlot', diff --git a/src/components/GeneClinvarCard/GeneClinvarCard.stories.ts b/src/components/GeneClinvarCard/GeneClinvarCard.stories.ts index ba457aa..b7f0ca9 100644 --- a/src/components/GeneClinvarCard/GeneClinvarCard.stories.ts +++ b/src/components/GeneClinvarCard/GeneClinvarCard.stories.ts @@ -17,19 +17,33 @@ import genesTxsTgdsJson38 from './fixture.genesTxs.TGDS.38.json' // as in the tests fails with "process is not defined" error in the browser. // @ts-ignore -const clinvarPerGeneTgds = ClinvarPerGeneRecord.fromJson(geneClinvarTgdsJson as JsonValue) +const clinvarPerGeneTgds = ClinvarPerGeneRecord.fromJson(geneClinvarTgdsJson as JsonValue, { + ignoreUnknownFields: true +}) // @ts-ignore -const clinvarPerGeneBrca1 = ClinvarPerGeneRecord.fromJson(geneClinvarBrca1Json as JsonValue) +const clinvarPerGeneBrca1 = ClinvarPerGeneRecord.fromJson(geneClinvarBrca1Json as JsonValue, { + ignoreUnknownFields: true +}) // @ts-ignore -const genesTxsTgds37 = GeneTranscriptsResponse.fromJson(genesTxsTgdsJson37 as JsonValue) +const genesTxsTgds37 = GeneTranscriptsResponse.fromJson(genesTxsTgdsJson37 as JsonValue, { + ignoreUnknownFields: true +}) // @ts-ignore -const genesTxsTgds38 = GeneTranscriptsResponse.fromJson(genesTxsTgdsJson38 as JsonValue) +const genesTxsTgds38 = GeneTranscriptsResponse.fromJson(genesTxsTgdsJson38 as JsonValue, { + ignoreUnknownFields: true +}) // @ts-ignore -const genesTxsBrca137 = GeneTranscriptsResponse.fromJson(genesTxsBrca1Json37 as JsonValue) +const genesTxsBrca137 = GeneTranscriptsResponse.fromJson(genesTxsBrca1Json37 as JsonValue, { + ignoreUnknownFields: true +}) // @ts-ignore -const geneInfoTgds = GeneInfoRecord.fromJson(geneInfoTgdsJson as JsonValue) +const geneInfoTgds = GeneInfoRecord.fromJson(geneInfoTgdsJson as JsonValue, { + ignoreUnknownFields: true +}) // @ts-ignore -const geneInfoBrca1 = GeneInfoRecord.fromJson(geneInfoBrca1Json as JsonValue) +const geneInfoBrca1 = GeneInfoRecord.fromJson(geneInfoBrca1Json as JsonValue, { + ignoreUnknownFields: true +}) const meta = { title: 'Gene/Clinvar/GeneClinvarCard', diff --git a/src/pbs/mehari/txs.ts b/src/pbs/mehari/txs.ts index fb3e914..9698619 100644 --- a/src/pbs/mehari/txs.ts +++ b/src/pbs/mehari/txs.ts @@ -59,6 +59,18 @@ export interface GeneToTxId { * @generated from protobuf field: repeated string tx_ids = 2; */ txIds: string[] + /** + * Whether this gene has been filtered out because of missing transcripts. + * + * @generated from protobuf field: optional bool filtered = 3; + */ + filtered?: boolean + /** + * Reason for filtering. + * + * @generated from protobuf field: optional uint32 filter_reason = 4; + */ + filterReason?: number } /** * Container for the transcript-related database. @@ -139,6 +151,18 @@ export interface Transcript { * @generated from protobuf field: repeated mehari.txs.GenomeAlignment genome_alignments = 9; */ genomeAlignments: GenomeAlignment[] + /** + * Whether this transcript has an issue (e.g. MissingStopCodon), cf. `mehari::db::create::mod::Reason`. + * + * @generated from protobuf field: optional bool filtered = 10; + */ + filtered?: boolean + /** + * Reason for filtering. + * + * @generated from protobuf field: optional uint32 filter_reason = 11; + */ + filterReason?: number } /** * Store information about a transcript aligning to a genome. @@ -329,7 +353,13 @@ export enum TranscriptTag { * * @generated from protobuf enum value: TRANSCRIPT_TAG_SELENOPROTEIN = 6; */ - TRANSCRIPT_TAG_SELENOPROTEIN = 6 + TRANSCRIPT_TAG_SELENOPROTEIN = 6, + /** + * Member of GENCODE Primary + * + * @generated from protobuf enum value: TRANSCRIPT_TAG_GENCODE_PRIMARY = 7; + */ + TRANSCRIPT_TAG_GENCODE_PRIMARY = 7 } /** * Enumeration for the known genome builds. @@ -495,7 +525,9 @@ class GeneToTxId$Type extends MessageType { kind: 'scalar', repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ - } + }, + { no: 3, name: 'filtered', kind: 'scalar', opt: true, T: 8 /*ScalarType.BOOL*/ }, + { no: 4, name: 'filter_reason', kind: 'scalar', opt: true, T: 13 /*ScalarType.UINT32*/ } ]) } create(value?: PartialMessage): GeneToTxId { @@ -522,6 +554,12 @@ class GeneToTxId$Type extends MessageType { case /* repeated string tx_ids */ 2: message.txIds.push(reader.string()) break + case /* optional bool filtered */ 3: + message.filtered = reader.bool() + break + case /* optional uint32 filter_reason */ 4: + message.filterReason = reader.uint32() + break default: const u = options.readUnknownField if (u === 'throw') @@ -551,6 +589,11 @@ class GeneToTxId$Type extends MessageType { /* repeated string tx_ids = 2; */ for (let i = 0; i < message.txIds.length; i++) writer.tag(2, WireType.LengthDelimited).string(message.txIds[i]) + /* optional bool filtered = 3; */ + if (message.filtered !== undefined) writer.tag(3, WireType.Varint).bool(message.filtered) + /* optional uint32 filter_reason = 4; */ + if (message.filterReason !== undefined) + writer.tag(4, WireType.Varint).uint32(message.filterReason) const u = options.writeUnknownFields if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer) return writer @@ -680,7 +723,9 @@ class Transcript$Type extends MessageType { kind: 'message', repeat: 1 /*RepeatType.PACKED*/, T: () => GenomeAlignment - } + }, + { no: 10, name: 'filtered', kind: 'scalar', opt: true, T: 8 /*ScalarType.BOOL*/ }, + { no: 11, name: 'filter_reason', kind: 'scalar', opt: true, T: 13 /*ScalarType.UINT32*/ } ]) } create(value?: PartialMessage): Transcript { @@ -737,6 +782,12 @@ class Transcript$Type extends MessageType { GenomeAlignment.internalBinaryRead(reader, reader.uint32(), options) ) break + case /* optional bool filtered */ 10: + message.filtered = reader.bool() + break + case /* optional uint32 filter_reason */ 11: + message.filterReason = reader.uint32() + break default: const u = options.readUnknownField if (u === 'throw') @@ -790,6 +841,11 @@ class Transcript$Type extends MessageType { writer.tag(9, WireType.LengthDelimited).fork(), options ).join() + /* optional bool filtered = 10; */ + if (message.filtered !== undefined) writer.tag(10, WireType.Varint).bool(message.filtered) + /* optional uint32 filter_reason = 11; */ + if (message.filterReason !== undefined) + writer.tag(11, WireType.Varint).uint32(message.filterReason) const u = options.writeUnknownFields if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer) return writer