From b0223090b414641745d8e858eb5b2920e12a2e1b Mon Sep 17 00:00:00 2001 From: LaShawn Toyoda <31802656+theyokohamalife@users.noreply.github.com> Date: Wed, 9 Aug 2023 01:10:36 +0900 Subject: [PATCH] feat: add mutations for addFacility and addHealthcareProfessional (#203) * feat: implement mutation for addFacility * feat: implement mutation for addHealthcareProfessional * refactor: fix healthcareProfessionalService types * refactor: fix types and move function --- src/resolvers.ts | 106 +++++-------- src/services/facilityService.ts | 59 +++++++- src/services/healthcareProfessionalService.ts | 118 ++++++++++++++- src/typeDefs/dbSchema.ts | 6 +- src/typeDefs/schema.graphql | 141 +++++++++++------- 5 files changed, 290 insertions(+), 140 deletions(-) diff --git a/src/resolvers.ts b/src/resolvers.ts index 7f046fdb..f7f76fb0 100644 --- a/src/resolvers.ts +++ b/src/resolvers.ts @@ -1,11 +1,9 @@ -import crypto from 'crypto' -// import { getDegreeById, getDegrees } from './services/degreeService' -import { getFacilityById, searchFacilities } from './services/facilityService' -import { getHealthcareProfessionalById, searchHealthcareProfessionals } from './services/healthcareProfessionalService' -// import { getPhysicalAddressById, getPhysicalAddresses } from './services/physicalAddressService' -// import { getSpecialtyById, getSpecialties } from './services/specialtyService' -// import { getSpokenLanguageByIso, getSpokenLanguages } from './services/spokenLanguageService' +import { addFacility, getFacilityById, searchFacilities } from './services/facilityService' +import { addHealthcareProfessional, + getHealthcareProfessionalById, + searchHealthcareProfessionals } from './services/healthcareProfessionalService' import { + Contact, Degree, Facility, Insurance, @@ -13,102 +11,66 @@ import { Specialty, LocaleNameInput, HealthcareProfessionalInput, - SpokenLanguage + SpokenLanguage, + LocaleName, + DegreeInput, + SpecialtyInput, + SpokenLanguageInput, + FacilityInput, + ContactInput } from './typeDefs/gqlTypes' const resolvers = { Query: { - // degrees: () => { - // const matchingDegrees = getDegrees() - - // return matchingDegrees - // }, - // degree: (_parent: Degree, args: { id: string; }) => { - // const matchingDegree = getDegreeById(args.id) - - // return matchingDegree - // }, facilities: async () => { - // TODO: add a validation step for incoming parameters const matchingFacilities = await searchFacilities(['1']) return matchingFacilities }, facility: async (_parent: Facility, args: { id: string; }) => { - // TODO: add a validation step for incoming parameters const matchingFacility = await getFacilityById(args.id) return matchingFacility }, healthcareProfessionals: async () => { - // TODO: add a validation step for incoming parameters const matchingProfessionals = await searchHealthcareProfessionals(['1']) return matchingProfessionals }, healthcareProfessional: async (_parent: HealthcareProfessional, args: { id: string; }) => { - // TODO: add a validation step for incoming parameters const matchingHealthcareProfessional = await getHealthcareProfessionalById(args.id) return matchingHealthcareProfessional } - // physicalAddress: (_parent: HealthcareProfessional, args: { id: string; }) => getPhysicalAddressById(args.id), - // physicalAddresses: () => getPhysicalAddresses(), - // specialties: () => { - // // TODO: add a validation step for incoming parameters - // const matchingSpecialties = getSpecialties() - - // return matchingSpecialties - // }, - // specialty: (_parent: Specialty, args: { id: string; }) => { - // // TODO: add a validation step for incoming parameters - // const matchingSpecialty = getSpecialtyById(args.id) - - // return matchingSpecialty - // }, - // spokenLanguages: () => getSpokenLanguages(), - // spokenLanguage: (_parent: SpokenLanguage, args: {iso639_3: string;}) => getSpokenLanguageByIso(args.iso639_3) }, Mutation: { - createHealthcareProfessional: (_parent: HealthcareProfessionalInput, args: { - id: string, - names: Array, - degrees: Array, - spokenLanguages: Array, - specialties: Array, - acceptedInsurance: Array - }) => { - const id = crypto.randomUUID() - - const { - names, - degrees, - spokenLanguages, - specialties, - acceptedInsurance - } = args + createFacility: async (_parent: FacilityInput, args: { + input: { + contact: ContactInput, + healthcareProfessionals: HealthcareProfessionalInput[], + nameEn: string, + nameJa: string, + } + }) => { + const newFacility = await addFacility(args.input) - // TODO: Eventually this should check if a specialty already exists in the DB - // and match it to that if it does. - specialties.map((specialty: { id: string }) => { - if (!specialty.id) { - // eslint-disable-next-line no-param-reassign - specialty.id = crypto.randomUUID() - } - return specialty.id - }) + return newFacility + }, + createHealthcareProfessional: async (_parent: HealthcareProfessionalInput, args: { + input:{ + acceptedInsurance: Insurance[], + degrees: DegreeInput[], + names: LocaleNameInput[] + specialties: SpecialtyInput[] + spokenLanguages: SpokenLanguageInput[] - const healthcareProfessional = { - id, - names, - degrees, - spokenLanguages, - specialties, - acceptedInsurance } + }) => { + const newHealthcareProfessional = await addHealthcareProfessional(args.input) - return healthcareProfessional + return newHealthcareProfessional } + } } diff --git a/src/services/facilityService.ts b/src/services/facilityService.ts index 7a1f2eda..ef1d560a 100644 --- a/src/services/facilityService.ts +++ b/src/services/facilityService.ts @@ -1,7 +1,8 @@ -import { HealthcareProfessional, LocaleName, Degree, - Specialty, SpecialtyName, SpokenLanguage, Insurance, Facility } from '../typeDefs/dbSchema' +import { HealthcareProfessional, LocaleName, Contact, Degree, + Specialty, SpokenLanguage, Insurance, Facility } from '../typeDefs/dbSchema' import { DocumentData, WhereFilterOp, getFirestore } from 'firebase-admin/firestore' -import { searchHealthcareProfessionals } from './healthcareProfessionalService' +import { FacilityInput, PhysicalAddress, ContactInput, HealthcareProfessionalInput } from '../typeDefs/gqlTypes' +import { mapAndValidateHealthcareProInput } from './healthcareProfessionalService' export const getFacilityById = async (id: string) : Promise => { const db = getFirestore() @@ -18,14 +19,33 @@ export const getFacilityById = async (id: string) : Promise => return convertedEntity } -export const addFacility = async (facility : Facility) : Promise => { - //todo +export const addFacility = async (input: FacilityInput) : Promise => { + const db = getFirestore() + + const facilityRef = db.collection('facilities') + + const healthcareProfessionalIds = await mapAndValidateHealthcareProInput( + input.healthcareProfessionals as HealthcareProfessionalInput[] + ) + + const newFacility = { + contact: validateContactInput(input.contact as Contact), + healthcareProfessionalIds: healthcareProfessionalIds, + healthcareProfessionals: [], + nameEn: validateNameEnInput(input.nameEn as string), + nameJa: validateNameJaInput(input.nameJa as string) + } satisfies Facility + + await facilityRef.add(newFacility) + + return newFacility as Facility } export const searchFacilities = async (userSearchQuery : string[]) : Promise => { const db = getFirestore() - const hpRef = db.collection('healthcareProfessionals') + const hpRef = db.collection('facilities') // make this a real query + // this is still incomplete const snapshot = await hpRef.where('id', 'in', userSearchQuery).get() const facilities = [] as Facility[] @@ -41,12 +61,37 @@ export const searchFacilities = async (userSearchQuery : string[]) : Promise { const gqlEntity = { - id: dbEntity.id, nameEn: dbEntity.nameEn, nameJa: dbEntity.nameJa, contact: dbEntity.contact, + healthcareProfessionalIds: dbEntity.healthcareProfessionalIds, healthcareProfessionals: dbEntity.healthcareProfessionals } satisfies Facility return gqlEntity } + +function validateContactInput(contactInput: Contact) : Contact { + const facilityContact = { + address: contactInput.address as PhysicalAddress, + email: contactInput.email as string, + mapsLink: contactInput.mapsLink as string, + phone: contactInput.phone as string, + website: contactInput.website as string + } + + return facilityContact +} + +function validateNameEnInput(nameEnInput: string) : string { + const nameEn = nameEnInput + + return nameEn +} + +function validateNameJaInput(nameJaInput: string) : string { + const nameJa = nameJaInput + + return nameJa +} + diff --git a/src/services/healthcareProfessionalService.ts b/src/services/healthcareProfessionalService.ts index 24ef1ec5..518b49c6 100644 --- a/src/services/healthcareProfessionalService.ts +++ b/src/services/healthcareProfessionalService.ts @@ -1,6 +1,12 @@ import { HealthcareProfessional, LocaleName, Degree, Specialty, SpecialtyName, SpokenLanguage, Insurance } from '../typeDefs/dbSchema' -import { DocumentData, WhereFilterOp, getFirestore } from 'firebase-admin/firestore' +import { DocumentData, DocumentReference, WhereFilterOp, getFirestore } from 'firebase-admin/firestore' +import { DegreeInput, + HealthcareProfessionalInput, + LocaleNameInput, + SpecialtyInput, + SpecialtyNameInput, + SpokenLanguageInput } from '../typeDefs/gqlTypes' export const getHealthcareProfessionalById = async (id: string) : Promise => { const db = getFirestore() @@ -17,9 +23,26 @@ export const getHealthcareProfessionalById = async (id: string) : Promise => { +export const addHealthcareProfessional = async (input : HealthcareProfessionalInput) : Promise => { + const db = getFirestore() + + const healthcareProRef = db.collection('healthcareProfessionals') + + const newHealthcareProfessional = { + acceptedInsurance: mapAndValidateInsurance(input.acceptedInsurance as []), + degrees: mapAndValidateDegrees(input.degrees as DegreeInput[]), + names: mapAndValidateNames(input.names as LocaleNameInput[]), + specialties: mapAndValidateSpecialties(input.specialties as SpecialtyInput[]), + spokenLanguages: mapAndValidateLanguages(input.spokenLanguages as SpokenLanguageInput[]) + } + + const idList: string[] = [] + + const docRef = await healthcareProRef.add(newHealthcareProfessional) - //todo + idList.push(docRef.id) + + return idList } export const searchHealthcareProfessionals = async (userSearchQuery : string[]) @@ -41,7 +64,6 @@ export const searchHealthcareProfessionals = async (userSearchQuery : string[]) const mapDbEntityTogqlEntity = (dbEntity : DocumentData) : HealthcareProfessional => { const gqlEntity = { - id: dbEntity.id, names: dbEntity.names, degrees: dbEntity.degrees, spokenLanguages: dbEntity.spokenLanguages, @@ -51,3 +73,91 @@ const mapDbEntityTogqlEntity = (dbEntity : DocumentData) : HealthcareProfessiona return gqlEntity } + +export const mapAndValidateHealthcareProInput = +(healthcareProInput: HealthcareProfessionalInput[]) : Promise => healthcareProInput.map( + (professional: HealthcareProfessionalInput) => addHealthcareProfessional(professional) +)[0] + +function mapAndValidateDegrees(degreesInput: DegreeInput[]) { + // TODO: Write conditional to check if already exists + // TODO: This should save to the degrees collection and return an array of IDs + const degrees = degreesInput.map((degree: DegreeInput) => { + const newDegree = {nameJa: degree.nameJa, + nameEn: degree.nameEn, + abbreviation: degree.abbreviation} + + return newDegree + }) + + return degrees +} + +function mapAndValidateNames(namesInput: LocaleNameInput[]) { + // TODO: Write conditional to check if already exists + const names = namesInput.map((name: LocaleNameInput) => { + const newLocaleName = { + lastName: name.lastName, + firstName: name.firstName, + middleName: name.middleName, + locale: name.locale + } + + return newLocaleName + }) + + return names +} + +function mapAndValidateSpecialties(specialtiesInput: SpecialtyInput[]) { + // TODO: Write conditional to check if already exists + // TODO: This should save to the specialties collection and return an array of IDs + const specialties = specialtiesInput.map((specialty: SpecialtyInput) => { + const newSpecialty = { + + names: mapAndValidateSpecialtyNames(specialty.names as SpecialtyNameInput[]) + } + + return newSpecialty + }) + + return specialties +} + +function mapAndValidateSpecialtyNames(specialtyNamesInput: SpecialtyNameInput[]) { + // TODO: Write conditional to check if already exists + const specialtyNames = specialtyNamesInput.map((name: SpecialtyNameInput) => { + const newSpecialtyName = { + name: name.name, + locale: name.locale + } + + return newSpecialtyName + }) + + return specialtyNames +} + +function mapAndValidateLanguages(languagesInput: SpokenLanguageInput[]) { + // TODO: Write conditional to check if already exists + const languages = languagesInput.map((language: SpokenLanguageInput) => { + const newLanguage = { + iso639_3: language.iso639_3, + nameJa: language.nameJa, + nameEn: language.nameEn, + nameNative: language.nameNative + } + + return newLanguage + }) + + return languages +} + +function mapAndValidateInsurance(insuranceInput: Insurance[]) { + // TODO: Write conditional to check if already exists + + const insurance = insuranceInput + + return insurance +} diff --git a/src/typeDefs/dbSchema.ts b/src/typeDefs/dbSchema.ts index 4c729f89..25acddcd 100644 --- a/src/typeDefs/dbSchema.ts +++ b/src/typeDefs/dbSchema.ts @@ -1,20 +1,22 @@ +import { PhysicalAddress } from './gqlTypes' + export type Contact = { email: string phone: string website: string mapsLink: string + address: PhysicalAddress } export type Facility = { - id: string nameEn: string nameJa: string contact: Contact + healthcareProfessionalIds: string[] healthcareProfessionals: HealthcareProfessional[] } export type HealthcareProfessional = { - id: string, names: LocaleName[], degrees: Degree[], spokenLanguages: SpokenLanguage[], diff --git a/src/typeDefs/schema.graphql b/src/typeDefs/schema.graphql index b2946f59..bde37d97 100644 --- a/src/typeDefs/schema.graphql +++ b/src/typeDefs/schema.graphql @@ -1,18 +1,38 @@ type Contact { - id: ID! - email: String! - phone: String! - website: String! - mapsLink: String! + id: ID + email: String + phone: String + website: String + mapsLink: String address: PhysicalAddress } +input ContactInput { + email: String + phone: String + website: String + mapsLink: String + address: PhysicalAddressInput +} + type PhysicalAddress { - id: ID! - postalCode: String! - prefectureEn: String! - cityEn: String! - addressLine1En: String! + id: ID + postalCode: String + prefectureEn: String + cityEn: String + addressLine1En: String + addressLine2En: String + prefectureJa: String + cityJa: String + addressLine1Ja: String + addressLine2Ja: String +} + +input PhysicalAddressInput { + postalCode: String + prefectureEn: String + cityEn: String + addressLine1En: String addressLine2En: String prefectureJa: String cityJa: String @@ -21,75 +41,83 @@ type PhysicalAddress { } type Facility { - id: ID! - nameEn: String! - nameJa: String! - contact: Contact! - healthcareProfessionals: [HealthcareProfessional]! + id: ID + nameEn: String + nameJa: String + contact: Contact + healthcareProfessionalIds: [String] + healthcareProfessionals: [HealthcareProfessional] +} + +input FacilityInput { + nameEn: String + nameJa: String + contact: ContactInput + healthcareProfessionals: [HealthcareProfessionalInput]! } input HealthcareProfessionalInput { - names: [LocaleNameInput]! - degrees: [DegreeInput]! - spokenLanguages: [SpokenLanguageInput]! - specialties: [SpecialtyInput]! - acceptedInsurance: [Insurance]! + names: [LocaleNameInput] + degrees: [DegreeInput] + spokenLanguages: [SpokenLanguageInput] + specialties: [SpecialtyInput] + acceptedInsurance: [Insurance] } type HealthcareProfessional { - id: ID! - names: [LocaleName]! - degrees: [Degree]! - spokenLanguages: [SpokenLanguage]! - specialties: [Specialty]! - acceptedInsurance: [Insurance]! + id: ID + names: [LocaleName] + degrees: [Degree] + spokenLanguages: [SpokenLanguage] + specialties: [Specialty] + acceptedInsurance: [Insurance] } input LocaleNameInput { - lastName: String! - firstName: String! + lastName: String + firstName: String middleName: String - locale: String! + locale: String } type LocaleName { - lastName: String! - firstName: String! + lastName: String + firstName: String middleName: String - locale: String! + locale: String } input SpecialtyInput { - names: [SpecialtyNameInput!]! + names: [SpecialtyNameInput] } type Specialty { - id: ID! - names: [SpecialtyName!]! + id: ID + names: [SpecialtyName!] } input SpecialtyNameInput { - name: String! - locale: String! + name: String + locale: String } type SpecialtyName { - name: String! - locale: String! + name: String + locale: String } input SpokenLanguageInput { - iso639_3: String! - nameJa: String! - nameEn: String! - nameNative: String! + iso639_3: String + nameJa: String + nameEn: String + nameNative: String } type SpokenLanguage { - iso639_3: String! - nameJa: String! - nameEn: String! - nameNative: String! + iso639_3: String + nameJa: String + nameEn: String + nameNative: String } type Submission { @@ -109,17 +137,17 @@ input SubmissionInput { } input DegreeInput { - id: ID! - nameJa: String! - nameEn: String! - abbreviation: String! + id: ID + nameJa: String + nameEn: String + abbreviation: String } type Degree { id: ID! - nameJa: String! - nameEn: String! - abbreviation: String! + nameJa: String + nameEn: String + abbreviation: String } enum Insurance { @@ -153,4 +181,7 @@ type Mutation { id: ID! input: HealthcareProfessionalInput ): HealthcareProfessional -} \ No newline at end of file + createFacility( + input: FacilityInput + ): Facility +}