Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Integrate cada prio web service (#213) #220

Merged
merged 6 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions backend/app/api/internal/endpoints/proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ async def reverse_proxy(request: Request) -> Response:
backend_url = settings.BACKEND_PREFIX_NGINX + url.path.replace("/internal/proxy/nginx", "")
elif url.path.startswith(f"{settings.INTERNAL_STR}/proxy/dotty"):
backend_url = settings.BACKEND_PREFIX_DOTTY + url.path.replace("/internal/proxy/dotty", "")
elif url.path.startswith(f"{settings.INTERNAL_STR}/proxy/cada-prio"):
backend_url = settings.BACKEND_PREFIX_CADA_PRIO + url.path.replace(
"/internal/proxy/cada-prio", ""
)

if backend_url:
client = httpx.AsyncClient()
Expand Down
2 changes: 2 additions & 0 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ def assemble_cors_origins(cls, v: str | list[str]) -> list[str] | str: # pragma
BACKEND_PREFIX_NGINX: str = "http://nginx:80"
#: Prefix for the backend of dotty service.
BACKEND_PREFIX_DOTTY: str = "http://dotty:8080"
#: Prefix for the backend of cada-prio service.
BACKEND_PREFIX_CADA_PRIO: str = "http://cada-prio:8080"

#: URL to REDIS service.
REDIS_URL: str = "redis://redis:6379"
Expand Down
1 change: 1 addition & 0 deletions backend/env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ BACKEND_PREFIX_MEHARI=http://localhost:3002
BACKEND_PREFIX_VIGUNO=http://localhost:3003
BACKEND_PREFIX_NGINX=http://localhost:3004
BACKEND_PREFIX_DOTTY=http://localhost:3005
BACKEND_PREFIX_CADA_PRIORI=http://localhost:3006

# Access to redis as it runs Docker Compose.
REDIS_URL=redis://localhost:3030
Expand Down
15 changes: 15 additions & 0 deletions backend/tests/api/internal/test_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,21 @@ async def test_proxy_dotty(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, clie
assert response.text == "Mocked response"


@pytest.mark.asyncio
async def test_proxy_cada_prio(monkeypatch: MonkeyPatch, httpx_mock: HTTPXMock, client: TestClient):
"""Test proxying to cada-prio backend."""
monkeypatch.setattr(settings, "BACKEND_PREFIX_CADA_PRIO", f"http://{MOCKED_BACKEND_HOST}")
httpx_mock.add_response(
url=f"http://{MOCKED_BACKEND_HOST}/{MOCKED_URL_TOKEN}",
method="GET",
text="Mocked response",
)

response = client.get(f"/internal/proxy/cada-prio/{MOCKED_URL_TOKEN}")
assert response.status_code == 200
assert response.text == "Mocked response"


@pytest.mark.asyncio
async def test_invalid_proxy_route(client: TestClient):
"""Test invalid proxy route."""
Expand Down
42 changes: 42 additions & 0 deletions frontend/src/api/__tests__/cadaprio.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import createFetchMock from 'vitest-fetch-mock'

import { CadaPrioClient } from '@/api/cadaprio'

const fetchMocker = createFetchMock(vi)

describe.concurrent('Cada Prio Client', () => {
beforeEach(() => {
fetchMocker.enableMocks()
fetchMocker.resetMocks()
})

it('fetches gene impact correctly', async () => {
fetchMocker.mockResponseOnce(JSON.stringify({ result: 'pathogenic' }))

const client = new CadaPrioClient()
const result = await client.predictGeneImpact(['HP:0000001'])
expect(JSON.stringify(result)).toEqual(JSON.stringify({ result: 'pathogenic' }))
})

it('fetches gene impact correctly with gene symbols', async () => {
fetchMocker.mockResponseOnce(JSON.stringify({ result: 'pathogenic' }))

const client = new CadaPrioClient()
const result = await client.predictGeneImpact(['HP:0000001'], ['BRCA1'])
expect(JSON.stringify(result)).toEqual(JSON.stringify({ result: 'pathogenic' }))
})

it('fails to fetch gene impact with wrong HPO terms', async () => {
fetchMocker.mockResponse((req) => {
if (req.url.includes('hpo_terms=HP:0000001')) {
return Promise.resolve(JSON.stringify({ result: 'pathogenic' }))
}
return Promise.resolve(JSON.stringify({ status: 400 }))
})

const client = new CadaPrioClient()
const result = await client.predictGeneImpact(['123'])
expect(JSON.stringify(result)).toEqual(JSON.stringify({ status: 400 }))
})
})
7 changes: 7 additions & 0 deletions frontend/src/api/__tests__/common.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { describe, expect, it } from 'vitest'
import {
API_INTERNAL_BASE_PREFIX,
API_INTERNAL_BASE_PREFIX_ANNONARS,
API_INTERNAL_BASE_PREFIX_CADA_PRIO,
API_INTERNAL_BASE_PREFIX_MEHARI,
API_INTERNAL_BASE_PREFIX_NGINX
} from '@/api/common'
Expand Down Expand Up @@ -31,4 +32,10 @@ describe.concurrent('API_BASE_PREFIX constants', () => {
expect(API_INTERNAL_BASE_PREFIX_NGINX).toBe('/internal/proxy/nginx')
import.meta.env.MODE = originalMode
})

it('returns the correct API base prefix for cada-prio in production mode', () => {
const originalMode = import.meta.env.MODE
expect(API_INTERNAL_BASE_PREFIX_CADA_PRIO).toBe('/internal/proxy/cada-prio')
import.meta.env.MODE = originalMode
})
})
23 changes: 23 additions & 0 deletions frontend/src/api/cadaprio.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { API_INTERNAL_BASE_PREFIX_CADA_PRIO } from './common'

const API_BASE_URL = `${API_INTERNAL_BASE_PREFIX_CADA_PRIO}/`

export class CadaPrioClient {
private apiBaseUrl: string
private csrfToken: string | null

constructor(apiBaseUrl?: string, csrfToken?: string) {
this.apiBaseUrl = apiBaseUrl ?? API_BASE_URL
this.csrfToken = csrfToken ?? null
}

async predictGeneImpact(hpoTerms: string[], geneSymbols?: string[]): Promise<any> {
const geneSuffix = geneSymbols ? `&gene_symbols=${geneSymbols.join(',')}` : ''
const url = `${this.apiBaseUrl}api/v1/predict?hpo_terms=${hpoTerms.join(',')}${geneSuffix}`

const response = await fetch(url, {
method: 'GET'
})
return await response.json()
}
}
1 change: 1 addition & 0 deletions frontend/src/api/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const API_INTERNAL_BASE_PREFIX_MEHARI = '/internal/proxy/mehari'
export const API_INTERNAL_BASE_PREFIX_VIGUNO = '/internal/proxy/viguno'
export const API_INTERNAL_BASE_PREFIX_NGINX = '/internal/proxy/nginx'
export const API_INTERNAL_BASE_PREFIX_DOTTY = '/internal/proxy/dotty'
export const API_INTERNAL_BASE_PREFIX_CADA_PRIO = '/internal/proxy/cada-prio'
export const API_V1_BASE_PREFIX = '/api/v1/'
Loading
Loading