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: put the current version into docker image and display (#28) #29

Merged
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
.coverage
coverage.lcov

# Version file
/VERSION

# Environment variables
.env

Expand Down
19 changes: 19 additions & 0 deletions backend/app/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import os
import pathlib
import subprocess
import sys

import httpx
Expand All @@ -25,6 +26,14 @@
BACKEND_PREFIX_MEHARI = env.get("REEV_BACKEND_PREFIX_MEHARI", "http://mehari:8080")
#: Prefix for the backend of viguno service
BACKEND_PREFIX_VIGUNO = env.get("REEV_BACKEND_PREFIX_VIGUNO", "http://viguno:8080")
#: Path to REEV version file.
VERSION_FILE = env.get("REEV_VERSION_FILE", "/VERSION")
#: The REEV version from the file (``None`` if to load dynamically from git)
REEV_VERSION = None
# Try to obtain version from file, otherwise keep it at ``None``
if os.path.exists(VERSION_FILE):
with open(VERSION_FILE) as f:
REEV_VERSION = f.read().strip() or None

Check warning on line 36 in backend/app/main.py

View check run for this annotation

Codecov / codecov/patch

backend/app/main.py#L35-L36

Added lines #L35 - L36 were not covered by tests


app = FastAPI()
Expand Down Expand Up @@ -82,6 +91,16 @@
app.add_route("/proxy/{path:path}", reverse_proxy, methods=["GET", "POST"])


# Register app for returning REEV version.
@app.get("/version")
async def version():
if REEV_VERSION:
version = REEV_VERSION

Check warning on line 98 in backend/app/main.py

View check run for this annotation

Codecov / codecov/patch

backend/app/main.py#L97-L98

Added lines #L97 - L98 were not covered by tests
else:
version = subprocess.check_output(["git", "describe", "--tags", "--dirty"]).strip()
return Response(content=version)

Check warning on line 101 in backend/app/main.py

View check run for this annotation

Codecov / codecov/patch

backend/app/main.py#L100-L101

Added lines #L100 - L101 were not covered by tests


# Register route for favicon.
@app.get("/favicon.ico")
async def favicon():
Expand Down
14 changes: 10 additions & 4 deletions frontend/src/api/__tests__/common.spec.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
import { describe, it, expect } from 'vitest'

import { API_BASE_PREFIX } from '../common'
import { API_BASE_PREFIX, API_PROXY_BASE_PREFIX } from '../common'

describe('API_BASE_PREFIX constant', () => {
it('returns the correct API base prefix in production mode', () => {
describe('constants', () => {
it('returns the correct proxy API base prefix in production mode', () => {
const originalMode = import.meta.env.MODE
expect(API_BASE_PREFIX).toBe('/proxy/annonars')
expect(API_BASE_PREFIX).toBe('/')
import.meta.env.MODE = originalMode
})

it('returns the correct proxy API base prefix in production mode', () => {
const originalMode = import.meta.env.MODE
expect(API_PROXY_BASE_PREFIX).toBe('/proxy/')
import.meta.env.MODE = originalMode
})
})
21 changes: 21 additions & 0 deletions frontend/src/api/__tests__/misc.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { beforeEach, describe, it, expect, vi } from 'vitest'
import createFetchMock from 'vitest-fetch-mock'

import { MiscClient } from '../misc'

const fetchMocker = createFetchMock(vi)

describe('Misc Client', () => {
beforeEach(() => {
fetchMocker.enableMocks()
fetchMocker.resetMocks()
})

it('fetches version info correctly', async () => {
fetchMocker.mockResponseOnce('v0.0.0')

const client = new MiscClient()
const result = await client.fetchVersion()
expect(result).toEqual('v0.0.0')
})
})
8 changes: 3 additions & 5 deletions frontend/src/api/annonars.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
import { API_BASE_PREFIX } from '@/api/common'

const API_BASE_URL = `${API_BASE_PREFIX}/`
import { API_PROXY_BASE_PREFIX } from '@/api/common'

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

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

async fetchGeneInfo(hgncId: string): Promise<any> {
const response = await fetch(`${this.apiBaseUrl}genes/info?hgnc_id=${hgncId}`, {
const response = await fetch(`${this.apiBaseUrl}/genes/info?hgnc_id=${hgncId}`, {
method: 'GET'
})
return await response.json()
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/api/common.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const API_BASE_PREFIX =
import.meta.env.MODE == 'development' ? '//localhost:8080/proxy/annonars' : '/proxy/annonars'
export const API_BASE_PREFIX = import.meta.env.MODE == 'development' ? '//localhost:8080/' : '/'

export const API_PROXY_BASE_PREFIX = `${API_BASE_PREFIX}proxy/`
20 changes: 20 additions & 0 deletions frontend/src/api/misc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { API_BASE_PREFIX } from '@/api/common'

const API_BASE_URL = API_BASE_PREFIX

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

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

async fetchVersion(): Promise<any> {
const response = await fetch(`${this.apiBaseUrl}version`, {
method: 'GET'
})
return await response.text()
}
}
14 changes: 14 additions & 0 deletions frontend/src/components/HeaderDefault.vue
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { useMiscStore } from '@/stores/misc'

const miscStore = useMiscStore()

onMounted(() => {
if (miscStore?.initialize) {
miscStore?.initialize()
}
})
</script>

<template>
<v-app-bar app class="top-bar">
<v-toolbar-title>
<router-link to="/">
<img src="@/assets/reev-logo.svg" id="logo" alt="logo" width="100" />
Explanation and Evaluation of Variants
<small>{{ miscStore?.appVersion }}</small>
</router-link>
</v-toolbar-title>
<v-spacer></v-spacer>
Expand Down
16 changes: 14 additions & 2 deletions frontend/src/components/__tests__/HeaderDefault.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, it, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from '@/router'
import { createTestingPinia } from '@pinia/testing'

import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
Expand All @@ -28,7 +29,18 @@ const makeWrapper = () => {
},
{
global: {
plugins: [vuetify, router],
plugins: [
vuetify,
router,
createTestingPinia({
createSpy: vi.fn(),
initialState: {
misc: {
appVersion: 'v0.0.0'
}
}
})
],
components: {
HeaderDefault
}
Expand All @@ -44,7 +56,7 @@ describe('HeaderDefault.vue', () => {
const logo = wrapper.find('#logo')
const title = wrapper.find('a[href="/"]')
expect(logo.exists()).toBe(true)
expect(title.text()).toBe('Explanation and Evaluation of Variants')
expect(title.text()).toBe('Explanation and Evaluation of Variants v0.0.0')
})

it('renders the navigation links', () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/__tests__/HeaderDetailPage.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'

import HeaderDetailPage from '../HeaderDetailPage.vue'
import { StoreState } from '@/stores/geneInfo'
import { StoreState } from '@/stores/misc'

const vuetify = createVuetify({
components,
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/stores/__tests__/geneInfo.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import createFetchMock from 'vitest-fetch-mock'

import { setActivePinia, createPinia } from 'pinia'

import { useGeneInfoStore, StoreState } from '../geneInfo'
import { StoreState } from '../misc'
import { useGeneInfoStore } from '../geneInfo'

const fetchMocker = createFetchMock(vi)

Expand Down
33 changes: 33 additions & 0 deletions frontend/src/stores/__tests__/misc.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { beforeEach, describe, it, expect, vi } from 'vitest'
import createFetchMock from 'vitest-fetch-mock'

import { setActivePinia, createPinia } from 'pinia'

import { StoreState, useMiscStore } from '../misc'

const fetchMocker = createFetchMock(vi)

describe('miscInfo Store', () => {
beforeEach(() => {
setActivePinia(createPinia())
fetchMocker.enableMocks()
fetchMocker.resetMocks()
})

it('should have initial state', () => {
const store = useMiscStore()

expect(store.storeState).toBe(StoreState.Initial)
expect(store.appVersion).toBe(null)
})

it('should load data', async () => {
const store = useMiscStore()
fetchMocker.mockResponseOnce('v0.0.0')

await store.initialize()

expect(store.storeState).toBe(StoreState.Active)
expect(store.appVersion).toBe('v0.0.0')
})
})
8 changes: 1 addition & 7 deletions frontend/src/stores/geneInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,9 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'

import { StoreState } from '@/stores/misc'
import { AnnonarsClient } from '@/api/annonars'

export enum StoreState {
Initial = 'initial',
Loading = 'loading',
Active = 'active',
Error = 'error'
}

export const useGeneInfoStore = defineStore('geneInfo', () => {
// The current store state
const storeState = ref<StoreState>(StoreState.Initial)
Expand Down
43 changes: 43 additions & 0 deletions frontend/src/stores/misc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Store for misc info such as the current version.
*/

import { defineStore } from 'pinia'
import { ref } from 'vue'

import { MiscClient } from '@/api/misc'

export enum StoreState {
Initial = 'initial',
Loading = 'loading',
Active = 'active',
Error = 'error'
}

export const useMiscStore = defineStore('misc', () => {
// The current store state
const storeState = ref<StoreState>(StoreState.Initial)

// The app version.
const appVersion = ref<string | null>(null)

// Initialize store, load version.
const initialize = async () => {
storeState.value = StoreState.Loading
try {
const client = new MiscClient()
appVersion.value = await client.fetchVersion()

storeState.value = StoreState.Active
} catch (e) {
console.error('There was an error loading the app version.', e)
storeState.value = StoreState.Error
}
}

return {
storeState,
appVersion,
initialize
}
})
3 changes: 2 additions & 1 deletion frontend/src/views/GeneDetailView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import { watch, onMounted } from 'vue'
import { useRoute, useRouter } from 'vue-router'

import { StoreState, useGeneInfoStore } from '@/stores/geneInfo'
import { StoreState } from '@/stores/misc'
import { useGeneInfoStore } from '@/stores/geneInfo'

import HeaderDetailPage from '@/components/HeaderDetailPage.vue'
import { roundIt } from '@/api/utils'
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/views/__tests__/AboutView.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from '@/router'
import { createTestingPinia } from '@pinia/testing'

import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
Expand All @@ -28,7 +29,18 @@ const makeWrapper = () => {
},
{
global: {
plugins: [vuetify, router],
plugins: [
vuetify,
router,
createTestingPinia({
createSpy: vi.fn(),
initialState: {
misc: {
appVersion: 'v0.0.0'
}
}
})
],
components: {
AboutView
}
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/views/__tests__/ContactView.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { describe, expect, it, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import { createRouter, createWebHistory } from 'vue-router'
import { routes } from '@/router'
import { createTestingPinia } from '@pinia/testing'

import { createVuetify } from 'vuetify'
import * as components from 'vuetify/components'
Expand All @@ -28,7 +29,18 @@ const makeWrapper = () => {
},
{
global: {
plugins: [vuetify, router],
plugins: [
vuetify,
router,
createTestingPinia({
createSpy: vi.fn(),
initialState: {
misc: {
appVersion: 'v0.0.0'
}
}
})
],
components: {
ContactView
}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/views/__tests__/GeneDetailView.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as components from 'vuetify/components'
import * as directives from 'vuetify/directives'

import GeneDetailView from '../GeneDetailView.vue'
import { StoreState } from '@/stores/geneInfo'
import { StoreState } from '@/stores/misc'
import * as BRCA1geneInfo from '@/assets/__tests__/BRCA1GeneInfo.json'

const vuetify = createVuetify({
Expand Down
Loading
Loading