From 7ccd1af30fbf5dd641675183cef02d5bddf3400e Mon Sep 17 00:00:00 2001 From: Mackenzie Ian Frew Date: Mon, 8 May 2023 15:46:37 -0400 Subject: [PATCH 1/6] Fix typo in g4rdAdapter --- .../src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts b/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts index a4e8b511..cd9fc9e8 100644 --- a/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts +++ b/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts @@ -46,7 +46,7 @@ const _getG4rdNodeQuery = async ({ Authorization = await getAuthHeader(); } catch (e: any) { logger.error(e); - logger.error(e?.resoponse?.data); + logger.error(e?.response?.data); return { data: [], error: { code: 403, message: 'ERROR FETCHING OAUTH TOKEN', id: uuidv4() }, From 6cae9483574cc4379f2bea9be701936ef28ebe00 Mon Sep 17 00:00:00 2001 From: Mackenzie Ian Frew Date: Thu, 11 May 2023 13:14:58 -0400 Subject: [PATCH 2/6] Add CMH adapter --- .env.sample | 9 + docker-compose.prod.yaml | 8 + docker-compose.staging.yaml | 8 + docker-compose.yaml | 8 + .../adapters/cmhAdapter.ts | 303 ++++++++++++++++++ .../getVariantsResolver.ts | 3 + 6 files changed, 339 insertions(+) create mode 100644 server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts diff --git a/.env.sample b/.env.sample index c26c3a6a..0222e6e9 100644 --- a/.env.sample +++ b/.env.sample @@ -48,4 +48,13 @@ G4RD_AUTH_METHOD=bearer G4RD_TOKEN_URL=https://some-provider.example.com G4RD_AUTH0_BASE_URL=https://some-subdomain.auth0.com +CMH_AZURE_CLIENT_ID=replacethis_id +CMH_AZURE_CLIENT_SECRET=replacethis_secret +CMH_TOKEN_URL=https://replacethis.fakeurl +CMH_RESOURCE=replacethis_id +CMH_SCOPE=https://replacethis.fakeurl/something +CMH_GRANT_TYPE=client_credentials +CMH_GENE42_SECRET=replacethis_secret +CMH_URL=https://replacethis.fakeurl + SERVER_SESSION_SECRET=secret diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml index 9295e364..1ad12f6f 100644 --- a/docker-compose.prod.yaml +++ b/docker-compose.prod.yaml @@ -22,6 +22,14 @@ services: G4RD_GRANT_TYPE: G4RD_TOKEN_URL: G4RD_CLIENT_ID: + CMH_AZURE_CLIENT_ID: + CMH_AZURE_CLIENT_SECRET: + CMH_TOKEN_URL: + CMH_RESOURCE: + CMH_SCOPE: + CMH_GRANT_TYPE: + CMH_GENE42_SECRET: + CMH_URL: KEYCLOAK_AUTH_URL: KEYCLOAK_CLIENT_ID: KEYCLOAK_REALM: diff --git a/docker-compose.staging.yaml b/docker-compose.staging.yaml index 379d0e1d..fb32a9b1 100644 --- a/docker-compose.staging.yaml +++ b/docker-compose.staging.yaml @@ -21,6 +21,14 @@ services: G4RD_GRANT_TYPE: G4RD_TOKEN_URL: G4RD_CLIENT_ID: + CMH_AZURE_CLIENT_ID: + CMH_AZURE_CLIENT_SECRET: + CMH_TOKEN_URL: + CMH_RESOURCE: + CMH_SCOPE: + CMH_GRANT_TYPE: + CMH_GENE42_SECRET: + CMH_URL: KEYCLOAK_AUTH_URL: KEYCLOAK_CLIENT_ID: KEYCLOAK_REALM: diff --git a/docker-compose.yaml b/docker-compose.yaml index fcf5c142..9e2f97c5 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -37,6 +37,14 @@ services: G4RD_TOKEN_URL: G4RD_URL: G4RD_USERNAME: + CMH_AZURE_CLIENT_ID: + CMH_AZURE_CLIENT_SECRET: + CMH_TOKEN_URL: + CMH_RESOURCE: + CMH_SCOPE: + CMH_GRANT_TYPE: + CMH_GENE42_SECRET: + CMH_URL: KEYCLOAK_AUTH_URL: KEYCLOAK_REALM: KEYCLOAK_CLIENT_ID: ${KEYCLOAK_SERVER_CLIENT_ID} diff --git a/server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts b/server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts new file mode 100644 index 00000000..8c917b00 --- /dev/null +++ b/server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts @@ -0,0 +1,303 @@ +import axios, { AxiosError, AxiosResponse } from 'axios'; +import jwtDecode from 'jwt-decode'; +import { URLSearchParams } from 'url'; +import { v4 as uuidv4 } from 'uuid'; +import logger from '../../../logger'; +import { + ErrorTransformer, + IndividualResponseFields, + QueryInput, + ResultTransformer, + VariantQueryResponse, + VariantResponseFields, + G4RDFamilyQueryResult, + G4RDPatientQueryResult, + G4RDVariantQueryResult, + Disorder, + IndividualInfoFields, + PhenotypicFeaturesFields, + NonStandardFeature, + Feature, +} from '../../../types'; +import { getFromCache, putInCache } from '../../../utils/cache'; +import { timeit, timeitAsync } from '../../../utils/timeit'; +import resolveAssembly from '../utils/resolveAssembly'; + +/* eslint-disable camelcase */ + +/** + * CMH's PhenoTips instance should have the same format as G4RD. + * However, there's a different process in place for accessing it: + * - Request access token from Azure, + * - Provide token and Gene42 secret when querying CMH PT. + */ + +const SOURCE_NAME = 'cmh'; +const AZURE_BEARER_CACHE_KEY = 'cmhToken'; + +type CMHNodeQueryError = AxiosError; + +/** + * @param args VariantQueryInput + * @returns Promise + */ +const _getCMHNodeQuery = async ({ + input: { gene: geneInput, variant }, +}: QueryInput): Promise => { + let CMHNodeQueryError: CMHNodeQueryError | null = null; + let CMHVariantQueryResponse: null | AxiosResponse = null; + let CMHPatientQueryResponse: null | AxiosResponse = null; + const FamilyIds: null | Record = {}; // + let Authorization = ''; + try { + Authorization = await getAuthHeader(); + } catch (e: any) { + logger.error(e); + logger.error(e?.response?.data); + return { + data: [], + error: { code: 403, message: 'ERROR FETCHING OAUTH TOKEN', id: uuidv4() }, + source: SOURCE_NAME, + }; + } + const url = `${process.env.CMH_URL}/rest/variants/match`; + /* eslint-disable @typescript-eslint/no-unused-vars */ + const { position, ...gene } = geneInput; + variant.assemblyId = 'GRCh37'; + try { + CMHVariantQueryResponse = await axios.post( + url, + { + gene, + variant, + }, + { + headers: { + Authorization, + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Gene42-Secret': `${process.env.CMH_GENE42_SECRET}`, // + }, + } + ); + + // Get patients info + if (CMHVariantQueryResponse) { + let individualIds = CMHVariantQueryResponse.data.results + .map(v => v.individual.individualId!) + .filter(Boolean); // Filter out undefined and null values. + + // Get all unique individual Ids. + individualIds = [...new Set(individualIds)]; + + if (individualIds.length > 0) { + const patientUrl = `${process.env.CMH_URL}/rest/patients/fetch?${individualIds + .map(id => `id=${id}`) + .join('&')}`; + + CMHPatientQueryResponse = await axios.get( + new URL(patientUrl).toString(), + { + headers: { + Authorization, + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Gene42-Secret': `${process.env.CMH_GENE42_SECRET}`, + }, + } + ); + + // Get Family Id for each patient. + const patientFamily = axios.create({ + headers: { + Authorization, + 'Content-Type': 'application/json', + Accept: 'application/json', + 'X-Gene42-Secret': `${process.env.CMH_GENE42_SECRET}`, + }, + }); + + const familyResponses = await Promise.allSettled( + individualIds.map(id => + patientFamily.get( + new URL(`${process.env.CMH_URL}/rest/patients/${id}/family`).toString() + ) + ) + ); + + familyResponses.forEach((response, index) => { + if (response.status === 'fulfilled' && response.value.status === 200) { + FamilyIds[individualIds[index]] = response.value.data.id; + } + }); + } + } + } catch (e: any) { + logger.error(e); + CMHNodeQueryError = e; + } + + return { + data: transformCMHQueryResponse( + (CMHVariantQueryResponse?.data as G4RDVariantQueryResult) || [], + (CMHPatientQueryResponse?.data as G4RDPatientQueryResult) || [], + FamilyIds + ), + error: transformCMHNodeErrorResponse(CMHNodeQueryError), + source: SOURCE_NAME, + }; +}; + +/** + * @param args VariantQueryInput + * @returns Promise + */ +const getCMHNodeQuery = timeitAsync('getCMHNodeQuery')(_getCMHNodeQuery); + +const getAuthHeader = async () => { + const { + CMH_AZURE_CLIENT_ID: client_id, + CMH_AZURE_CLIENT_SECRET: client_secret, + CMH_TOKEN_URL, + CMH_RESOURCE: resource, + CMH_SCOPE: scope, + CMH_GRANT_TYPE: grant_type, + } = process.env; + const cachedToken = getFromCache(AZURE_BEARER_CACHE_KEY); + if (cachedToken) { + return `Bearer ${cachedToken}`; + } + + const params = new URLSearchParams({ + client_id, + client_secret, + resource, + scope, + grant_type, + } as Record); + + const tokenResponse = await axios.post<{ access_token: string }>(CMH_TOKEN_URL!, params, { + headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: '*/*' }, + }); + const token = tokenResponse.data.access_token; + const decoded = jwtDecode<{ iat: number; exp: number }>(token); + const ttl = decoded.exp - Date.now() / 1000; + putInCache(AZURE_BEARER_CACHE_KEY, token, ttl); + return `Bearer ${token}`; +}; + +export const transformCMHNodeErrorResponse: ErrorTransformer = error => { + if (!error) { + return undefined; + } else { + return { + id: uuidv4(), + code: error.response?.status || 500, + message: + error.response?.status === 404 + ? 'No variants found matching your query.' + : error.response?.statusText, + }; + } +}; + +const isObserved = (feature: Feature | NonStandardFeature) => + feature.observed === 'yes' ? true : feature.observed === 'no' ? false : undefined; + +export const transformCMHQueryResponse: ResultTransformer = timeit( + 'transformCMHQueryResponse' +)( + ( + variantResponse: G4RDVariantQueryResult, + patientResponse: G4RDPatientQueryResult[], + familyIds: Record + ) => { + const individualIdsMap = Object.fromEntries(patientResponse.map(p => [p.id, p])); + + return (variantResponse.results || []).map(r => { + /* eslint-disable @typescript-eslint/no-unused-vars */ + r.variant.assemblyId = resolveAssembly(r.variant.assemblyId); + const { individual, contactInfo } = r; + + const patient = individual.individualId ? individualIdsMap[individual.individualId] : null; + + let info: IndividualInfoFields = {}; + let ethnicity: string = ''; + let disorders: Disorder[] = []; + let phenotypicFeatures: PhenotypicFeaturesFields[] = individual.phenotypicFeatures || []; + + if (patient) { + const candidateGene = (patient.genes ?? []).map(g => g.gene).join('\n'); + const classifications = (patient.genes ?? []).map(g => g.status).join('\n'); + const diagnosis = patient.clinicalStatus; + const solved = patient.solved ? patient.solved.status : ''; + const clinicalStatus = patient.clinicalStatus; + disorders = patient.disorders.filter(({ label }) => label !== 'affected') as Disorder[]; + ethnicity = Object.values(patient.ethnicity) + .flat() + .map(p => p.trim()) + .join(', '); + info = { + solved, + candidateGene, + diagnosis, + classifications, + clinicalStatus, + disorders, + }; + // variant response contains all phenotypic features listed, + // even if some of them are explicitly _not_ observed by clinician and recorded as such + if (individual.phenotypicFeatures !== null && individual.phenotypicFeatures !== undefined) { + const features = [...(patient.features ?? []), ...(patient.nonstandard_features ?? [])]; + const detailedFeatures = individual.phenotypicFeatures; + // build list of features the safe way + const detailedFeatureMap = Object.fromEntries( + detailedFeatures.map(feat => [feat.phenotypeId, feat]) + ); + const finalFeatures: PhenotypicFeaturesFields[] = features.map(feat => { + if (feat.id === undefined) { + return { + ageOfOnset: null, + dateOfOnset: null, + levelSeverity: null, + onsetType: null, + phenotypeId: feat.id, + phenotypeLabel: feat.label, + observed: isObserved(feat), + }; + } + return { + ...detailedFeatureMap[feat.id], + observed: isObserved(feat), + }; + }); + phenotypicFeatures = finalFeatures; + } + } + + const variant: VariantResponseFields = { + alt: r.variant.alt, + assemblyId: r.variant.assemblyId, + callsets: r.variant.callsets, + end: r.variant.end, + ref: r.variant.ref, + start: r.variant.start, + chromosome: r.variant.chromosome, + }; + + let familyId: string = ''; + if (individual.individualId) familyId = familyIds[individual.individualId]; + + const individualResponseFields: IndividualResponseFields = { + ...individual, + ethnicity, + info, + familyId, + phenotypicFeatures, + }; + return { individual: individualResponseFields, variant, contactInfo, source: SOURCE_NAME }; + }); + } +); + +export default getCMHNodeQuery; diff --git a/server/src/resolvers/getVariantsResolver/getVariantsResolver.ts b/server/src/resolvers/getVariantsResolver/getVariantsResolver.ts index b0072629..21d5a904 100644 --- a/server/src/resolvers/getVariantsResolver/getVariantsResolver.ts +++ b/server/src/resolvers/getVariantsResolver/getVariantsResolver.ts @@ -18,6 +18,7 @@ import liftover from './utils/liftOver'; import { QueryResponseError } from './utils/queryResponseError'; import getG4rdNodeQuery from './adapters/g4rdAdapter'; import { timeitAsync } from '../../utils/timeit'; +import getCMHNodeQuery from './adapters/cmhAdapter'; const getVariants = async (parent: any, args: QueryInput): Promise => await resolveVariantQuery(args); @@ -144,6 +145,8 @@ const buildSourceQuery = timeitAsync('buildSourceQuery')( return getRemoteTestNodeQuery(args); case 'g4rd': return getG4rdNodeQuery(args); + case 'cmh': + return getCMHNodeQuery(args); default: throw new Error(`source ${source} not found!`); } From 7030985b19285ce468abafd4560c3b7bc309f014 Mon Sep 17 00:00:00 2001 From: Mackenzie Ian Frew Date: Thu, 11 May 2023 15:48:45 -0400 Subject: [PATCH 3/6] Add source prefixes; fix error messages --- react/src/utils/tableHelpers.ts | 12 ++++++++++++ .../getVariantsResolver/adapters/cmhAdapter.ts | 2 +- .../getVariantsResolver/adapters/g4rdAdapter.ts | 4 ++-- .../adapters/remoteTestNodeAdapter.ts | 2 +- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/react/src/utils/tableHelpers.ts b/react/src/utils/tableHelpers.ts index d33838ae..68b18581 100644 --- a/react/src/utils/tableHelpers.ts +++ b/react/src/utils/tableHelpers.ts @@ -248,6 +248,18 @@ export const prepareData = ( row.maleCount = currMaleCount; }); + // update individualId, familyId with source prefix + // (P0001 from G4RD is not the same as P0001 from CMH) + result.map(r => { + if (r.individualId) { + r.individualId = [r.source, r.individualId].join('_'); + } + if (r.familyId) { + r.familyId = [r.source, r.familyId].join('_'); + } + return r; + }); + // Remove duplicate variants for the same patient const uniquePatientVariants = result.filter( (arr, index, self) => diff --git a/server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts b/server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts index 8c917b00..da3deca3 100644 --- a/server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts +++ b/server/src/resolvers/getVariantsResolver/adapters/cmhAdapter.ts @@ -53,7 +53,7 @@ const _getCMHNodeQuery = async ({ Authorization = await getAuthHeader(); } catch (e: any) { logger.error(e); - logger.error(e?.response?.data); + logger.error(JSON.stringify(e?.response?.data)); return { data: [], error: { code: 403, message: 'ERROR FETCHING OAUTH TOKEN', id: uuidv4() }, diff --git a/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts b/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts index cd9fc9e8..f306d496 100644 --- a/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts +++ b/server/src/resolvers/getVariantsResolver/adapters/g4rdAdapter.ts @@ -46,7 +46,7 @@ const _getG4rdNodeQuery = async ({ Authorization = await getAuthHeader(); } catch (e: any) { logger.error(e); - logger.error(e?.response?.data); + logger.error(JSON.stringify(e?.response?.data)); return { data: [], error: { code: 403, message: 'ERROR FETCHING OAUTH TOKEN', id: uuidv4() }, @@ -165,7 +165,7 @@ const getAuthHeader = async () => { } const params = new URLSearchParams({ - client_id: client_id, + client_id, grant_type, password, realm, diff --git a/server/src/resolvers/getVariantsResolver/adapters/remoteTestNodeAdapter.ts b/server/src/resolvers/getVariantsResolver/adapters/remoteTestNodeAdapter.ts index 8bc8c205..c88b6d3e 100644 --- a/server/src/resolvers/getVariantsResolver/adapters/remoteTestNodeAdapter.ts +++ b/server/src/resolvers/getVariantsResolver/adapters/remoteTestNodeAdapter.ts @@ -140,7 +140,7 @@ export const transformRemoteTestNodeErrorResponse: ErrorTransformer< return { id: uuidv4(), code: error.response?.status || 500, - message: error.response?.data, + message: JSON.stringify(error.response?.data), }; } }; From c4bc4278f7878bd99a69ff3bbb918c19fad4e1ed Mon Sep 17 00:00:00 2001 From: Mackenzie Ian Frew Date: Thu, 11 May 2023 15:51:01 -0400 Subject: [PATCH 4/6] Update docs --- docs/production.md | 2 +- docs/staging.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/production.md b/docs/production.md index b2af21f1..be57e740 100644 --- a/docs/production.md +++ b/docs/production.md @@ -1,6 +1,6 @@ # Production -OSMP is deployed via Docker Compose at [https://osmp.genomics4rd.ca](https://osmp.genomics4rd.ca). The key differences between the staging and production are the removal of the test node and increased memory and CPU resource allocations for the `server` container. The OSMP production instance is connected to the [production instance of G4RD Phenotips](https://phenotips.genomics4rd.ca). Similar to the staging stack, to deploy the frontend, compiled static bundles are uploaded to a designated MinIO bucket. The routing between the frontend and backend is handled by the HAProxy reverse proxy. User accounts are managed in [Keycloak](https://keycloak.genomics4rd.ca). +OSMP is deployed via Docker Compose at [https://osmp.genomics4rd.ca](https://osmp.genomics4rd.ca). The key differences between the staging and production are the removal of the test node and increased memory and CPU resource allocations for the `server` container. The OSMP production instance is connected to the [production instance of G4RD Phenotips](https://phenotips.genomics4rd.ca) and to the [production instance of CMH Phenotips](https://phenotips-ga4k.cmh.edu). Similar to the staging stack, to deploy the frontend, compiled static bundles are uploaded to a designated MinIO bucket. The routing between the frontend and backend is handled by the HAProxy reverse proxy. User accounts are managed in [Keycloak](https://keycloak.genomics4rd.ca). ## Continuous deployment through Github Actions diff --git a/docs/staging.md b/docs/staging.md index 4fbd8de6..256a0985 100644 --- a/docs/staging.md +++ b/docs/staging.md @@ -1,6 +1,6 @@ # Staging -OSMP is deployed via Docker Compose at [https://osmp.ccmdev.ca/](https://osmp.ccmdev.ca/). For G4RD data source, the OSMP staging instance is connected to the [staging instance of G4RD Phenotips](https://staging.phenotips.genomics4rd.ca). To deploy the frontend, compiled static bundles are uploaded to a designated MinIO bucket. The routing between the frontend and backend is handled by the HAProxy reverse proxy. User accounts are managed in [Keycloak](https://keycloak.ccmdev.ca). +OSMP is deployed via Docker Compose at [https://osmp.ccmdev.ca/](https://osmp.ccmdev.ca/). For G4RD data source, the OSMP staging instance is connected to the [staging instance of G4RD Phenotips](https://staging.phenotips.genomics4rd.ca) and the [staging instance of CMH Phenotips](https://phenotipstest-ga4k.cmh.edu). To deploy the frontend, compiled static bundles are uploaded to a designated MinIO bucket. The routing between the frontend and backend is handled by the HAProxy reverse proxy. User accounts are managed in [Keycloak](https://keycloak.ccmdev.ca). ## Continuous deployment through Github Actions From 3a113dcbfe8cf7e9c281e04a8b0440c4a9488016 Mon Sep 17 00:00:00 2001 From: Mackenzie Ian Frew Date: Thu, 11 May 2023 16:23:16 -0400 Subject: [PATCH 5/6] Update staging server deploy with CMH vars --- .github/workflows/node.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/node.yml b/.github/workflows/node.yml index 3699332c..0116cf01 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/node.yml @@ -163,6 +163,14 @@ jobs: G4RD_GRANT_TYPE: ${{ secrets.G4RD_GRANT_TYPE }} G4RD_TOKEN_URL: ${{ secrets.G4RD_TOKEN_URL }} G4RD_CLIENT_ID: ${{ secrets.G4RD_CLIENT_ID }} + CMH_AZURE_CLIENT_ID: ${{ secrets.CMH_AZURE_CLIENT_ID }} + CMH_AZURE_CLIENT_SECRET: ${{ secrets.CMH_AZURE_CLIENT_SECRET }} + CMH_TOKEN_URL: ${{ secrets.CMH_TOKEN_URL }} + CMH_RESOURCE: ${{ secrets.CMH_RESOURCE }} + CMH_SCOPE: ${{ secrets.CMH_SCOPE }} + CMH_GRANT_TYPE: ${{ secrets.CMH_GRANT_TYPE }} + CMH_GENE42_SECRET: ${{ secrets.CMH_GENE42_SECRET }} + CMH_URL: ${{ secrets.CMH_URL }} KEYCLOAK_AUTH_URL: ${{ secrets.KEYCLOAK_AUTH_URL }} KEYCLOAK_REALM: ${{ secrets.KEYCLOAK_REALM }} KEYCLOAK_CLIENT_ID: ${{ secrets.KEYCLOAK_CLIENT_ID }} From b2a0b5494615c2d758028d984e94d6fbc333ae88 Mon Sep 17 00:00:00 2001 From: Mackenzie Ian Frew Date: Mon, 15 May 2023 14:08:46 -0400 Subject: [PATCH 6/6] Remove cmh env vars from docker prod (for now) --- docker-compose.prod.yaml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docker-compose.prod.yaml b/docker-compose.prod.yaml index 1ad12f6f..9295e364 100644 --- a/docker-compose.prod.yaml +++ b/docker-compose.prod.yaml @@ -22,14 +22,6 @@ services: G4RD_GRANT_TYPE: G4RD_TOKEN_URL: G4RD_CLIENT_ID: - CMH_AZURE_CLIENT_ID: - CMH_AZURE_CLIENT_SECRET: - CMH_TOKEN_URL: - CMH_RESOURCE: - CMH_SCOPE: - CMH_GRANT_TYPE: - CMH_GENE42_SECRET: - CMH_URL: KEYCLOAK_AUTH_URL: KEYCLOAK_CLIENT_ID: KEYCLOAK_REALM: