Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
1d4e2e6
feat: update customDomainUpdate mutation for modern journeys integration
mikeallisonJS Apr 24, 2026
70849d5
fix: lint issues
autofix-ci[bot] Apr 24, 2026
b5aa8c2
feat: add customDomainDelete mutation for modern journeys integration…
mikeallisonJS Apr 24, 2026
03112f8
fix: lint issues
autofix-ci[bot] Apr 25, 2026
5b07de2
merge: main
mikeallisonJS Apr 29, 2026
95fcaf9
refactor(customDomainUpdate): simplify authentication and error handl…
mikeallisonJS Apr 29, 2026
5ebec92
fix: validate journeyCollectionId team ownership and support null dis…
mikeallisonJS Apr 29, 2026
3dcd4fc
fix: lint issues
autofix-ci[bot] Apr 29, 2026
5035f75
test(customDomainUpdate): add tests for journeyCollectionId handling …
mikeallisonJS Apr 29, 2026
583b1a7
merge: main
mikeallisonJS Apr 29, 2026
762b137
feat(customDomain): enhance custom domain check functionality
mikeallisonJS Apr 29, 2026
0f6d489
refactor(customDomain): remove custom domain functionality from api-j…
mikeallisonJS Apr 29, 2026
f73e216
refactor(customDomain): standardize custom domain fields in GraphQL s…
mikeallisonJS Apr 29, 2026
13ad207
fix: lint issues
autofix-ci[bot] Apr 29, 2026
e78fc43
refactor(chatButton): remove create and update mutations from GraphQL…
mikeallisonJS Apr 29, 2026
8563e08
refactor(chatButton): update chatButtonRemove mutation for consistency
mikeallisonJS Apr 29, 2026
436dd51
Merge branch '26-00-MA-chore-chatbuttonremove-modern' into 26-00-MA-c…
mikeallisonJS Apr 29, 2026
b02abf5
refactor(chatButton): reorganize chat button mutations and inputs
mikeallisonJS Apr 29, 2026
74f90a7
refactor(chatButton): remove override references for chat button muta…
mikeallisonJS Apr 29, 2026
d404856
refactor(journeyViewEvent): update journeyViewEventCreate mutation fo…
mikeallisonJS Apr 30, 2026
b8c3e3c
refactor(stepViewEvent): update stepViewEventCreate mutation for mode…
mikeallisonJS Apr 30, 2026
7ad9bcc
refactor(videoStartEvent): update videoStartEventCreate mutation for …
mikeallisonJS Apr 30, 2026
06ecb0e
refactor(videoPlayEvent): update videoPlayEventCreate mutation for mo…
mikeallisonJS Apr 30, 2026
73e6736
refactor(videoPauseEvent): update videoPauseEventCreate mutation for …
mikeallisonJS Apr 30, 2026
a01cd01
fix: lint issues
autofix-ci[bot] Apr 30, 2026
168b9a3
refactor(videoCompleteEvent): update videoCompleteEventCreate mutatio…
mikeallisonJS May 1, 2026
86ef52d
refactor(videoExpandEvent): update videoExpandEventCreate mutation fo…
mikeallisonJS May 1, 2026
b1e5cf6
refactor(videoCollapseEvent): update videoCollapseEventCreate mutatio…
mikeallisonJS May 1, 2026
9c85dec
refactor(videoProgressEvent): update videoProgressEventCreate mutatio…
mikeallisonJS May 1, 2026
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
698 changes: 342 additions & 356 deletions apis/api-gateway/schema.graphql

Large diffs are not rendered by default.

48 changes: 43 additions & 5 deletions apis/api-journeys-modern/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,15 @@ type CustomDomain
journeyCollection: JourneyCollection
}

type CustomDomainCheck
@shareable
{
configured: Boolean!
verified: Boolean!
verification: [CustomDomainVerification!]
verificationResponse: CustomDomainVerificationResponse
}

input CustomDomainCreateInput {
id: ID
teamId: String!
Expand All @@ -359,6 +368,22 @@ input CustomDomainUpdateInput {
routeAllTeamJourneys: Boolean
}

type CustomDomainVerification
@shareable
{
type: String!
domain: String!
value: String!
reason: String!
}

type CustomDomainVerificationResponse
@shareable
{
code: String!
message: String!
}

"""
A date string, such as 2007-12-03, compliant with the `full-date` format outlined in section 5.6 of the RFC 3339 profile of the ISO 8601 standard for representation of dates and times using the Gregorian calendar.
"""
Expand Down Expand Up @@ -1518,15 +1543,28 @@ type Mutation {
blockUpdateNavigateToBlockAction(id: ID!, input: NavigateToBlockActionInput!, journeyId: ID): NavigateToBlockAction!
blockUpdatePhoneAction(id: ID!, input: PhoneActionInput!, journeyId: ID): PhoneAction!
blockUpdateChatAction(id: ID!, input: ChatActionInput!, journeyId: ID): ChatAction!
chatButtonCreate(journeyId: ID!, input: ChatButtonCreateInput): ChatButton! @override(from: "api-journeys")
chatButtonUpdate(id: ID!, journeyId: ID!, input: ChatButtonUpdateInput!): ChatButton! @override(from: "api-journeys")
customDomainCreate(input: CustomDomainCreateInput!): CustomDomain! @override(from: "api-journeys")
chatButtonCreate(journeyId: ID!, input: ChatButtonCreateInput): ChatButton!
chatButtonRemove(id: ID!): ChatButton!
chatButtonUpdate(id: ID!, journeyId: ID!, input: ChatButtonUpdateInput!): ChatButton!
customDomainCheck(id: ID!): CustomDomainCheck!
customDomainDelete(id: ID!): CustomDomain!
customDomainUpdate(id: ID!, input: CustomDomainUpdateInput!): CustomDomain!
customDomainCreate(input: CustomDomainCreateInput!): CustomDomain!
buttonClickEventCreate(input: ButtonClickEventCreateInput!): ButtonClickEvent!
chatOpenEventCreate(input: ChatOpenEventCreateInput!): ChatOpenEvent!
journeyViewEventCreate(input: JourneyViewEventCreateInput!): JourneyViewEvent @override(from: "api-journeys")
stepViewEventCreate(input: StepViewEventCreateInput!): StepViewEvent! @override(from: "api-journeys")
radioQuestionSubmissionEventCreate(input: RadioQuestionSubmissionEventCreateInput!): RadioQuestionSubmissionEvent!
multiselectSubmissionEventCreate(input: MultiselectSubmissionEventCreateInput!): MultiselectSubmissionEvent!
signUpSubmissionEventCreate(input: SignUpSubmissionEventCreateInput!): SignUpSubmissionEvent!
textResponseSubmissionEventCreate(input: TextResponseSubmissionEventCreateInput!): TextResponseSubmissionEvent!
videoStartEventCreate(input: VideoStartEventCreateInput!): VideoStartEvent! @override(from: "api-journeys")
videoPlayEventCreate(input: VideoPlayEventCreateInput!): VideoPlayEvent! @override(from: "api-journeys")
videoPauseEventCreate(input: VideoPauseEventCreateInput!): VideoPauseEvent! @override(from: "api-journeys")
videoCompleteEventCreate(input: VideoCompleteEventCreateInput!): VideoCompleteEvent! @override(from: "api-journeys")
videoExpandEventCreate(input: VideoExpandEventCreateInput!): VideoExpandEvent! @override(from: "api-journeys")
videoCollapseEventCreate(input: VideoCollapseEventCreateInput!): VideoCollapseEvent! @override(from: "api-journeys")
videoProgressEventCreate(input: VideoProgressEventCreateInput!): VideoProgressEvent! @override(from: "api-journeys")
journeySimpleUpdate(id: ID!, journey: Json!): Json
googleSheetsSyncCreate(input: CreateGoogleSheetsSyncInput!): GoogleSheetsSync!
googleSheetsSyncDelete(id: ID!): GoogleSheetsSync!
Expand Down Expand Up @@ -1871,8 +1909,8 @@ input QrCodesFilter {
type Query {
block(id: ID!): Block! @override(from: "api-journeys")
blocks(where: BlocksFilter): [Block!]! @override(from: "api-journeys")
customDomains(teamId: ID!): [CustomDomain!]! @override(from: "api-journeys")
customDomain(id: ID!): CustomDomain! @override(from: "api-journeys")
customDomains(teamId: ID!): [CustomDomain!]!
customDomain(id: ID!): CustomDomain!
node(id: ID!): Node
nodes(ids: [ID!]!): [Node]!
journeySimpleGet(id: ID!): Json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ builder.mutationField('chatButtonCreate', (t) =>
.prismaField({
type: ChatButtonRef,
nullable: false,
override: { from: 'api-journeys' },
args: {
journeyId: t.arg({ type: 'ID', required: true }),
input: t.arg({ type: ChatButtonCreateInput, required: false })
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { getUserFromPayload } from '@core/yoga/firebaseClient'

import { getClient } from '../../../test/client'
import { prismaMock } from '../../../test/prismaMock'
import { graphql } from '../../lib/graphql/subgraphGraphql'
import { recalculateJourneyCustomizable } from '../../lib/recalculateJourneyCustomizable/recalculateJourneyCustomizable'

jest.mock('@core/yoga/firebaseClient', () => ({
getUserFromPayload: jest.fn()
}))

jest.mock(
'../../lib/recalculateJourneyCustomizable/recalculateJourneyCustomizable',
() => ({
recalculateJourneyCustomizable: jest.fn()
})
)

const mockGetUserFromPayload = getUserFromPayload as jest.MockedFunction<
typeof getUserFromPayload
>

const mockRecalculate = recalculateJourneyCustomizable as jest.MockedFunction<
typeof recalculateJourneyCustomizable
>

describe('chatButtonRemove', () => {
const mockUser = {
id: 'userId',
firstName: 'Test',
emailVerified: true
}
const authClient = getClient({
headers: { authorization: 'token' },
context: { currentUser: mockUser }
})

const CHAT_BUTTON_REMOVE = graphql(`
mutation ChatButtonRemove($id: ID!) {
chatButtonRemove(id: $id) {
id
link
platform
customizable
}
}
`)

beforeEach(() => {
jest.clearAllMocks()
mockGetUserFromPayload.mockReturnValue(mockUser)
prismaMock.userRole.findUnique.mockResolvedValue({
id: 'userRoleId',
userId: mockUser.id,
roles: []
})
})

it('removes a chat button when authorized', async () => {
prismaMock.chatButton.delete.mockResolvedValue({
id: 'chatButtonId',
journeyId: 'journeyId',
link: 'https://m.me/user',
platform: 'facebook',
customizable: true
} as any)

const result = await authClient({
document: CHAT_BUTTON_REMOVE,
variables: { id: 'chatButtonId' }
})

expect(result).toEqual({
data: {
chatButtonRemove: {
id: 'chatButtonId',
link: 'https://m.me/user',
platform: 'facebook',
customizable: true
}
}
})

expect(prismaMock.chatButton.delete).toHaveBeenCalledWith(
expect.objectContaining({
where: { id: 'chatButtonId' }
})
)

expect(mockRecalculate).toHaveBeenCalledWith('journeyId')
})

it('removes a chat button with null fields', async () => {
prismaMock.chatButton.delete.mockResolvedValue({
id: 'chatButtonId',
journeyId: 'journeyId',
link: null,
platform: null,
customizable: null
} as any)

const result = await authClient({
document: CHAT_BUTTON_REMOVE,
variables: { id: 'chatButtonId' }
})

expect(result).toEqual({
data: {
chatButtonRemove: {
id: 'chatButtonId',
link: null,
platform: null,
customizable: null
}
}
})

expect(mockRecalculate).toHaveBeenCalledWith('journeyId')
})

it('throws error when user is not authenticated', async () => {
mockGetUserFromPayload.mockReturnValue(null)
const unauthClient = getClient({
headers: { authorization: 'token' },
context: { currentUser: null }
})

const result = await unauthClient({
document: CHAT_BUTTON_REMOVE,
variables: { id: 'chatButtonId' }
})

expect(result).toEqual({
data: null,
errors: [
expect.objectContaining({
message: expect.stringContaining('Not authorized')
})
]
})
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { prisma } from '@core/prisma/journeys/client'

import { recalculateJourneyCustomizable } from '../../lib/recalculateJourneyCustomizable/recalculateJourneyCustomizable'
import { builder } from '../builder'

import { ChatButtonRef } from './chatButton'

builder.mutationField('chatButtonRemove', (t) =>
t
.withAuth({ $any: { isAuthenticated: true, isAnonymous: true } })
.prismaField({
type: ChatButtonRef,
nullable: false,
args: {
id: t.arg({ type: 'ID', required: true })
},
resolve: async (query, _parent, args, _context) => {
const { id } = args

const result = await prisma.chatButton.delete({
...query,
where: { id }
})

await recalculateJourneyCustomizable(result.journeyId)

return result
}
})
)
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ builder.mutationField('chatButtonUpdate', (t) =>
.prismaField({
type: ChatButtonRef,
nullable: false,
override: { from: 'api-journeys' },
args: {
id: t.arg({ type: 'ID', required: true }),
journeyId: t.arg({ type: 'ID', required: true }),
Expand Down
1 change: 1 addition & 0 deletions apis/api-journeys-modern/src/schema/chatButton/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import './chatButton'
import './chatButtonCreate.mutation'
import './chatButtonRemove.mutation'
import './chatButtonUpdate.mutation'
import './inputs'
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ builder.queryField('customDomain', (t) =>
t.withAuth({ isAuthenticated: true }).prismaField({
type: CustomDomainRef,
nullable: false,
override: { from: 'api-journeys' },
args: {
id: t.arg({ type: 'ID', required: true })
},
Expand Down
Loading
Loading