diff --git a/pages/api/Schema/ContactPrimaryAddress/contactPrimaryAddress.graphql b/pages/api/Schema/ContactPrimaryAddress/contactPrimaryAddress.graphql deleted file mode 100644 index 3543fe418b..0000000000 --- a/pages/api/Schema/ContactPrimaryAddress/contactPrimaryAddress.graphql +++ /dev/null @@ -1,22 +0,0 @@ -extend type Mutation { - setContactPrimaryAddress( - input: ContactPrimaryAddressInput! - ): ContactPrimaryAddress! -} - -input ContactPrimaryAddressInput { - contactId: ID! - # If primaryAddressId is null, no address will be primary - primaryAddressId: ID -} - -# If we could set @key(fields: "upc") on the Address type in the Rails API, then we could add -# primaryMailingAddress to it, but since we can't, we're creating a new type here -type AddressWithPrimary { - id: ID! - primaryMailingAddress: Boolean! -} - -type ContactPrimaryAddress { - addresses: [AddressWithPrimary!]! -} diff --git a/pages/api/Schema/ContactPrimaryAddress/datahandler.ts b/pages/api/Schema/ContactPrimaryAddress/datahandler.ts deleted file mode 100644 index ebfd512f5d..0000000000 --- a/pages/api/Schema/ContactPrimaryAddress/datahandler.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { array, boolean, mixed, object, string } from 'yup'; -import { AddressWithPrimary } from 'src/graphql/types.generated'; - -const getAddressesResponseSchema = object({ - data: object({ - id: string().required(), - type: string().required(), - relationships: object({ - addresses: object({ - data: array().of( - object({ - id: string().required(), - type: string().required(), - }).required(), - ), - }).required(), - }).required(), - }), - included: array() - .of( - object({ - id: string().required(), - type: string().required(), - attributes: mixed().required(), - }), - ) - .required(), -}); - -const addressSchema = object({ - primary_mailing_address: boolean().required(), -}); - -export const readExistingAddresses = ( - response: unknown, -): Array => { - const addresses = getAddressesResponseSchema.validateSync(response); - return addresses.included - .filter((included) => included.type === 'addresses') - .map((address) => { - const attributes = addressSchema.validateSync(address.attributes); - return { - __typename: 'AddressWithPrimary', - id: address.id, - primaryMailingAddress: attributes.primary_mailing_address, - }; - }); -}; diff --git a/pages/api/Schema/ContactPrimaryAddress/resolvers.ts b/pages/api/Schema/ContactPrimaryAddress/resolvers.ts deleted file mode 100644 index ab00455c43..0000000000 --- a/pages/api/Schema/ContactPrimaryAddress/resolvers.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Resolvers } from '../../graphql-rest.page.generated'; - -export const ContactPrimaryAddressResolvers: Resolvers = { - Mutation: { - setContactPrimaryAddress: ( - _source, - { input: { contactId, primaryAddressId } }, - { dataSources }, - ) => - dataSources.mpdxRestApi.setContactPrimaryAddress( - contactId, - primaryAddressId, - ), - }, -}; diff --git a/pages/api/Schema/index.ts b/pages/api/Schema/index.ts index 903c1eadd3..b979eb2872 100644 --- a/pages/api/Schema/index.ts +++ b/pages/api/Schema/index.ts @@ -7,8 +7,6 @@ import AccountListDonorAccountsTypeDefs from './AccountListDonorAccounts/account import { AccountListDonorAccountsResolvers } from './AccountListDonorAccounts/resolvers'; import CoachingAnswerSetsTypeDefs from './CoachingAnswerSets/coachingAnswerSets.graphql'; import { CoachingAnswerSetsResolvers } from './CoachingAnswerSets/resolvers'; -import ContactPrimaryAddressTypeDefs from './ContactPrimaryAddress/contactPrimaryAddress.graphql'; -import { ContactPrimaryAddressResolvers } from './ContactPrimaryAddress/resolvers'; import DestroyDonorAccountTypeDefs from './Contacts/DonorAccounts/Destroy/destroyDonorAccount.graphql'; import { DestroyDonorAccountResolvers } from './Contacts/DonorAccounts/Destroy/resolvers'; import ExportContactsTypeDefs from './ExportContacts/exportContacts.graphql'; @@ -72,10 +70,6 @@ const schema = buildSubgraphSchema([ typeDefs: AppointmentResultsTypeDefs, resolvers: AppointmentResultsResolvers, }, - { - typeDefs: ContactPrimaryAddressTypeDefs, - resolvers: ContactPrimaryAddressResolvers, - }, { typeDefs: ExportContactsTypeDefs, resolvers: ExportContactsResolvers }, { typeDefs: MergeContactsTypeDefs, resolvers: MergeContactsResolvers }, { typeDefs: MergePeopleBulkTypeDefs, resolvers: MergePeopleBulkResolvers }, diff --git a/pages/api/graphql-rest.page.ts b/pages/api/graphql-rest.page.ts index ccf89777cc..e0e09c01f2 100644 --- a/pages/api/graphql-rest.page.ts +++ b/pages/api/graphql-rest.page.ts @@ -20,7 +20,6 @@ import { getCoachingAnswerSet, getCoachingAnswerSets, } from './Schema/CoachingAnswerSets/dataHandler'; -import { readExistingAddresses } from './Schema/ContactPrimaryAddress/datahandler'; import { DestroyDonorAccount, DestroyDonorAccountResponse, @@ -416,53 +415,6 @@ class MpdxRestApi extends RESTDataSource { return getCoachingAnswer(res); } - // TODO: This should be merged with the updateContact mutation by adding the ability to update - // the primaryMailingAddress field when we have API resources again - async setContactPrimaryAddress( - contactId: string, - primaryAddressId: string | null | undefined, - ) { - // Setting primary_mailing_address to true on one address doesn't set it to false on all the - // others, so we have to load all the existing addresses and update all of their - // primary_mailing_address attributes - const getAddressesResponse = await this.get( - `contacts/${contactId}?include=addresses`, - ); - const addresses = readExistingAddresses(getAddressesResponse).map( - (address) => ({ - ...address, - primaryMailingAddress: address.id === primaryAddressId, - }), - ); - await this.put(`contacts/${contactId}`, { - body: { - included: addresses.map(({ id, primaryMailingAddress }) => ({ - type: 'addresses', - id, - attributes: { - primary_mailing_address: primaryMailingAddress, - }, - })), - data: { - type: 'contacts', - id: contactId, - attributes: { overwrite: true }, - relationships: { - addresses: { - data: addresses.map(({ id }) => ({ - type: 'addresses', - id, - })), - }, - }, - }, - }, - }); - return { - addresses, - }; - } - async getExpectedMonthlyTotalReport( accountListId: string, designationAccountId: string[] | null | undefined, diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx index 7d6b4f5a19..cffe3e99c9 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.test.tsx @@ -6,9 +6,10 @@ import { SnackbarProvider } from 'notistack'; import { placePromise, setupMocks } from '__tests__/util/googlePlacesMock'; import { GqlMockedProvider } from '__tests__/util/graphqlMocking'; import theme from '../../../../../../theme'; +import { SetContactPrimaryAddressMutation } from '../SetPrimaryAddress.generated'; import { AddAddressModal } from './AddAddressModal'; +import { CreateContactAddressMutation } from './CreateContactAddress.generated'; -const handleClose = jest.fn(); const accountListId = 'abc'; const contactId = '123'; @@ -27,43 +28,41 @@ jest.mock('notistack', () => ({ jest.mock('@react-google-maps/api'); +const mutationSpy = jest.fn(); +const handleClose = jest.fn(); + +const TestComponent: React.FC = () => ( + + + + onCall={mutationSpy} + > + + + + +); + describe('AddAddressModal', () => { beforeEach(() => { setupMocks(); }); it('should render edit contact address modal', async () => { - const { getByText } = render( - - - - - - - , - ); + const { getByText } = render(); expect(getByText('Add Address')).toBeInTheDocument(); }); it('should close edit contact other modal', () => { - const { getByText, getByLabelText } = render( - - - - - - - , - ); + const { getByText, getByLabelText } = render(); expect(getByText('Add Address')).toBeInTheDocument(); userEvent.click(getByLabelText('Close')); @@ -71,19 +70,7 @@ describe('AddAddressModal', () => { }); it('should handle cancel click', () => { - const { getByText } = render( - - - - - - - , - ); + const { getByText } = render(); expect(getByText('Add Address')).toBeInTheDocument(); userEvent.click(getByText('Cancel')); @@ -91,19 +78,7 @@ describe('AddAddressModal', () => { }); it('requires at least one field to be filled', async () => { - const { getByRole } = render( - - - - - - - , - ); + const { getByRole } = render(); const saveButton = getByRole('button', { name: 'Save' }); await waitFor(() => expect(saveButton).toBeDisabled()); @@ -113,7 +88,6 @@ describe('AddAddressModal', () => { }); it('should create contact address', async () => { - const mutationSpy = jest.fn(); const newStreet = '4321 Neat Street'; const newCity = 'Orlando'; const newState = 'FL'; @@ -121,19 +95,7 @@ describe('AddAddressModal', () => { const newCountry = 'United States'; const newRegion = 'New Region'; const newMetroArea = 'New Metro'; - const { getByRole, getByText, getByLabelText } = render( - - - - - - - , - ); + const { getByRole, getByText, getByLabelText } = render(); userEvent.clear(getByRole('combobox', { name: 'Street' })); userEvent.clear(getByLabelText('City')); @@ -176,19 +138,7 @@ describe('AddAddressModal', () => { it('handles chosen address predictions', async () => { jest.useFakeTimers(); - const { getByRole } = render( - - - - - - - , - ); + const { getByRole } = render(); // Let Google Maps initialize jest.runOnlyPendingTimers(); @@ -218,21 +168,8 @@ describe('AddAddressModal', () => { }, 20000); it('should set new address as primary', async () => { - const mutationSpy = jest.fn(); const newStreet = '4321 Neat Street'; - const { getByText, getByRole } = render( - - - - - - - , - ); + const { getByText, getByRole } = render(); const street = getByRole('combobox', { name: 'Street' }); userEvent.clear(street); @@ -249,25 +186,13 @@ describe('AddAddressModal', () => { expect(operation.variables.attributes.street).toEqual(newStreet); const { operation: operation2 } = mutationSpy.mock.calls[1][0]; + expect(operation2.variables.accountListId).toEqual(accountListId); expect(operation2.variables.primaryAddressId).not.toBeNull(); }, 30000); it('should not set new address as primary if it is unchecked', async () => { - const mutationSpy = jest.fn(); const newStreet = '4321 Neat Street'; - const { getByText, getByLabelText, getByRole } = render( - - - - - - - , - ); + const { getByText, getByLabelText, getByRole } = render(); userEvent.type(getByRole('combobox', { name: 'Street' }), newStreet); userEvent.click(getByLabelText('Primary')); diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx index 6ac6b14b70..2b37bb91a2 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/AddAddressModal/AddAddressModal.tsx @@ -23,7 +23,6 @@ import { ContactInputWrapper, } from 'src/components/Shared/styledComponents/ContactStyling'; import { LoadingIndicator } from 'src/components/Shared/styledComponents/LoadingStyling'; -import { useUpdateCache } from 'src/hooks/useUpdateCache'; import Modal from '../../../../../Shared/Modal/Modal'; import { ContactDetailsTabDocument, @@ -53,7 +52,6 @@ export const AddAddressModal: React.FC = ({ useCreateContactAddressMutation(); const [setContactPrimaryAddress, { loading: settingPrimaryAddress }] = useSetContactPrimaryAddressMutation(); - const { update } = useUpdateCache(contactId); const onSubmit = async ({ primaryMailingAddress, @@ -110,10 +108,10 @@ export const AddAddressModal: React.FC = ({ if (primaryMailingAddress && newAddressId) { await setContactPrimaryAddress({ variables: { + accountListId, contactId, primaryAddressId: newAddressId, }, - update, }); } enqueueSnackbar(t('Address added successfully'), { diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx index 2450509774..a469222239 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.test.tsx @@ -10,6 +10,12 @@ import { ContactMailingFragment, ContactMailingFragmentDoc, } from '../ContactMailing.generated'; +import { SetContactPrimaryAddressMutation } from '../SetPrimaryAddress.generated'; +import { + DeleteContactAddressMutation, + DonationServicesEmailQuery, + UpdateContactAddressMutation, +} from './EditContactAddress.generated'; import { EditContactAddressModal } from './EditContactAddressModal'; const handleClose = jest.fn(); @@ -49,45 +55,49 @@ const mockContact: ContactMailingFragment = { }, }; +const mutationSpy = jest.fn(); + +interface TestComponentProps { + address?: ContactMailingFragment['addresses']['nodes'][0]; +} + +const TestComponent: React.FC = ({ + address = mockContact.addresses.nodes[0], +}) => ( + + + + onCall={mutationSpy} + > + + + + +); + describe('EditContactAddressModal', () => { beforeEach(() => { setupMocks(); }); it('should render edit contact address modal', async () => { - const { getByText } = render( - - - - - - - , - ); + const { getByText } = render(); expect(getByText('Edit Address')).toBeInTheDocument(); }); it('should close edit contact other modal', () => { - const { getByText, getByLabelText } = render( - - - - - - - , - ); + const { getByText, getByLabelText } = render(); expect(getByText('Edit Address')).toBeInTheDocument(); userEvent.click(getByLabelText('Close')); @@ -95,20 +105,7 @@ describe('EditContactAddressModal', () => { }); it('should handle cancel click', () => { - const { getByText } = render( - - - - - - - , - ); + const { getByText } = render(); expect(getByText('Edit Address')).toBeInTheDocument(); userEvent.click(getByText('Cancel')); @@ -117,33 +114,24 @@ describe('EditContactAddressModal', () => { it('requires at least one field to be filled', async () => { const { getByRole } = render( - - - - - - - , + , ); const saveButton = getByRole('button', { name: 'Save' }); @@ -157,7 +145,6 @@ describe('EditContactAddressModal', () => { }); it('should edit contact address', async () => { - const mutationSpy = jest.fn(); const newStreet = '4321 Neat Street'; const newCity = 'Orlando'; const newState = 'FL'; @@ -165,20 +152,7 @@ describe('EditContactAddressModal', () => { const newCountry = 'United States'; const newRegion = 'New Region'; const newMetroArea = 'New Metro'; - const { getByRole, getByText, getByLabelText } = render( - - - - - - - , - ); + const { getByRole, getByText, getByLabelText } = render(); userEvent.clear(getByRole('combobox', { name: 'Street' })); userEvent.clear(getByLabelText('City')); @@ -219,28 +193,42 @@ describe('EditContactAddressModal', () => { expect(operation.variables.attributes.historic).toEqual(false); const { operation: operation2 } = mutationSpy.mock.calls[1][0]; + expect(operation2.variables.accountListId).toEqual(accountListId); expect(operation2.variables.primaryAddressId).toEqual( mockContact.addresses.nodes[0].id, ); }, 80000); + it('should clear primary address when unchecked', async () => { + const { getByLabelText, getByText } = render( + , + ); + + userEvent.click(getByLabelText('Primary')); + userEvent.click(getByText('Save')); + await waitFor(() => + expect(mockEnqueue).toHaveBeenCalledWith('Address updated successfully', { + variant: 'success', + }), + ); + + const { operation: operation2 } = mutationSpy.mock.calls[1][0]; + + expect(operation2.variables.accountListId).toEqual(accountListId); + + // API ensures all addresses on a contact are no longer primary when receiving null primaryAddressId + expect(operation2.variables.primaryAddressId).toBeNull(); + }, 30000); + it('handles chosen address predictions', async () => { jest.useFakeTimers(); - const { getByRole } = render( - - - - - - - , - ); + const { getByRole } = render(); // Let Google Maps initialize jest.runOnlyPendingTimers(); @@ -271,22 +259,8 @@ describe('EditContactAddressModal', () => { }, 20000); it('should edit not set primary address when it has not changed', async () => { - const mutationSpy = jest.fn(); const newStreet = '4321 Neat Street'; - const { getByRole, getByText } = render( - - - - - - - , - ); + const { getByRole, getByText } = render(); const street = getByRole('combobox', { name: 'Street' }); userEvent.clear(street); @@ -302,20 +276,7 @@ describe('EditContactAddressModal', () => { }, 30000); it('should handle delete click', async () => { - const { getByText, getByTestId } = render( - - - - - - - , - ); + const { getByText, getByTestId } = render(); expect(getByText('Edit Address')).toBeInTheDocument(); expect(getByTestId('modal-delete-button')).toBeInTheDocument(); @@ -330,18 +291,9 @@ describe('EditContactAddressModal', () => { it('should restrict editing of Siebel addresses', async () => { const { getByRole, getByText, findByRole } = render( - - - - - - - , + , ); expect( diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx index 23784a7213..1c87018163 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/EditContactAddressModal/EditContactAddressModal.tsx @@ -27,7 +27,6 @@ import { ContactInputWrapper, } from 'src/components/Shared/styledComponents/ContactStyling'; import { LoadingIndicator } from 'src/components/Shared/styledComponents/LoadingStyling'; -import { useUpdateCache } from 'src/hooks/useUpdateCache'; import { isEditableSource } from 'src/lib/sourceHelper'; import Modal from '../../../../../Shared/Modal/Modal'; import { @@ -70,7 +69,6 @@ export const EditContactAddressModal: React.FC< useDeleteContactAddressMutation(); const [setContactPrimaryAddress, { loading: settingPrimaryAddress }] = useSetContactPrimaryAddressMutation(); - const { update } = useUpdateCache(contactId); const onSubmit = async ({ primaryMailingAddress, @@ -87,15 +85,13 @@ export const EditContactAddressModal: React.FC< }, }, }); - // updateContactAddress doesn't set support setting the primaryMailingAddress field, so if - // that field changes, then use the setContactPrimaryAddress mutation to update it if (address.primaryMailingAddress !== primaryMailingAddress) { await setContactPrimaryAddress({ variables: { + accountListId, contactId, primaryAddressId: primaryMailingAddress ? address.id : null, }, - update, }); } enqueueSnackbar(t('Address updated successfully'), { diff --git a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.graphql b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.graphql index b15865377b..c747ea0c45 100644 --- a/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.graphql +++ b/src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.graphql @@ -1,20 +1,25 @@ -fragment PrimaryMailingAddress on Address { - primaryMailingAddress -} - -fragment ContactPrimaryAddressRelation on Contact { - primaryAddress { - id - } -} - -mutation SetContactPrimaryAddress($contactId: ID!, $primaryAddressId: ID) { - setContactPrimaryAddress( - input: { contactId: $contactId, primaryAddressId: $primaryAddressId } +mutation SetContactPrimaryAddress( + $accountListId: ID! + $contactId: ID! + $primaryAddressId: String +) { + updateContact( + input: { + accountListId: $accountListId + attributes: { id: $contactId, primaryAddressId: $primaryAddressId } + } ) { - addresses { + contact { id - primaryMailingAddress + primaryAddress { + id + } + addresses(first: 25) { + nodes { + id + primaryMailingAddress + } + } } } } diff --git a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx index b39e72231f..de3ec915a7 100644 --- a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx +++ b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.test.tsx @@ -173,6 +173,7 @@ describe('CreateMultipleContacts', () => { }, }); expect(mutationSpy).toHaveGraphqlOperation('SetContactPrimaryAddress', { + accountListId, contactId: 'contact-1', primaryAddressId: 'address-1', }); @@ -496,6 +497,7 @@ describe('CreateMultipleContacts', () => { }, }); expect(mutationSpy).toHaveGraphqlOperation('SetContactPrimaryAddress', { + accountListId, contactId: 'contact-1', primaryAddressId: 'address-1', }); diff --git a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx index 7975b8bc93..170924d338 100644 --- a/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx +++ b/src/components/Layouts/Primary/TopBar/Items/AddMenu/Items/CreateMultipleContacts/CreateMultipleContacts.tsx @@ -233,6 +233,7 @@ export const CreateMultipleContacts = ({ if (addressId) { await setPrimaryAddress({ variables: { + accountListId, contactId, primaryAddressId: addressId, }, diff --git a/src/hooks/useUpdateCache.ts b/src/hooks/useUpdateCache.ts deleted file mode 100644 index 30909e406e..0000000000 --- a/src/hooks/useUpdateCache.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { ApolloCache, MutationUpdaterFunction } from '@apollo/client'; -import { - ContactPrimaryAddressRelationFragmentDoc, - PrimaryMailingAddressFragmentDoc, - SetContactPrimaryAddressMutation, -} from 'src/components/Contacts/ContactDetails/ContactDetailsTab/Mailing/SetPrimaryAddress.generated'; - -// This hook provides an Apollo cache update function for the setContactPrimaryAddress mutation -// used by the add address and edit address modals -export function useUpdateCache(contactId: string) { - const update: MutationUpdaterFunction< - SetContactPrimaryAddressMutation, - unknown, - unknown, - ApolloCache - > = (cache, { data }) => { - // Update the the primaryMailingAddress field for all addresses that were updated - const addresses = data?.setContactPrimaryAddress.addresses ?? []; - addresses.forEach((address) => { - cache.writeFragment({ - id: `Address:${address.id}`, - fragment: PrimaryMailingAddressFragmentDoc, - data: { - primaryMailingAddress: address.primaryMailingAddress, - }, - }); - }); - // Also update the contact's primaryMailingAddress relation - const primaryAddressId = addresses.find( - (address) => address.primaryMailingAddress, - ); - cache.writeFragment({ - id: `Contact:${contactId}`, - fragment: ContactPrimaryAddressRelationFragmentDoc, - data: { - primaryAddress: primaryAddressId - ? { - id: primaryAddressId, - } - : null, - }, - }); - }; - - return { update }; -}