diff --git a/package-lock.json b/package-lock.json index 9a55c79d..28ec0ff3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "server", - "version": "3.42.1", + "version": "3.43.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "server", - "version": "3.42.1", + "version": "3.43.0", "license": "ISC", "dependencies": { "@arranger/server": "^2.18.2", diff --git a/package.json b/package.json index 9c6bb0ba..4a456c19 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "server", - "version": "3.42.1", + "version": "3.43.0", "description": "", "main": "index.js", "scripts": { diff --git a/src/config.ts b/src/config.ts index be12a79f..73a54e64 100644 --- a/src/config.ts +++ b/src/config.ts @@ -23,8 +23,9 @@ export const ADVERTISED_HOST = process.env.ADVERTISED_HOST || 'http://localhost: // Elasticsearch config export const ELASTICSEARCH_HOST = process.env.ELASTICSEARCH_HOST || 'http://localhost:9200'; -export const ELASTICSEARCH_VAULT_SECRET_PATH = process.env - .ELASTICSEARCH_VAULT_SECRET_PATH as string; +export const ELASTICSEARCH_VAULT_SECRET_PATH = + process.env.ELASTICSEARCH_VAULT_SECRET_PATH || + 'Missing env variable ELASTICSEARCH_VAULT_SECRET_PATH'; export const ELASTICSEARCH_USERNAME = process.env.ELASTICSEARCH_USERNAME; export const ELASTICSEARCH_PASSWORD = process.env.ELASTICSEARCH_PASSWORD; export const ELASTICSEARCH_CLIENT_TRUST_SSL_CERT = @@ -44,28 +45,27 @@ export const ARRANGER_PROJECT_ID = process.env.ARRANGER_PROJECT_ID || 'argo'; export const EGO_ROOT_REST = process.env.EGO_ROOT_REST || 'http://localhost:8081'; export const EGO_ROOT_GRPC = process.env.EGO_ROOT_GRPC || 'localhost:50051'; export const EGO_DACO_POLICY_NAME = process.env.EGO_DACO_POLICY_NAME || 'DACO'; -export const EGO_VAULT_SECRET_PATH = process.env.EGO_VAULT_SECRET_PATH as string; +export const EGO_VAULT_SECRET_PATH = + process.env.EGO_VAULT_SECRET_PATH || 'Missing env variable EGO_VAULT_SECRET_PATH'; export const EGO_CLIENT_ID = process.env.EGO_CLIENT_ID; export const EGO_CLIENT_SECRET = process.env.EGO_CLIENT_SECRET; // Ego Credentials for Score Proxy -export const EGO_VAULT_SCORE_PROXY_SECRET_PATH = process.env - .EGO_VAULT_SCORE_PROXY_SECRET_PATH as string; +export const EGO_VAULT_SCORE_PROXY_SECRET_PATH = process.env.EGO_VAULT_SCORE_PROXY_SECRET_PATH; export const EGO_SCORE_PROXY_CLIENT_ID = process.env.EGO_SCORE_PROXY_CLIENT_ID; export const EGO_SCORE_PROXY_CLIENT_SECRET = process.env.EGO_SCORE_PROXY_CLIENT_SECRET; // Ego Credentials for Clinical API -export const EGO_VAULT_CLINICAL_API_SECRET_PATH = process.env - .EGO_VAULT_CLINICAL_API_SECRET_PATH as string; +export const EGO_VAULT_CLINICAL_API_SECRET_PATH = process.env.EGO_VAULT_CLINICAL_API_SECRET_PATH; export const EGO_CLINICAL_API_CLIENT_ID = process.env.EGO_CLINICAL_API_CLIENT_ID; export const EGO_CLINICAL_API_CLIENT_SECRET = process.env.EGO_CLINICAL_API_CLIENT_SECRET; // Vault export const USE_VAULT = process.env.USE_VAULT === 'true'; -export const VAULT_TOKEN = process.env.VAULT_TOKEN as string; -export const VAULT_AUTH_METHOD = process.env.VAULT_AUTH_METHOD as 'token' | 'kubernetes'; -export const VAULT_URL = (process.env.VAULT_URL as string) || 'http://localhost:8200'; -export const VAULT_ROLE = process.env.VAULT_ROLE as string; +export const VAULT_TOKEN = process.env.VAULT_TOKEN; +export const VAULT_AUTH_METHOD = process.env.VAULT_AUTH_METHOD; +export const VAULT_URL = process.env.VAULT_URL || 'http://localhost:8200'; +export const VAULT_ROLE = process.env.VAULT_ROLE; // Default ego public key value is the example value provided in the application.yml of the public overture repository export const EGO_PUBLIC_KEY = @@ -85,8 +85,9 @@ export const DATA_CENTER_REGISTRY_API_ROOT = export const APP_DIR = __dirname; // Helpdesk auth -export const JIRA_ADMIN_VAULT_CREDENTIALS_PATH = process.env - .JIRA_ADMIN_VAULT_CREDENTIALS_PATH as string; +export const JIRA_ADMIN_VAULT_CREDENTIALS_PATH = + process.env.JIRA_ADMIN_VAULT_CREDENTIALS_PATH || + 'Missing env variable JIRA_ADMIN_VAULT_CREDENTIALS_PATH'; export const JIRA_REST_URI = process.env.JIRA_REST_URI || 'https://extsd.oicr.on.ca/rest/servicedeskapi'; export const JIRA_SERVICEDESK_ID = process.env.JIRA_SERVICEDESK_ID || '9'; diff --git a/src/resources/Ego.proto b/src/resources/Ego.proto deleted file mode 100644 index 8d082ee8..00000000 --- a/src/resources/Ego.proto +++ /dev/null @@ -1,59 +0,0 @@ -syntax = "proto3"; -import "google/protobuf/wrappers.proto"; - -option java_multiple_files = true; -option java_package = "bio.overture.ego.grpc"; -option java_outer_classname = "EgoProto"; - -package bio.overture.ego.grpc; - -service UserService { - rpc GetUser (GetUserRequest) returns (User) {} - rpc ListUsers (ListUsersRequest) returns (ListUsersResponse) {} -} - -message PagedRequest { - uint32 page_number = 1; - uint32 page_size = 2; - string order_by = 3; -} - -message PagedResponse { - uint32 max_results = 1; - google.protobuf.UInt32Value next_page = 2; -} - -message GetUserRequest { - string id = 1; -} - -message ListUsersRequest { - PagedRequest page = 1; - - google.protobuf.StringValue query = 2; - repeated string group_ids = 3; -} - -message ListUsersResponse { - PagedResponse page = 1; - - repeated User users = 2; -} - -message User { - google.protobuf.StringValue id = 1; - google.protobuf.StringValue email = 2; - google.protobuf.StringValue first_name = 3; - google.protobuf.StringValue last_name = 4; - - google.protobuf.StringValue created_at = 5; - google.protobuf.StringValue last_login = 6; - google.protobuf.StringValue name = 7; - google.protobuf.StringValue preferred_language = 8; - google.protobuf.StringValue status = 9; - google.protobuf.StringValue type = 10; - - repeated string applications = 11; - repeated string groups = 12; - repeated string scopes = 13; -} \ No newline at end of file diff --git a/src/schemas/Clinical/index.ts b/src/schemas/Clinical/index.ts index 4862e1f1..0280ee47 100644 --- a/src/schemas/Clinical/index.ts +++ b/src/schemas/Clinical/index.ts @@ -469,8 +469,6 @@ const resolvers = { ) => { const { Authorization } = context; - console.log('clinical errors'); - const errorResponse: ClinicalErrors = await clinicalService.getClinicalErrors( args.programShortName, args.donorIds, diff --git a/src/schemas/Program/index.js b/src/schemas/Program/index.js index 60d589f3..7674bad6 100644 --- a/src/schemas/Program/index.js +++ b/src/schemas/Program/index.js @@ -23,7 +23,6 @@ import { get, merge, pickBy } from 'lodash'; import customScalars from 'schemas/customScalars'; import programService from 'services/programService'; -import { grpcToGql } from 'utils/grpcUtils'; const typeDefs = gql` scalar DateTime @@ -72,6 +71,7 @@ const typeDefs = gql` regions: [String] cancerTypes: [String] primarySites: [String] + dataCenter: DataCenter membershipType: MembershipType @@ -96,6 +96,22 @@ const typeDefs = gql` countries: [String]! } + type DataCenter { + id: ID + shortName: String! + name: String + organization: String + email: String + uiUrl: String + gatewayUrl: String + analysisSongCode: String + analysisSongUrl: String + analysisScoreUrl: String + submissionSongCode: String + submissionSongUrl: String + submissionScoreUrl: String + } + input ProgramUserInput { email: String! firstName: String! @@ -161,7 +177,7 @@ const typeDefs = gql` """ retrieve all Programs """ - programs: [Program] + programs(dataCenter: String): [Program] """ retrieve join program invitation by id @@ -169,6 +185,11 @@ const typeDefs = gql` joinProgramInvite(id: ID!): JoinProgramInvite programOptions: ProgramOptions! + + """ + retrieve all DataCenters + """ + dataCenters(shortName: String): [DataCenter] } type Mutation { @@ -212,7 +233,7 @@ const typeDefs = gql` `; /* ========= - Convert GRPC Response to GQL output +HTTP resolvers * ========= */ const getIsoDate = (time) => (time ? new Date(parseInt(time) * 1000).toISOString() : null); @@ -242,33 +263,19 @@ const convertGrpcUserToGql = (userDetails) => ({ inviteAcceptedAt: getIsoDate(get(userDetails, 'accepted_at.seconds')), }); -const formatHttpProgram = (program) => ({ - name: program.name, - shortName: program.shortName, - description: program.description, - website: program.website, - institutions: program.programInstitutions?.map((institution) => institution.name) || [], - countries: program.programCountries?.map((country) => country.name) || [], - regions: program.processingRegions?.map((region) => region.name) || [], - cancerTypes: program.programCancers?.map((cancer) => cancer.name) || [], - primarySites: program.programPrimarySites?.map((primarySite) => primarySite.name) || [], -}); - -const resolveProgramList = async (egoToken) => { - const response = await programService.listPrograms(egoToken); - const programs = get(response, 'programs', []); - return programs.map((program) => convertGrpcProgramToGql(program)); +const resolvePrivateProgramList = async (egoToken) => { + const response = await programService.listPrivatePrograms(egoToken); + return response || null; }; -const resolveSingleProgram = async (egoToken, programShortName) => { - const response = await programService.getProgram(programShortName, egoToken); - const programDetails = get(response, 'program'); - return response ? convertGrpcProgramToGql(programDetails) : null; +const resolvePrivateSingleProgram = async (egoToken, programShortName) => { + const response = await programService.getPrivateProgram(egoToken, programShortName); + return response || null; }; -const resolveHTTPProgram = async (programShortName) => { - const response = await programService.getProgramPublicFields(programShortName); - return response ? formatHttpProgram(response) : null; +const resolvePublicSingleProgram = async (programShortName) => { + const response = await programService.getPublicProgram(programShortName); + return response || null; }; const programServicePrivateFields = [ @@ -277,6 +284,7 @@ const programServicePrivateFields = [ 'genomicDonors', 'membershipType', 'users', + 'dataCenter', ]; const resolvers = { @@ -339,21 +347,35 @@ const resolvers = { ); return hasPrivateField - ? resolveSingleProgram(egoToken, shortName) - : resolveHTTPProgram(shortName); + ? resolvePrivateSingleProgram(egoToken, shortName) + : resolvePublicSingleProgram(shortName); }, - programs: async (obj, args, context, info) => { + programs: async (obj, args, context) => { const { egoToken } = context; - return resolveProgramList(egoToken); + const { dataCenter } = args; + + const programs = await resolvePrivateProgramList(egoToken); + + const filteredPrograms = dataCenter + ? programs.filter((program) => program.dataCenter?.shortName === dataCenter) + : programs; + + return filteredPrograms; }, + joinProgramInvite: async (obj, args, context, info) => { const { egoToken } = context; - const response = await programService.getJoinProgramInvite(args.id, egoToken); - const joinProgramDetails = get(response, 'invitation'); - return response ? grpcToGql(joinProgramDetails) : null; + const response = await programService.getJoinProgramInvite(egoToken, args.id); + return response || null; }, programOptions: () => ({}), + dataCenters: async (obj, args, context, info) => { + const { egoToken } = context; + const shortName = get(args, 'shortName', null); + const response = await programService.listDataCenters(shortName, egoToken); + return response || null; + }, }, Mutation: { createProgram: async (obj, args, context, info) => { @@ -368,7 +390,7 @@ const resolvers = { try { const createResponse = await programService.createProgram(program, egoToken); - return resolveSingleProgram(egoToken, program.shortName); + return resolvePrivateSingleProgram(egoToken, program.shortName); } catch (err) { const GRPC_INVALID_ARGUMENT_ERROR_CODE = 3; if (err.code === GRPC_INVALID_ARGUMENT_ERROR_CODE) { diff --git a/src/schemas/User/index.ts b/src/schemas/User/index.ts index 515857bd..6f00d753 100644 --- a/src/schemas/User/index.ts +++ b/src/schemas/User/index.ts @@ -19,12 +19,11 @@ import { gql } from 'apollo-server-express'; import { makeExecutableSchema } from 'graphql-tools'; -import get from 'lodash/get'; -import { EgoClient, EgoGrpcUser, ListUserSortOptions } from '../../services/ego'; -import { EGO_DACO_POLICY_NAME } from '../../config'; -import egoTokenUtils from 'utils/egoTokenUtils'; import { GlobalGqlContext } from 'app'; +import egoTokenUtils from 'utils/egoTokenUtils'; +import { EGO_DACO_POLICY_NAME } from '../../config'; +import { EgoClient } from '../../services/ego'; import logger from '../../utils/logger'; // Construct a schema, using GraphQL schema language @@ -69,16 +68,6 @@ const typeDefs = gql` } type Query { - """ - retrieve User data by id - """ - user(id: String!): User - - """ - retrieve paginated list of user data - """ - users(pageNum: Int, limit: Int, sort: String, groups: [String], query: String): [User] - """ retrive user profile data """ @@ -93,22 +82,6 @@ const typeDefs = gql` } `; -const convertEgoUser = (user: EgoGrpcUser) => ({ - id: get(user, 'id.value'), - email: get(user, 'email.value'), - firstName: get(user, 'first_name.value'), - lastName: get(user, 'last_name.value'), - createdAt: get(user, 'created_at.value'), - lastLogin: get(user, 'last_login.value'), - name: get(user, 'name.value'), - preferredLanguage: get(user, 'preferred_language.value'), - status: get(user, 'status.value'), - type: get(user, 'type.value'), - applications: get(user, 'applications'), - groups: get(user, 'groups'), - scopes: get(user, 'scopes'), -}); - const createProfile = ({ apiKey, isDacoApproved, @@ -124,20 +97,6 @@ const createProfile = ({ const createResolvers = (egoClient: EgoClient) => { return { Query: { - user: async (obj: unknown, args: { id: string }, context: GlobalGqlContext) => { - const { egoToken } = context; - const egoUser: EgoGrpcUser = await egoClient.getUser(args.id, egoToken); - return egoUser === null ? null : convertEgoUser(egoUser); - }, - users: async (obj: unknown, args: ListUserSortOptions, context: GlobalGqlContext) => { - const { egoToken } = context; - const options = { - ...args, - }; - const response = await egoClient.listUsers(options, egoToken); - const egoUserList: EgoGrpcUser[] = get(response, 'users', []); - return egoUserList.map((egoUser) => convertEgoUser(egoUser)); - }, self: async (obj: unknown, args: undefined, context: GlobalGqlContext) => { const { Authorization, egoToken, userJwtData } = context; logger.info({ Authorization, egoToken, userJwtData }); diff --git a/src/services/ego/index.ts b/src/services/ego/index.ts index fbc6cf1f..fba8f31f 100644 --- a/src/services/ego/index.ts +++ b/src/services/ego/index.ts @@ -21,43 +21,14 @@ * This file dynamically generates a gRPC client from Ego.proto. * The content of Ego.proto is copied directly from: https://github.com/overture-stack/ego/blob/develop/src/main/proto/Ego.proto */ -import path from 'path'; - -import * as loader from '@grpc/proto-loader'; -import grpc, { ChannelCredentials } from 'grpc'; import memoize from 'lodash/memoize'; import fetch from 'node-fetch'; import urlJoin from 'url-join'; -import { APP_DIR, EGO_DACO_POLICY_NAME, EGO_ROOT_GRPC, EGO_ROOT_REST } from '../../config'; -import { defaultPromiseCallback, getAuthMeta, withRetries } from '../../utils/grpcUtils'; +import { EGO_DACO_POLICY_NAME, EGO_ROOT_REST } from '../../config'; import logger from '../../utils/logger'; import { restErrorResponseHandler } from '../../utils/restUtils'; -export type EgoGrpcUser = { - id: { value: unknown }; - email: { value: unknown }; - first_name: { value: unknown }; - last_name: { value: unknown }; - created_at: { value: unknown }; - last_login: { value: unknown }; - name: { value: unknown }; - preferred_language: { value: unknown }; - status: { value: unknown }; - type: { value: unknown }; - applications: unknown; - groups: unknown; - scopes: unknown; -}; - -export type ListUserSortOptions = { - pageNum?: number; - limit?: number; - sort?: string; - groups?: string[]; - query?: string; -}; - export type EgoApplicationCredential = { clientId: string; clientSecret: string; @@ -88,65 +59,12 @@ const createEgoClient = (applicationCredential: EgoApplicationCredential) => { `${applicationCredential.clientId}:${applicationCredential.clientSecret}`, ).toString('base64'); - const PROTO_PATH = path.join(APP_DIR, '/resources/Ego.proto'); - const EGO_API_KEY_ENDPOINT = urlJoin(EGO_ROOT_REST, '/o/api_key'); - const packageDefinition = loader.loadSync(PROTO_PATH, { - keepCase: true, - longs: String, - enums: String, - defaults: true, - oneofs: true, - }); - - const protoBio = grpc.loadPackageDefinition(packageDefinition).bio as { - overture: { - ego: { - grpc: { - UserService: new (grpc_root: string, credentials: ChannelCredentials) => any; - }; - }; - }; - }; - const proto = protoBio.overture.ego.grpc; - - const userService = withRetries( - new proto.UserService(EGO_ROOT_GRPC, grpc.credentials.createInsecure()), - ); let memoizedGetDacoIds: ReturnType | null = null; let dacoIdsCalled = new Date(); const dacoGroupIdExpiry = 86400000; // 24hours - const getUser = async (id: string, jwt: string | null = null): Promise => { - return await new Promise((resolve, reject) => { - userService.getUser( - { id }, - getAuthMeta(jwt), - defaultPromiseCallback(resolve, reject, 'Ego.getUser'), - ); - }); - }; - - const listUsers = async ( - { pageNum, limit, sort, groups, query }: ListUserSortOptions = {}, - jwt: string | null = null, - ) => { - const payload = { - page: { page_number: pageNum, page_size: limit, sort }, - group_ids: groups, - query: { value: query }, - }; - - return await new Promise((resolve, reject) => { - userService.listUsers( - payload, - getAuthMeta(jwt), - defaultPromiseCallback(resolve, reject, 'Ego.listUsers'), - ); - }); - }; - type EgoAccessKeyObj = { name: string; expiryDate: string; @@ -160,10 +78,6 @@ const createEgoClient = (applicationCredential: EgoApplicationCredential) => { userId: string, Authorization: string, ): Promise => { - type EgoApiKeyResponse = { - count: number; - resultSet: EgoAccessKeyObj[]; - }; const firstResponse = await fetch(urlJoin(EGO_API_KEY_ENDPOINT, `?user_id=${userId}`), { headers: { Authorization }, }) @@ -233,7 +147,7 @@ const createEgoClient = (applicationCredential: EgoApplicationCredential) => { method: 'delete', headers: { Authorization }, }) - .then((resp) => ({ key, success: true })) + .then((_) => ({ key, success: true })) .catch((err) => { logger.error(err); return { key, success: false }; @@ -326,8 +240,6 @@ const createEgoClient = (applicationCredential: EgoApplicationCredential) => { }; return { - getUser, - listUsers, generateEgoAccessKey, getScopes, getEgoAccessKeys, diff --git a/src/services/programService/httpClient.js b/src/services/programService/httpClient.js index 61ab3d4d..2d22ef6f 100644 --- a/src/services/programService/httpClient.js +++ b/src/services/programService/httpClient.js @@ -23,18 +23,156 @@ */ import fetch from 'node-fetch'; +import urljoin from 'url-join'; + +import logger from 'utils/logger'; import { PROGRAM_SERVICE_HTTP_ROOT } from '../../config'; import { restErrorResponseHandler } from '../../utils/restUtils'; -const getProgramPublicFields = async (programShortName) => { - const url = `${PROGRAM_SERVICE_HTTP_ROOT}/public/program?name=${programShortName}`; - const response = await fetch(url, { +import authorizationHeader from './utils/authorizationHeader'; + +//data formatters +const formatPublicProgram = (program) => ({ + name: program.name, + shortName: program.shortName, + description: program.description, + website: program.website, + institutions: program.programInstitutions?.map((institution) => institution.name) || [], + countries: program.programCountries?.map((country) => country.name) || [], + regions: program.processingRegions?.map((region) => region.name) || [], + cancerTypes: program.programCancers?.map((cancer) => cancer.name) || [], + primarySites: program.programPrimarySites?.map((primarySite) => primarySite.name) || [], +}); + +const formatPrivateProgram = (program) => program.program; +const formatPrivateProgramList = (programList) => programList.map(formatPrivateProgram); + +const formatJoinProgramInvite = (invitation) => { + const formattedObj = { + ...invitation, + createdAt: new Date(invitation.createdAt), + expiresAt: new Date(invitation.expiresAt), + acceptedAt: new Date(invitation.acceptedAt), + user: { ...invitation.user, role: invitation.user.role.value }, + program: { + ...invitation.program, + institutions: invitation.program.programInstitutions, + countries: invitation.program.programCountries, + cancerTypes: invitation.program.programCancers, + primarySite: invitation.program.programPrimarySites, + }, + }; + + delete formattedObj.program.programInstitutions; + delete formattedObj.program.programCountries; + delete formattedObj.program.programCancers; + delete formattedObj.program.programPrimarySites; + return formattedObj; +}; + +const getDataCenterByShortName = (shortName, dataCenterResponse) => + dataCenterResponse.filter((dataCenterObject) => dataCenterObject.shortName === shortName); +//private fields +export const listPrivatePrograms = async (jwt = null) => { + const url = urljoin(PROGRAM_SERVICE_HTTP_ROOT, `/programs`); + return await fetch(url, { + method: 'get', + headers: { + Authorization: authorizationHeader(jwt), + }, + }) + .then(restErrorResponseHandler) + .then((response) => response.json()) + .then((data) => { + if (data && Array.isArray(data)) { + return formatPrivateProgramList(data); + } else { + logger.error( + 'Error: no data or wrong data type is returned from /programs. Data must be an array', + ); + throw new Error( + 'no data or wrong data type is returned from /programs. Data must be an array', + ); + } + }); +}; + +export const getPrivateProgram = async (jwt = null, programShortName) => { + const url = urljoin(PROGRAM_SERVICE_HTTP_ROOT, `/programs/${programShortName}`); + return await fetch(url, { + method: 'get', + headers: { + Authorization: authorizationHeader(jwt), + }, + }) + .then(restErrorResponseHandler) + .then((response) => response.json()) + .then((data) => { + if (data) { + return formatPrivateProgram(data); + } else { + logger.error('Error: no data is returned from /program/{shortName}'); + throw new Error('No data is returned from /program/{shortName}'); + } + }); +}; + +export const getJoinProgramInvite = async (jwt = null, id) => { + const url = urljoin(PROGRAM_SERVICE_HTTP_ROOT, `/programs/joinProgramInvite/${id}`); + return await fetch(url, { method: 'get', + headers: { + Authorization: authorizationHeader(jwt), + }, }) .then(restErrorResponseHandler) - .then((response) => response.json()); - return response; + .then((response) => response.json()) + .then((data) => { + if (data.invitation) { + return formatJoinProgramInvite(data.invitation); + } else { + logger.error( + 'Error: no data or wrong data type is returned from /programs/joinProgramInvite/{invite_id}. Data must be an object with a property of "invitation"', + ); + throw new Error( + 'No data or wrong data type is returned from /programs/joinProgramInvite/{invite_id}. Data must be an object with a property of "invitation"', + ); + } + }); }; -export { getProgramPublicFields }; +export const listDataCenters = async (shortName, jwt) => { + const url = urljoin(PROGRAM_SERVICE_HTTP_ROOT, `/datacenters`); + return await fetch(url, { + method: 'get', + headers: { + Authorization: authorizationHeader(jwt), + }, + }) + .then(restErrorResponseHandler) + .then((response) => response.json()) + .then((data) => { + if (data && Array.isArray(data)) { + return shortName ? getDataCenterByShortName(shortName, data) : data; + } else { + logger.error( + 'Error: no data or wrong data type is returned from /datacenters. Data must be an array', + ); + throw new Error( + 'No data or wrong data type is returned from /datacenters. Data must be an array', + ); + } + }); +}; + +// public fields +export const getPublicProgram = async (programShortName) => { + const url = urljoin(PROGRAM_SERVICE_HTTP_ROOT, `public/program?name=${programShortName}`); + return await fetch(url, { + method: 'get', + }) + .then(restErrorResponseHandler) + .then((response) => response.json()) + .then(formatPublicProgram); +}; diff --git a/src/services/programService/index.js b/src/services/programService/index.js index fe56d105..3b240e90 100644 --- a/src/services/programService/index.js +++ b/src/services/programService/index.js @@ -26,9 +26,9 @@ import * as grpc from './grpcClient.js'; import * as http from './httpClient.js'; export default { - getProgram: grpc.getProgram, - listPrograms: grpc.listPrograms, - getJoinProgramInvite: grpc.getJoinProgramInvite, + getPrivateProgram: http.getPrivateProgram, + listPrivatePrograms: http.listPrivatePrograms, + getJoinProgramInvite: http.getJoinProgramInvite, listUsers: grpc.listUsers, listCancers: grpc.listCancers, @@ -45,5 +45,6 @@ export default { updateUser: grpc.updateUser, removeUser: grpc.removeUser, - getProgramPublicFields: http.getProgramPublicFields, + getPublicProgram: http.getPublicProgram, + listDataCenters: http.listDataCenters, }; diff --git a/src/services/programService/utils/authorizationHeader.ts b/src/services/programService/utils/authorizationHeader.ts new file mode 100644 index 00000000..8a92ddbd --- /dev/null +++ b/src/services/programService/utils/authorizationHeader.ts @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022 The Ontario Institute for Cancer Research. All rights reserved + * + * This program and the accompanying materials are made available under the terms of + * the GNU Affero General Public License v3.0. You should have received a copy of the + * GNU Affero General Public License along with this program. + * If not, see . + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This file dynamically generates a gRPC client from Ego.proto. + * The content of Ego.proto is copied directly from: https://github.com/icgc-argo/argo-proto/blob/4e2aeda59eb48b7af20b462aef2f04ef5d0d6e7c/ProgramService.proto + */ + +// Create a function that return this repetitive string template in the fecth header +const authorizationHeader = (jwt: string) => `Bearer ${jwt}`; + +export default authorizationHeader;