-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
946 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
import fs from 'fs' | ||
import path from 'path' | ||
import { beforeEach, describe, expect, it, vi } from 'vitest' | ||
import createFetchMock from 'vitest-fetch-mock' | ||
|
||
import { SeqvarImpl } from '@/lib/genomicVars' | ||
|
||
import { LinearStrucvarImpl } from '../../lib/genomicVars' | ||
import { MehariClient } from './client' | ||
import { SeqvarResult, StrucvarResult } from './types' | ||
|
||
/** Fixture Seqvar */ | ||
const seqvar = new SeqvarImpl('grch37', '1', 123, 'A', 'G') | ||
|
||
/** Fixture with BRCA1 seqvar consequence. */ | ||
const seqvarCsqResponseBrca1 = JSON.parse( | ||
fs.readFileSync(path.resolve(__dirname, './fixture.seqvarCsqResponse.BRCA1.json'), 'utf8') | ||
) | ||
|
||
/** Fixture Strucvar affecting BRCA1 */ | ||
const strucvar = new LinearStrucvarImpl('DEL', 'grch37', 'chr17', 43044295, 43044297) | ||
|
||
/** Fixture with strucvar (BRCA1) consequence */ | ||
const strucvarCsqResponseBrca1 = JSON.parse( | ||
fs.readFileSync(path.resolve(__dirname, './fixture.strucvarCsqResponse.BRCA1.json'), 'utf8') | ||
) | ||
|
||
/** Initialize mock for `fetch()`. */ | ||
const fetchMocker = createFetchMock(vi) | ||
|
||
describe.concurrent('MehariClient/seqvar', () => { | ||
beforeEach(() => { | ||
fetchMocker.enableMocks() | ||
fetchMocker.resetMocks() | ||
}) | ||
|
||
it('fetches TxCsq info correctly', async () => { | ||
// arrange: | ||
fetchMocker.mockResponseOnce(JSON.stringify(seqvarCsqResponseBrca1)) | ||
|
||
// act: | ||
const client = new MehariClient() | ||
const result = await client.retrieveSeqvarsCsq(seqvar, 'HGNC:1100') | ||
|
||
// assert: | ||
expect(JSON.stringify(result)).toEqual( | ||
JSON.stringify(SeqvarResult.fromJson(seqvarCsqResponseBrca1)) | ||
) | ||
}) | ||
|
||
it('fetches TxCsq info correctly without HGNC id', async () => { | ||
// arrange: | ||
fetchMocker.mockResponseOnce(JSON.stringify(seqvarCsqResponseBrca1)) | ||
|
||
// act: | ||
const client = new MehariClient() | ||
const result = await client.retrieveSeqvarsCsq(seqvar) | ||
|
||
// assert: | ||
expect(JSON.stringify(result)).toEqual( | ||
JSON.stringify(SeqvarResult.fromJson(seqvarCsqResponseBrca1)) | ||
) | ||
}) | ||
|
||
it('fails to fetch variant info with wrong variant', async () => { | ||
// arrange: | ||
const seqVarInvalid = new SeqvarImpl('grch37', '1', 123, 'A', 'T') | ||
fetchMocker.mockResponse((req) => { | ||
if (req.url.includes('alternative=G')) { | ||
return Promise.resolve(JSON.stringify(seqvarCsqResponseBrca1)) | ||
} | ||
return Promise.reject('failed to fetch seqvar') | ||
}) | ||
|
||
// act: | ||
const client = new MehariClient() | ||
// (with guard) | ||
await expect(async () => await client.retrieveSeqvarsCsq(seqVarInvalid)).rejects.toThrow( | ||
'failed to fetch seqvar' | ||
) | ||
|
||
// assert: | ||
}) | ||
}) | ||
|
||
describe.concurrent('MehariClient/strucvar', () => { | ||
beforeEach(() => { | ||
fetchMocker.enableMocks() | ||
fetchMocker.resetMocks() | ||
}) | ||
|
||
it('fetches strucvar info correctly', async () => { | ||
// arrange: | ||
fetchMocker.mockResponseOnce(JSON.stringify(strucvarCsqResponseBrca1)) | ||
|
||
// act: | ||
const client = new MehariClient() | ||
const result = await client.retrieveStrucvarsCsq(strucvar) | ||
|
||
// assert: | ||
expect(JSON.stringify(result)).toEqual( | ||
JSON.stringify(StrucvarResult.fromJson(strucvarCsqResponseBrca1)) | ||
) | ||
}) | ||
|
||
it('fails to fetch variant info with wrong variant', async () => { | ||
// arrange: | ||
const strucVarInvalid = new LinearStrucvarImpl('DUP', 'grch37', 'chr17', 43044295, 43044297) | ||
fetchMocker.mockResponse((req) => { | ||
if (req.url.includes('DEL')) { | ||
return Promise.resolve(JSON.stringify(strucvarCsqResponseBrca1)) | ||
} | ||
return Promise.reject('failed to fetch strucvar') | ||
}) | ||
|
||
// act: | ||
const client = new MehariClient() | ||
// (with guard) | ||
await expect(async () => await client.retrieveStrucvarsCsq(strucVarInvalid)).rejects.toThrow( | ||
'failed to fetch strucvar' | ||
) | ||
|
||
// assert: | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import type { LinearStrucvar, Seqvar } from '../../lib/genomicVars' | ||
import { SeqvarResult, StrucvarResult } from './types' | ||
|
||
/** API base URL to use. */ | ||
const API_BASE_URL = '/internal/proxy/pubtator3-api' | ||
|
||
/** | ||
* Client for Mehari API requests. | ||
*/ | ||
export class MehariClient { | ||
private apiBaseUrl: string | ||
|
||
constructor(apiBaseUrl?: string) { | ||
this.apiBaseUrl = apiBaseUrl ?? API_BASE_URL | ||
} | ||
|
||
/** | ||
* Retrieve consequences for sequence variants. | ||
* | ||
* @param seqvar Sequence variant to retrieve consequences for. | ||
* @param hgncId HGNC ID of gene to restrict results to. | ||
* @returns The response from the API. | ||
* @throws Error if the API request fails. | ||
*/ | ||
async retrieveSeqvarsCsq(seqvar: Seqvar, hgncId?: string): Promise<SeqvarResult> { | ||
const { genomeBuild, chrom, pos, del, ins } = seqvar | ||
const hgncSuffix = hgncId ? `&hgnc_id=${hgncId}` : '' | ||
const url = | ||
`${this.apiBaseUrl}seqvars/csq?genome_release=${genomeBuild}&` + | ||
`chromosome=${chrom}&position=${pos}&reference=${del}&` + | ||
`alternative=${ins}${hgncSuffix}` | ||
|
||
const response = await fetch(url, { | ||
method: 'GET' | ||
}) | ||
if (!response.ok) { | ||
throw new Error(`Failed to fetch sequence variant consequences: ${response.statusText}`) | ||
} | ||
const responseJson = await response.json() | ||
return SeqvarResult.fromJson(responseJson) | ||
} | ||
|
||
/** | ||
* Retrieve consequences for structural variants. | ||
* | ||
* @param strucvar Structural variant to retrieve consequences for. | ||
* @returns The response from the API. | ||
* @throws Error if the API request fails. | ||
*/ | ||
async retrieveStrucvarsCsq(strucvar: LinearStrucvar): Promise<StrucvarResult> { | ||
const { genomeBuild, chrom, start, stop, svType } = strucvar | ||
const url = | ||
`${this.apiBaseUrl}strucvars/csq?genome_release=${genomeBuild}&` + | ||
`chromosome=${chrom}&start=${start}&stop=${stop}&sv_type=${svType}` | ||
|
||
const response = await fetch(url, { | ||
method: 'GET' | ||
}) | ||
if (!response.ok) { | ||
throw new Error(`Failed to fetch structural variant consequences: ${response.statusText}`) | ||
} | ||
const responseJson = await response.json() | ||
return StrucvarResult.fromJson(responseJson) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
{ | ||
"version": { "tx_db": "0.5.0", "mehari": "0.7.0" }, | ||
"query": { | ||
"genome_release": "grch37", | ||
"chromosome": "chr17", | ||
"position": 41197751, | ||
"reference": "G", | ||
"alternative": "T", | ||
"hgnc_id": null | ||
}, | ||
"result": [ | ||
{ | ||
"consequences": ["MissenseVariant"], | ||
"putative_impact": "Moderate", | ||
"gene_symbol": "BRCA1", | ||
"gene_id": "HGNC:1100", | ||
"feature_type": { "SoTerm": { "term": "Transcript" } }, | ||
"feature_id": "NM_007294.4", | ||
"feature_biotype": "Coding", | ||
"rank": { "ord": 23, "total": 23 }, | ||
"hgvs_t": "c.5536C>A", | ||
"hgvs_p": "p.Q1846K", | ||
"tx_pos": { "ord": 5649, "total": 7088 }, | ||
"cds_pos": { "ord": 5536, "total": 5592 }, | ||
"protein_pos": { "ord": 1846, "total": 1864 }, | ||
"distance": -1440, | ||
"messages": null | ||
}, | ||
{ | ||
"consequences": ["MissenseVariant"], | ||
"putative_impact": "Moderate", | ||
"gene_symbol": "BRCA1", | ||
"gene_id": "HGNC:1100", | ||
"feature_type": { "SoTerm": { "term": "Transcript" } }, | ||
"feature_id": "NM_007297.4", | ||
"feature_biotype": "Coding", | ||
"rank": { "ord": 22, "total": 22 }, | ||
"hgvs_t": "c.5395C>A", | ||
"hgvs_p": "p.Q1799K", | ||
"tx_pos": { "ord": 5589, "total": 7028 }, | ||
"cds_pos": { "ord": 5395, "total": 5451 }, | ||
"protein_pos": { "ord": 1799, "total": 1817 }, | ||
"distance": -1440, | ||
"messages": null | ||
}, | ||
{ | ||
"consequences": ["MissenseVariant"], | ||
"putative_impact": "Moderate", | ||
"gene_symbol": "BRCA1", | ||
"gene_id": "HGNC:1100", | ||
"feature_type": { "SoTerm": { "term": "Transcript" } }, | ||
"feature_id": "NM_007298.3", | ||
"feature_biotype": "Coding", | ||
"rank": { "ord": 22, "total": 22 }, | ||
"hgvs_t": "c.2224C>A", | ||
"hgvs_p": "p.Q742K", | ||
"tx_pos": { "ord": 2243, "total": 3682 }, | ||
"cds_pos": { "ord": 2224, "total": 2280 }, | ||
"protein_pos": { "ord": 742, "total": 760 }, | ||
"distance": -1440, | ||
"messages": null | ||
}, | ||
{ | ||
"consequences": ["MissenseVariant"], | ||
"putative_impact": "Moderate", | ||
"gene_symbol": "BRCA1", | ||
"gene_id": "HGNC:1100", | ||
"feature_type": { "SoTerm": { "term": "Transcript" } }, | ||
"feature_id": "NM_007300.4", | ||
"feature_biotype": "Coding", | ||
"rank": { "ord": 24, "total": 24 }, | ||
"hgvs_t": "c.5599C>A", | ||
"hgvs_p": "p.Q1867K", | ||
"tx_pos": { "ord": 5712, "total": 7151 }, | ||
"cds_pos": { "ord": 5599, "total": 5655 }, | ||
"protein_pos": { "ord": 1867, "total": 1885 }, | ||
"distance": -1440, | ||
"messages": null | ||
}, | ||
{ | ||
"consequences": ["ThreePrimeUtrVariant"], | ||
"putative_impact": "Modifier", | ||
"gene_symbol": "BRCA1", | ||
"gene_id": "HGNC:1100", | ||
"feature_type": { "SoTerm": { "term": "Transcript" } }, | ||
"feature_id": "NM_007299.4", | ||
"feature_biotype": "Coding", | ||
"rank": { "ord": 22, "total": 22 }, | ||
"hgvs_t": "c.*50C>A", | ||
"hgvs_p": "p.?", | ||
"tx_pos": { "ord": 2257, "total": 3696 }, | ||
"cds_pos": { "ord": 50, "total": 2100 }, | ||
"protein_pos": null, | ||
"distance": -1440, | ||
"messages": null | ||
} | ||
] | ||
} |
Oops, something went wrong.