diff --git a/apis/api-gateway/schema.graphql b/apis/api-gateway/schema.graphql index 8f4e6cd71e3..b18baf32469 100644 --- a/apis/api-gateway/schema.graphql +++ b/apis/api-gateway/schema.graphql @@ -552,7 +552,7 @@ type Mutation @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS videoEditionDelete(id: ID!) : VideoEdition! @join__field(graph: API_MEDIA) userImpersonate(email: String!) : String @join__field(graph: API_USERS) createVerificationRequest(input: CreateVerificationRequestInput) : Boolean @join__field(graph: API_USERS) - validateEmail(email: String!, token: String!) : User @join__field(graph: API_USERS) + validateEmail(email: String!, token: String!) : AuthenticatedUser @join__field(graph: API_USERS) } type MutationSiteCreateSuccess @join__type(graph: API_ANALYTICS) { @@ -711,21 +711,6 @@ type Query @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS) customDomains(teamId: ID!) : [CustomDomain!]! @join__field(graph: API_JOURNEYS) hosts(teamId: ID!) : [Host!]! @join__field(graph: API_JOURNEYS) integrations(teamId: ID!) : [Integration!]! @join__field(graph: API_JOURNEYS) - """ - returns all journeys that match the provided filters - If no team id is provided and template is not true then only returns journeys - where the user is not a member of a team but is an editor or owner of the - journey - """ - adminJourneys( - status: [JourneyStatus!] - template: Boolean - teamId: ID - """ - Use Last Active Team Id from JourneyProfile (if null will error) - """ - useLastActiveTeamId: Boolean - ): [Journey!]! @join__field(graph: API_JOURNEYS) adminJourneysReport(reportType: JourneysReportType!) : PowerBiEmbed @join__field(graph: API_JOURNEYS) adminJourney(id: ID!, idType: IdType) : Journey! @join__field(graph: API_JOURNEYS) journeys(where: JourneysFilter, options: JourneysQueryOptions) : [Journey!]! @join__field(graph: API_JOURNEYS) @@ -734,7 +719,6 @@ type Query @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS) journeyCollections(teamId: ID!) : [JourneyCollection]! @join__field(graph: API_JOURNEYS) journeyEventsConnection(journeyId: ID!, filter: JourneyEventsFilter, first: Int, after: String) : JourneyEventsConnection! @join__field(graph: API_JOURNEYS) journeyEventsCount(journeyId: ID!, filter: JourneyEventsFilter) : Int! @join__field(graph: API_JOURNEYS) - getJourneyProfile: JourneyProfile @join__field(graph: API_JOURNEYS) journeyTheme(journeyId: ID!) : JourneyTheme @join__field(graph: API_JOURNEYS) """ Get a list of Visitor Information by Journey @@ -767,7 +751,6 @@ type Query @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS) teams: [Team!]! @join__field(graph: API_JOURNEYS) team(id: ID!) : Team! @join__field(graph: API_JOURNEYS) userInvites(journeyId: ID!) : [UserInvite!] @join__field(graph: API_JOURNEYS) - getUserRole: UserRole @join__field(graph: API_JOURNEYS) userTeams(teamId: ID!, where: UserTeamFilterInput) : [UserTeam!]! @join__field(graph: API_JOURNEYS) userTeam(id: ID!) : UserTeam! @join__field(graph: API_JOURNEYS) userTeamInvites(teamId: ID!) : [UserTeamInvite!]! @join__field(graph: API_JOURNEYS) @@ -797,6 +780,13 @@ type Query @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS) journeySimpleGet(id: ID!) : Json @join__field(graph: API_JOURNEYS_MODERN) googleSheetsSyncs(filter: GoogleSheetsSyncsFilter!) : [GoogleSheetsSync!]! @join__field(graph: API_JOURNEYS_MODERN) integrationGooglePickerToken(integrationId: ID!) : String! @join__field(graph: API_JOURNEYS_MODERN) + adminJourneys( + status: [JourneyStatus!] + template: Boolean + teamId: ID + useLastActiveTeamId: Boolean + ): [Journey!]! @join__field(graph: API_JOURNEYS_MODERN) + getJourneyProfile: JourneyProfile @join__field(graph: API_JOURNEYS_MODERN) """ Returns a CSV formatted string with journey visitor export data including headers and visitor data with event information """ @@ -849,6 +839,7 @@ type Query @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS) """ status: [JourneyStatus!] ): [TemplateFamilyStatsBreakdownResponse!] @join__field(graph: API_JOURNEYS_MODERN) + getUserRole: UserRole @join__field(graph: API_JOURNEYS_MODERN) language(id: ID!, idType: LanguageIdType = databaseId) : Language @join__field(graph: API_LANGUAGES) languages(offset: Int, limit: Int, where: LanguagesFilter, term: String) : [Language!]! @join__field(graph: API_LANGUAGES) languagesCount(where: LanguagesFilter, term: String) : Int! @join__field(graph: API_LANGUAGES) @@ -948,8 +939,8 @@ type Query @join__type(graph: API_ANALYTICS) @join__type(graph: API_JOURNEYS) arclightApiKeys: [ArclightApiKey!]! @join__field(graph: API_MEDIA) arclightApiKeyByKey(key: String!) : ArclightApiKey @join__field(graph: API_MEDIA) me(input: MeInput) : User @join__field(graph: API_USERS) - user(id: ID!) : User @join__field(graph: API_USERS) - userByEmail(email: String!) : User @join__field(graph: API_USERS) + user(id: ID!) : AuthenticatedUser @join__field(graph: API_USERS) + userByEmail(email: String!) : AuthenticatedUser @join__field(graph: API_USERS) } type ButtonBlockSettings @join__type(graph: API_JOURNEYS) @join__type(graph: API_JOURNEYS_MODERN) { @@ -1837,7 +1828,7 @@ type IntegrationGoogle implements Integration @join__type(graph: API_JOURNEYS) id: ID! team: Team! type: IntegrationType! - user: User + user: AuthenticatedUser accountEmail: String } @@ -1892,7 +1883,7 @@ type UserJourney @join__type(graph: API_JOURNEYS, key: "id") @join__type(graph: userId: ID! journeyId: ID! role: UserJourneyRole! - user: User + user: AuthenticatedUser """ Date time of when the journey was first opened """ @@ -2181,7 +2172,7 @@ type UserInvite @join__type(graph: API_JOURNEYS, key: "id") @join__type(graph: removedAt: DateTime } -type User @join__type(graph: API_JOURNEYS, key: "id", extension: true) @join__type(graph: API_JOURNEYS_MODERN, key: "id", extension: true) @join__type(graph: API_LANGUAGES, key: "id", extension: true) @join__type(graph: API_MEDIA, key: "id", extension: true) @join__type(graph: API_USERS, key: "id") { +type AuthenticatedUser @join__type(graph: API_JOURNEYS, key: "id", extension: true) @join__type(graph: API_JOURNEYS_MODERN, key: "id", extension: true) @join__type(graph: API_LANGUAGES, key: "id", extension: true) @join__type(graph: API_MEDIA, key: "id", extension: true) @join__type(graph: API_USERS, key: "id") { id: ID! languageUserRoles: [LanguageRole!]! @join__field(graph: API_LANGUAGES) mediaUserRoles: [MediaRole!]! @join__field(graph: API_MEDIA) @@ -2193,10 +2184,8 @@ type User @join__type(graph: API_JOURNEYS, key: "id", extension: true) @join__t emailVerified: Boolean! @join__field(graph: API_USERS) } -type UserRole @join__type(graph: API_JOURNEYS, key: "id") @join__type(graph: API_JOURNEYS_MODERN, key: "id") { +type AnonymousUser @join__type(graph: API_JOURNEYS) @join__type(graph: API_JOURNEYS_MODERN) @join__type(graph: API_USERS) { id: ID! - userId: ID! - roles: [Role!] } type UserTeamInvite @join__type(graph: API_JOURNEYS) @join__type(graph: API_JOURNEYS_MODERN) { @@ -2554,6 +2543,12 @@ type TemplateFamilyStatsEventResponse @join__type(graph: API_JOURNEYS_MODERN) { visitors: Int! } +type UserRole @join__type(graph: API_JOURNEYS_MODERN, key: "id") { + id: ID! + userId: ID! + roles: [Role!] +} + type YouTube @join__type(graph: API_JOURNEYS_MODERN, key: "id primaryLanguageId", extension: true) { id: ID! primaryLanguageId: ID @@ -2890,7 +2885,7 @@ type Playlist @join__type(graph: API_MEDIA) { createdAt: DateTime! updatedAt: DateTime! slug: String! - owner: User! + owner: AuthenticatedUser! items: [PlaylistItem!]! } @@ -3294,6 +3289,8 @@ interface Node @join__type(graph: API_JOURNEYS_MODERN) { union MutationSiteCreateResult @join__type(graph: API_ANALYTICS) @join__unionMember(graph: API_ANALYTICS, member: "Error") @join__unionMember(graph: API_ANALYTICS, member: "MutationSiteCreateSuccess") = Error | MutationSiteCreateSuccess +union User @join__type(graph: API_JOURNEYS) @join__type(graph: API_JOURNEYS_MODERN) @join__type(graph: API_USERS) @join__unionMember(graph: API_JOURNEYS, member: "AuthenticatedUser") @join__unionMember(graph: API_JOURNEYS, member: "AnonymousUser") @join__unionMember(graph: API_JOURNEYS_MODERN, member: "AuthenticatedUser") @join__unionMember(graph: API_JOURNEYS_MODERN, member: "AnonymousUser") @join__unionMember(graph: API_USERS, member: "AuthenticatedUser") @join__unionMember(graph: API_USERS, member: "AnonymousUser") = AuthenticatedUser | AnonymousUser + union MediaVideo @join__type(graph: API_JOURNEYS_MODERN) @join__unionMember(graph: API_JOURNEYS_MODERN, member: "MuxVideo") @join__unionMember(graph: API_JOURNEYS_MODERN, member: "Video") @join__unionMember(graph: API_JOURNEYS_MODERN, member: "YouTube") = MuxVideo | Video | YouTube union MutationPlaylistCreateResult @join__type(graph: API_MEDIA) @join__unionMember(graph: API_MEDIA, member: "ZodError") @join__unionMember(graph: API_MEDIA, member: "MutationPlaylistCreateSuccess") = ZodError | MutationPlaylistCreateSuccess @@ -3628,14 +3625,6 @@ enum UserTeamRole @join__type(graph: API_JOURNEYS) @join__type(graph: API_JOURN member @join__enumValue(graph: API_JOURNEYS) @join__enumValue(graph: API_JOURNEYS_MODERN) } -enum Role @join__type(graph: API_JOURNEYS) @join__type(graph: API_JOURNEYS_MODERN) { - """ - User can create templates and - add them to template library - """ - publisher @join__enumValue(graph: API_JOURNEYS) @join__enumValue(graph: API_JOURNEYS_MODERN) -} - enum DeviceType @join__type(graph: API_JOURNEYS) @join__type(graph: API_JOURNEYS_MODERN) { console @join__enumValue(graph: API_JOURNEYS) @join__enumValue(graph: API_JOURNEYS_MODERN) mobile @join__enumValue(graph: API_JOURNEYS) @join__enumValue(graph: API_JOURNEYS_MODERN) @@ -3728,21 +3717,29 @@ enum PlausibleEvent @join__type(graph: API_JOURNEYS_MODERN) { journeyResponses @join__enumValue(graph: API_JOURNEYS_MODERN) } -enum LanguageIdType @join__type(graph: API_LANGUAGES) { - databaseId @join__enumValue(graph: API_LANGUAGES) - bcp47 @join__enumValue(graph: API_LANGUAGES) +enum Role @join__type(graph: API_JOURNEYS_MODERN) { + publisher @join__enumValue(graph: API_JOURNEYS_MODERN) } enum LanguageRole @join__type(graph: API_LANGUAGES) { publisher @join__enumValue(graph: API_LANGUAGES) } +enum LanguageIdType @join__type(graph: API_LANGUAGES) { + databaseId @join__enumValue(graph: API_LANGUAGES) + bcp47 @join__enumValue(graph: API_LANGUAGES) +} + enum DefaultPlatform @join__type(graph: API_MEDIA) { ios @join__enumValue(graph: API_MEDIA) android @join__enumValue(graph: API_MEDIA) web @join__enumValue(graph: API_MEDIA) } +enum MediaRole @join__type(graph: API_MEDIA) { + publisher @join__enumValue(graph: API_MEDIA) +} + enum ImageAspectRatio @join__type(graph: API_MEDIA) { hd @join__enumValue(graph: API_MEDIA) banner @join__enumValue(graph: API_MEDIA) @@ -3754,10 +3751,6 @@ enum MaxResolutionTier @join__type(graph: API_MEDIA) { uhd @join__enumValue(graph: API_MEDIA) } -enum MediaRole @join__type(graph: API_MEDIA) { - publisher @join__enumValue(graph: API_MEDIA) -} - enum SegmindModel @join__type(graph: API_MEDIA) { sdxl1__0_txt2img @join__enumValue(graph: API_MEDIA) kandinsky2__2_txt2img @join__enumValue(graph: API_MEDIA) diff --git a/apis/api-journeys-modern/schema.graphql b/apis/api-journeys-modern/schema.graphql index cdefbabbfdb..b8308c9d1e5 100644 --- a/apis/api-journeys-modern/schema.graphql +++ b/apis/api-journeys-modern/schema.graphql @@ -7,6 +7,19 @@ interface Action { parentBlock: Block! } +type AnonymousUser + @shareable +{ + id: ID! +} + +type AuthenticatedUser + @key(fields: "id") + @extends +{ + id: ID! @external +} + interface Block { id: ID! journeyId: ID! @@ -697,7 +710,7 @@ type IntegrationGoogle implements Integration type: IntegrationType! team: Team! accountEmail: String - user: User + user: AuthenticatedUser } input IntegrationGoogleCreateInput { @@ -1702,6 +1715,8 @@ type Query { journeySimpleGet(id: ID!): Json googleSheetsSyncs(filter: GoogleSheetsSyncsFilter!): [GoogleSheetsSync!]! integrationGooglePickerToken(integrationId: ID!): String! + adminJourneys(status: [JourneyStatus!], template: Boolean, teamId: ID, useLastActiveTeamId: Boolean): [Journey!]! + getJourneyProfile: JourneyProfile """ Returns a CSV formatted string with journey visitor export data including headers and visitor data with event information @@ -1760,6 +1775,7 @@ type Query { """ status: [JourneyStatus!] ): [TemplateFamilyStatsBreakdownResponse!] + getUserRole: UserRole } type RadioOptionBlock implements Block @@ -2250,12 +2266,7 @@ enum TypographyVariant { overline } -type User - @key(fields: "id") - @extends -{ - id: ID! @external -} +union User = AuthenticatedUser | AnonymousUser type UserAgent @shareable @@ -2290,7 +2301,7 @@ type UserJourney role: UserJourneyRole! openedAt: DateTime journey: Journey - user: User + user: AuthenticatedUser journeyNotification: JourneyNotification } diff --git a/apis/api-journeys-modern/src/schema/authScopes.ts b/apis/api-journeys-modern/src/schema/authScopes.ts index bf9982ad55c..9d7d4333058 100644 --- a/apis/api-journeys-modern/src/schema/authScopes.ts +++ b/apis/api-journeys-modern/src/schema/authScopes.ts @@ -76,6 +76,7 @@ async function isTeamManager({ export interface AuthScopes { isAuthenticated: boolean + isAnonymous: boolean isPublisher: boolean isValidInterop: boolean } @@ -83,6 +84,7 @@ export interface AuthScopes { export async function authScopes(context: Context) { const defaultScopes = { isAuthenticated: false, + isAnonymous: false, isPublisher: false, isValidInterop: false } @@ -91,6 +93,7 @@ export async function authScopes(context: Context) { return { ...defaultScopes, isAuthenticated: true, + isAnonymous: context.user.email == null, isPublisher: context.currentRoles.includes('publisher'), isInTeam: async (teamId: string) => await isInTeam({ context, teamId }), isIntegrationOwner: async (integrationId: string) => diff --git a/apis/api-journeys-modern/src/schema/builder.ts b/apis/api-journeys-modern/src/schema/builder.ts index d0da16b5f8f..ab2a778dc0d 100644 --- a/apis/api-journeys-modern/src/schema/builder.ts +++ b/apis/api-journeys-modern/src/schema/builder.ts @@ -35,6 +35,7 @@ export const builder = new SchemaBuilder<{ AuthScopes: AuthScopes AuthContexts: { isAuthenticated: Extract + isAnonymous: Extract isInTeam: Extract isIntegrationOwner: Extract isTeamManager: Extract diff --git a/apis/api-journeys-modern/src/schema/integration/google/google.ts b/apis/api-journeys-modern/src/schema/integration/google/google.ts index eac4baa7f79..10d1cb4d8b6 100644 --- a/apis/api-journeys-modern/src/schema/integration/google/google.ts +++ b/apis/api-journeys-modern/src/schema/integration/google/google.ts @@ -1,5 +1,5 @@ import { builder } from '../../builder' -import { UserRef } from '../../user' +import { AuthenticatedUserRef } from '../../user' import { IntegrationRef } from '../integration' export const IntegrationGoogleRef = builder.prismaObject('Integration', { @@ -11,7 +11,7 @@ export const IntegrationGoogleRef = builder.prismaObject('Integration', { accountEmail: t.exposeString('accountEmail', { nullable: true }), team: t.relation('team', { nullable: false }), user: t.field({ - type: UserRef, + type: AuthenticatedUserRef, nullable: true, resolve: async (integration) => { if (integration.userId == null) return null diff --git a/apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts b/apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts new file mode 100644 index 00000000000..9a5df4c8b0e --- /dev/null +++ b/apis/api-journeys-modern/src/schema/journey/adminJourneys.query.spec.ts @@ -0,0 +1,252 @@ +import { getUserFromPayload } from '@core/yoga/firebaseClient' + +import { getClient } from '../../../test/client' +import { prismaMock } from '../../../test/prismaMock' +import { graphql } from '../../lib/graphql/subgraphGraphql' + +jest.mock('@core/yoga/firebaseClient', () => ({ + getUserFromPayload: jest.fn() +})) + +const mockGetUserFromPayload = getUserFromPayload as jest.MockedFunction< + typeof getUserFromPayload +> + +describe('adminJourneys', () => { + const mockUser = { + id: 'userId', + email: 'test@example.com', + emailVerified: true, + firstName: 'Test', + lastName: 'User', + imageUrl: null, + roles: [] + } + + const mockAnonymousUser = { + id: 'anonUserId', + email: null, + emailVerified: false, + firstName: '', + imageUrl: null, + roles: [] + } + + const authClient = getClient({ + headers: { authorization: 'token' }, + context: { currentUser: mockUser } + }) + + const ADMIN_JOURNEYS_QUERY = graphql(` + query AdminJourneys( + $status: [JourneyStatus!] + $template: Boolean + $teamId: ID + $useLastActiveTeamId: Boolean + ) { + adminJourneys( + status: $status + template: $template + teamId: $teamId + useLastActiveTeamId: $useLastActiveTeamId + ) { + id + title + status + template + } + } + `) + + const mockJourney = { + id: 'journeyId', + title: 'Test Journey', + description: null, + slug: 'test-journey', + languageId: '529', + themeMode: 'dark', + themeName: 'base', + status: 'published', + template: false, + teamId: 'teamId', + createdAt: new Date(), + updatedAt: new Date(), + publishedAt: new Date(), + archivedAt: null, + trashedAt: null, + deletedAt: null, + featuredAt: null, + seoTitle: null, + seoDescription: null, + primaryImageBlockId: null, + creatorImageBlockId: null, + logoImageBlockId: null, + creatorDescription: null, + website: false, + showShareButton: null, + showLikeButton: null, + showDislikeButton: null, + displayTitle: null, + showHosts: null, + showChatButtons: null, + showReactionButtons: null, + showLogo: null, + showMenu: null, + showDisplayTitle: null, + showAssistant: null, + menuButtonIcon: null, + menuStepBlockId: null, + socialNodeX: null, + socialNodeY: null, + strategySlug: null, + plausibleToken: null, + templateSite: null, + fromTemplateId: null, + hostId: null, + journeyCustomizationDescription: null + } + + beforeEach(() => { + jest.clearAllMocks() + mockGetUserFromPayload.mockReturnValue(mockUser) + prismaMock.userRole.findUnique.mockResolvedValue({ + id: 'userRoleId', + userId: mockUser.id, + roles: [] + }) + }) + + it('should return journeys for authenticated user', async () => { + prismaMock.journey.findMany.mockResolvedValue([mockJourney as any]) + + const result = await authClient({ + document: ADMIN_JOURNEYS_QUERY + }) + + expect(result.data?.adminJourneys).toHaveLength(1) + expect(result.data?.adminJourneys[0]).toMatchObject({ + id: 'journeyId', + title: 'Test Journey', + status: 'published', + template: false + }) + }) + + it('should return journeys for anonymous user', async () => { + mockGetUserFromPayload.mockReturnValue(mockAnonymousUser) + prismaMock.userRole.findUnique.mockResolvedValue({ + id: 'userRoleId', + userId: mockAnonymousUser.id, + roles: [] + }) + + const anonClient = getClient({ + headers: { authorization: 'token' }, + context: { currentUser: mockAnonymousUser } + }) + + prismaMock.journey.findMany.mockResolvedValue([mockJourney as any]) + + const result = await anonClient({ + document: ADMIN_JOURNEYS_QUERY + }) + + expect(result.data?.adminJourneys).toHaveLength(1) + }) + + it('should handle useLastActiveTeamId gracefully when profile not found', async () => { + prismaMock.journeyProfile.findUnique.mockResolvedValue(null) + prismaMock.journey.findMany.mockResolvedValue([mockJourney as any]) + + const result = await authClient({ + document: ADMIN_JOURNEYS_QUERY, + variables: { useLastActiveTeamId: true } + }) + + // Should not throw, should return journeys + expect(result.errors).toBeUndefined() + expect(result.data?.adminJourneys).toBeDefined() + }) + + it('should filter by lastActiveTeamId when profile exists', async () => { + prismaMock.journeyProfile.findUnique.mockResolvedValue({ + id: 'profileId', + userId: 'userId', + lastActiveTeamId: 'teamId', + acceptedTermsAt: new Date(), + onboardingComplete: false + }) + prismaMock.journey.findMany.mockResolvedValue([mockJourney as any]) + + const result = await authClient({ + document: ADMIN_JOURNEYS_QUERY, + variables: { useLastActiveTeamId: true } + }) + + expect(result.data?.adminJourneys).toHaveLength(1) + expect(prismaMock.journey.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + where: expect.objectContaining({ + AND: expect.arrayContaining([ + expect.objectContaining({ teamId: 'teamId' }) + ]) + }) + }) + ) + }) + + it('should filter by teamId', async () => { + prismaMock.journey.findMany.mockResolvedValue([mockJourney as any]) + + const result = await authClient({ + document: ADMIN_JOURNEYS_QUERY, + variables: { teamId: 'teamId' } + }) + + expect(result.data?.adminJourneys).toHaveLength(1) + expect(prismaMock.journey.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + where: expect.objectContaining({ + AND: expect.arrayContaining([ + expect.objectContaining({ teamId: 'teamId' }) + ]) + }) + }) + ) + }) + + it('should filter by template', async () => { + prismaMock.journey.findMany.mockResolvedValue([mockJourney as any]) + + const result = await authClient({ + document: ADMIN_JOURNEYS_QUERY, + variables: { template: true } + }) + + expect(result.data?.adminJourneys).toHaveLength(1) + expect(prismaMock.journey.findMany).toHaveBeenCalledWith( + expect.objectContaining({ + where: expect.objectContaining({ + AND: expect.arrayContaining([ + expect.objectContaining({ template: true }) + ]) + }) + }) + ) + }) + + it('should reject unauthenticated users', async () => { + mockGetUserFromPayload.mockReturnValue(null) + const unauthClient = getClient({ + headers: { authorization: 'token' }, + context: { currentUser: null } + }) + + const result = await unauthClient({ + document: ADMIN_JOURNEYS_QUERY + }) + + expect(result.errors).toBeDefined() + expect(result.errors?.[0]?.message).toContain('Not authorized') + }) +}) diff --git a/apis/api-journeys-modern/src/schema/journey/adminJourneys.query.ts b/apis/api-journeys-modern/src/schema/journey/adminJourneys.query.ts new file mode 100644 index 00000000000..44bdc058261 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/journey/adminJourneys.query.ts @@ -0,0 +1,124 @@ +import { + Prisma, + JourneyStatus as PrismaJourneyStatus, + UserJourneyRole, + UserTeamRole, + prisma +} from '@core/prisma/journeys/client' + +import { builder } from '../builder' + +import { JourneyStatus } from './enums/journeyStatus' +import { JourneyRef } from './journey' + +builder.queryField('adminJourneys', (t) => + t + .withAuth({ $any: { isAuthenticated: true, isAnonymous: true } }) + .prismaField({ + type: [JourneyRef], + nullable: false, + args: { + status: t.arg({ type: [JourneyStatus], required: false }), + template: t.arg.boolean({ required: false }), + teamId: t.arg.id({ required: false }), + useLastActiveTeamId: t.arg.boolean({ required: false }) + }, + resolve: async (query, _parent, args, context) => { + const userId = context.user.id + const filter: Prisma.JourneyWhereInput = {} + + if (args.useLastActiveTeamId === true) { + const profile = await prisma.journeyProfile.findUnique({ + where: { userId } + }) + if (profile?.lastActiveTeamId != null) { + filter.teamId = profile.lastActiveTeamId + } + } + + if (args.teamId != null) { + filter.teamId = args.teamId + } else if (args.template !== true && filter.teamId == null) { + // if not looking for templates then only return journeys where: + // 1. the user is an owner or editor + // 2. not a member of the team + filter.userJourneys = { + some: { + userId, + role: { in: [UserJourneyRole.owner, UserJourneyRole.editor] } + } + } + filter.team = { + userTeams: { + none: { + userId + } + } + } + } + + if (args.template != null) filter.template = args.template + if (args.status != null) { + filter.status = { in: args.status as PrismaJourneyStatus[] } + } + + // ACL: only return journeys the user has access to + const accessibleJourneys: Prisma.JourneyWhereInput = { + OR: [ + // user is a team manager + { + team: { + userTeams: { + some: { + userId, + role: UserTeamRole.manager + } + } + } + }, + // user is a team member + { + team: { + userTeams: { + some: { + userId, + role: UserTeamRole.member + } + } + } + }, + // user is a journey owner + { + userJourneys: { + some: { + userId, + role: UserJourneyRole.owner + } + } + }, + // user is a journey editor + { + userJourneys: { + some: { + userId, + role: UserJourneyRole.editor + } + } + }, + // published templates are readable by everyone + { + template: true, + status: PrismaJourneyStatus.published + } + ] + } + + return await prisma.journey.findMany({ + ...query, + where: { + AND: [accessibleJourneys, filter] + } + }) + } + }) +) diff --git a/apis/api-journeys-modern/src/schema/journey/index.ts b/apis/api-journeys-modern/src/schema/journey/index.ts index fafa8cd6405..e9bb8479599 100644 --- a/apis/api-journeys-modern/src/schema/journey/index.ts +++ b/apis/api-journeys-modern/src/schema/journey/index.ts @@ -1,3 +1,4 @@ +import './adminJourneys.query' import './journey' import './inputs' import './enums' diff --git a/apis/api-journeys-modern/src/schema/journeyProfile/getJourneyProfile.query.spec.ts b/apis/api-journeys-modern/src/schema/journeyProfile/getJourneyProfile.query.spec.ts new file mode 100644 index 00000000000..b8b5bfd5153 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/journeyProfile/getJourneyProfile.query.spec.ts @@ -0,0 +1,129 @@ +import { getUserFromPayload } from '@core/yoga/firebaseClient' + +import { getClient } from '../../../test/client' +import { prismaMock } from '../../../test/prismaMock' +import { graphql } from '../../lib/graphql/subgraphGraphql' + +jest.mock('@core/yoga/firebaseClient', () => ({ + getUserFromPayload: jest.fn() +})) + +const mockGetUserFromPayload = getUserFromPayload as jest.MockedFunction< + typeof getUserFromPayload +> + +describe('getJourneyProfile', () => { + const mockUser = { + id: 'userId', + email: 'test@example.com', + emailVerified: true, + firstName: 'Test', + lastName: 'User', + imageUrl: null, + roles: [] + } + + const authClient = getClient({ + headers: { authorization: 'token' }, + context: { currentUser: mockUser } + }) + + const GET_JOURNEY_PROFILE_QUERY = graphql(` + query GetJourneyProfile { + getJourneyProfile { + id + userId + acceptedTermsAt + lastActiveTeamId + journeyFlowBackButtonClicked + plausibleJourneyFlowViewed + plausibleDashboardViewed + } + } + `) + + beforeEach(() => { + jest.clearAllMocks() + mockGetUserFromPayload.mockReturnValue(mockUser) + prismaMock.userRole.findUnique.mockResolvedValue({ + id: 'userRoleId', + userId: mockUser.id, + roles: [] + }) + }) + + it('should return journey profile when user is authenticated', async () => { + const profile = { + id: 'profileId', + userId: 'userId', + acceptedTermsAt: new Date('2021-02-18T00:00:00.000Z'), + lastActiveTeamId: null, + journeyFlowBackButtonClicked: null, + plausibleJourneyFlowViewed: null, + plausibleDashboardViewed: null + } + + prismaMock.journeyProfile.findUnique.mockResolvedValue(profile) + + const result = await authClient({ + document: GET_JOURNEY_PROFILE_QUERY + }) + + expect(result).toEqual({ + data: { + getJourneyProfile: { + id: 'profileId', + userId: 'userId', + acceptedTermsAt: '2021-02-18T00:00:00.000Z', + lastActiveTeamId: null, + journeyFlowBackButtonClicked: null, + plausibleJourneyFlowViewed: null, + plausibleDashboardViewed: null + } + } + }) + + expect(prismaMock.journeyProfile.findUnique).toHaveBeenCalledWith( + expect.objectContaining({ + where: { userId: 'userId' } + }) + ) + }) + + it('should return null when profile does not exist', async () => { + prismaMock.journeyProfile.findUnique.mockResolvedValue(null) + + const result = await authClient({ + document: GET_JOURNEY_PROFILE_QUERY + }) + + expect(result).toEqual({ + data: { + getJourneyProfile: null + } + }) + }) + + it('should throw error when user is not authenticated', async () => { + mockGetUserFromPayload.mockReturnValue(null) + const unauthClient = getClient({ + headers: { authorization: 'token' }, + context: { currentUser: null } + }) + + const result = await unauthClient({ + document: GET_JOURNEY_PROFILE_QUERY + }) + + expect(result).toEqual({ + data: { + getJourneyProfile: null + }, + errors: [ + expect.objectContaining({ + message: expect.stringContaining('Not authorized') + }) + ] + }) + }) +}) diff --git a/apis/api-journeys-modern/src/schema/journeyProfile/getJourneyProfile.query.ts b/apis/api-journeys-modern/src/schema/journeyProfile/getJourneyProfile.query.ts new file mode 100644 index 00000000000..2e823775f2e --- /dev/null +++ b/apis/api-journeys-modern/src/schema/journeyProfile/getJourneyProfile.query.ts @@ -0,0 +1,18 @@ +import { prisma } from '@core/prisma/journeys/client' + +import { builder } from '../builder' + +import { JourneyProfileRef } from './journeyProfile' + +builder.queryField('getJourneyProfile', (t) => + t.withAuth({ isAuthenticated: true }).prismaField({ + type: JourneyProfileRef, + nullable: true, + resolve: async (query, _parent, _args, context) => { + return await prisma.journeyProfile.findUnique({ + ...query, + where: { userId: context.user.id } + }) + } + }) +) diff --git a/apis/api-journeys-modern/src/schema/journeyProfile/index.ts b/apis/api-journeys-modern/src/schema/journeyProfile/index.ts index ac0a9fa73d9..cde31650979 100644 --- a/apis/api-journeys-modern/src/schema/journeyProfile/index.ts +++ b/apis/api-journeys-modern/src/schema/journeyProfile/index.ts @@ -1,3 +1,4 @@ +import './getJourneyProfile.query' import './journeyProfile' import './inputs' diff --git a/apis/api-journeys-modern/src/schema/user/index.ts b/apis/api-journeys-modern/src/schema/user/index.ts index ddac8a72157..c1c134bb17e 100644 --- a/apis/api-journeys-modern/src/schema/user/index.ts +++ b/apis/api-journeys-modern/src/schema/user/index.ts @@ -1,3 +1,3 @@ import './user' -export { UserRef } from './user' +export { AnonymousUserRef, AuthenticatedUserRef, UserRef } from './user' diff --git a/apis/api-journeys-modern/src/schema/user/user.ts b/apis/api-journeys-modern/src/schema/user/user.ts index 30b98bee598..087c68f88ae 100644 --- a/apis/api-journeys-modern/src/schema/user/user.ts +++ b/apis/api-journeys-modern/src/schema/user/user.ts @@ -1,15 +1,37 @@ import { builder } from '../builder' -// Define the federated User type reference - this should only be defined once -export const UserRef = builder.externalRef( - 'User', +// Define the federated AuthenticatedUser type reference - this should only be defined once +export const AuthenticatedUserRef = builder.externalRef( + 'AuthenticatedUser', builder.selection<{ id: string }>('id') ) -// Implement the external fields for the User type -UserRef.implement({ +// Implement the external fields for the AuthenticatedUser type +AuthenticatedUserRef.implement({ externalFields: (t) => ({ id: t.id({ nullable: false }) }), fields: (t) => ({ // No additional fields needed - this is just the external reference }) }) + +// AnonymousUser is not a federation entity (no @key in api-users), so we define it locally +// to match the api-users schema for the User union +// Marked as shareable since it's also defined in api-users +export const AnonymousUserRef = builder.objectRef<{ id: string }>('AnonymousUser') + +AnonymousUserRef.implement({ + shareable: true, + fields: (t) => ({ + id: t.exposeID('id', { nullable: false }) + }) +}) + +// Define the User union type to match api-users schema +export const UserRef = builder.unionType('User', { + types: [AuthenticatedUserRef, AnonymousUserRef], + resolveType: (user) => { + // In practice, UserTeam members are always authenticated users + // but the union type allows for both possibilities + return 'AuthenticatedUser' + } +}) diff --git a/apis/api-journeys-modern/src/schema/userJourney/userJourney.ts b/apis/api-journeys-modern/src/schema/userJourney/userJourney.ts index 83327b91235..9a667705319 100644 --- a/apis/api-journeys-modern/src/schema/userJourney/userJourney.ts +++ b/apis/api-journeys-modern/src/schema/userJourney/userJourney.ts @@ -1,5 +1,5 @@ import { builder } from '../builder' -import { UserRef } from '../user' +import { AuthenticatedUserRef } from '../user' import { UserJourneyRole } from './enums/userJourneyRole' @@ -17,7 +17,7 @@ export const UserJourneyRef = builder.prismaObject('UserJourney', { openedAt: t.expose('openedAt', { type: 'DateTime', nullable: true }), journey: t.relation('journey'), user: t.field({ - type: UserRef, + type: AuthenticatedUserRef, resolve: (userJourney) => ({ id: userJourney.userId }) diff --git a/apis/api-journeys-modern/src/schema/userRole/getUserRole.query.spec.ts b/apis/api-journeys-modern/src/schema/userRole/getUserRole.query.spec.ts new file mode 100644 index 00000000000..b94d2f90568 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userRole/getUserRole.query.spec.ts @@ -0,0 +1,154 @@ +import { getUserFromPayload } from '@core/yoga/firebaseClient' + +import { getClient } from '../../../test/client' +import { prismaMock } from '../../../test/prismaMock' +import { graphql } from '../../lib/graphql/subgraphGraphql' + +jest.mock('@core/yoga/firebaseClient', () => ({ + getUserFromPayload: jest.fn() +})) + +const mockGetUserFromPayload = getUserFromPayload as jest.MockedFunction< + typeof getUserFromPayload +> + +describe('getUserRole', () => { + const mockUser = { + id: 'userId', + email: 'test@example.com', + emailVerified: true, + firstName: 'Test', + lastName: 'User', + imageUrl: null, + roles: [] + } + + const authClient = getClient({ + headers: { authorization: 'token' }, + context: { currentUser: mockUser } + }) + + const GET_USER_ROLE_QUERY = graphql(` + query GetUserRole { + getUserRole { + id + userId + roles + } + } + `) + + beforeEach(() => { + jest.clearAllMocks() + mockGetUserFromPayload.mockReturnValue(mockUser) + prismaMock.userRole.findUnique.mockResolvedValue({ + id: 'userRoleId', + userId: mockUser.id, + roles: [] + }) + }) + + it('should return user role when user is authenticated', async () => { + const userRole = { + id: 'userRoleId', + userId: 'userId', + roles: [] + } + + prismaMock.userRole.upsert.mockResolvedValue(userRole) + + const result = await authClient({ + document: GET_USER_ROLE_QUERY + }) + + expect(result).toEqual({ + data: { + getUserRole: { + id: 'userRoleId', + userId: 'userId', + roles: [] + } + } + }) + + expect(prismaMock.userRole.upsert).toHaveBeenCalledWith({ + where: { userId: 'userId' }, + create: { userId: 'userId' }, + update: {} + }) + }) + + it('should return user role with roles', async () => { + const userRole = { + id: 'userRoleId', + userId: 'userId', + roles: ['publisher'] + } + + prismaMock.userRole.upsert.mockResolvedValue(userRole as any) + + const result = await authClient({ + document: GET_USER_ROLE_QUERY + }) + + expect(result).toEqual({ + data: { + getUserRole: { + id: 'userRoleId', + userId: 'userId', + roles: ['publisher'] + } + } + }) + }) + + it('should retry on unique constraint violation', async () => { + const userRole = { + id: 'userRoleId', + userId: 'userId', + roles: [] + } + + prismaMock.userRole.upsert.mockRejectedValueOnce({ code: 'P2002' }) + prismaMock.userRole.upsert.mockResolvedValue(userRole) + + const result = await authClient({ + document: GET_USER_ROLE_QUERY + }) + + expect(result).toEqual({ + data: { + getUserRole: { + id: 'userRoleId', + userId: 'userId', + roles: [] + } + } + }) + + expect(prismaMock.userRole.upsert).toHaveBeenCalledTimes(2) + }) + + it('should throw error when user is not authenticated', async () => { + mockGetUserFromPayload.mockReturnValue(null) + const unauthClient = getClient({ + headers: { authorization: 'token' }, + context: { currentUser: null } + }) + + const result = await unauthClient({ + document: GET_USER_ROLE_QUERY + }) + + expect(result).toEqual({ + data: { + getUserRole: null + }, + errors: [ + expect.objectContaining({ + message: expect.stringContaining('Not authorized') + }) + ] + }) + }) +}) diff --git a/apis/api-journeys-modern/src/schema/userRole/getUserRole.query.ts b/apis/api-journeys-modern/src/schema/userRole/getUserRole.query.ts new file mode 100644 index 00000000000..69d7e45d184 --- /dev/null +++ b/apis/api-journeys-modern/src/schema/userRole/getUserRole.query.ts @@ -0,0 +1,32 @@ +import { prisma } from '@core/prisma/journeys/client' + +import { builder } from '../builder' + +import { UserRoleRef } from './userRole' + +const ERROR_PSQL_UNIQUE_CONSTRAINT_VIOLATED = 'P2002' + +async function getUserRoleByUserId(userId: string) { + try { + return await prisma.userRole.upsert({ + where: { userId }, + create: { userId }, + update: {} + }) + } catch (err: any) { + if (err.code !== ERROR_PSQL_UNIQUE_CONSTRAINT_VIOLATED) { + throw err + } + } + return await getUserRoleByUserId(userId) +} + +builder.queryField('getUserRole', (t) => + t.withAuth({ $any: { isAuthenticated: true, isAnonymous: true } }).prismaField({ + type: UserRoleRef, + nullable: true, + resolve: async (query, _parent, _args, context) => { + return await getUserRoleByUserId(context.user.id) + } + }) +) diff --git a/apis/api-journeys-modern/src/schema/userRole/index.ts b/apis/api-journeys-modern/src/schema/userRole/index.ts index 5427355d269..d55ecc70a55 100644 --- a/apis/api-journeys-modern/src/schema/userRole/index.ts +++ b/apis/api-journeys-modern/src/schema/userRole/index.ts @@ -1 +1,3 @@ +import './getUserRole.query' + export { UserRoleRef } from './userRole' diff --git a/apis/api-journeys/db/seed.ts b/apis/api-journeys/db/seed.ts index 04d92b1c8e8..7c05c89a70a 100644 --- a/apis/api-journeys/db/seed.ts +++ b/apis/api-journeys/db/seed.ts @@ -10,6 +10,7 @@ import { nua9 } from './seeds/nua9' import { onboarding } from './seeds/onboarding' import { onboardingTemplates } from './seeds/onboardingTemplates' import { playwrightUserAccess } from './seeds/playwrightUserAccess' +import { quickStartTemplate } from './seeds/quickStartTemplate' async function main(): Promise { // this should be removed when the UI can support team management @@ -21,6 +22,7 @@ async function main(): Promise { await nua1() await onboarding() await onboardingTemplates() + await quickStartTemplate() await playwrightUserAccess() await formBlocksDelete() diff --git a/apis/api-journeys/db/seeds/playwrightUserAccess.ts b/apis/api-journeys/db/seeds/playwrightUserAccess.ts index 9e939a6c6d3..c3d664e312e 100644 --- a/apis/api-journeys/db/seeds/playwrightUserAccess.ts +++ b/apis/api-journeys/db/seeds/playwrightUserAccess.ts @@ -2,7 +2,6 @@ import { v4 as uuidv4 } from 'uuid' import { JourneyStatus, - Role, ThemeMode, ThemeName, UserTeamRole @@ -52,6 +51,10 @@ const PLAYWRIGHT_USER_DATA = [ } ] +enum Role { + publisher = 'publisher' +} + interface UserAccessData { journeyData: { id: string diff --git a/apis/api-journeys/db/seeds/quickStartTemplate.ts b/apis/api-journeys/db/seeds/quickStartTemplate.ts new file mode 100644 index 00000000000..2f5bdbbb03d --- /dev/null +++ b/apis/api-journeys/db/seeds/quickStartTemplate.ts @@ -0,0 +1,140 @@ +import { PrismaClient } from '.prisma/api-journeys-client' + +import { + JourneyStatus, + ThemeMode, + ThemeName +} from '../../src/app/__generated__/graphql' + +const prisma = new PrismaClient() + +const QUICK_START_TEMPLATE = { + id: 'b4a4e122-2b7f-4e6f-a2d1-81c1f792c92b', + slug: 'quick-start-template' +} + +const CUSTOMIZATION_DESCRIPTION = + 'Hi {{ name }}, welcome to your journey! We are glad you are here.' + +export async function quickStartTemplate(action?: 'reset'): Promise { + if (action === 'reset') { + const existingJourney = await prisma.journey.findUnique({ + where: { slug: QUICK_START_TEMPLATE.slug } + }) + if (existingJourney != null) { + await prisma.journey.delete({ where: { id: existingJourney.id } }) + } + } + + const existingJourney = await prisma.journey.findUnique({ + where: { slug: QUICK_START_TEMPLATE.slug } + }) + if (existingJourney != null) return + + const journey = await prisma.journey.create({ + data: { + id: QUICK_START_TEMPLATE.id, + title: 'Quick Start', + description: 'A customizable quick start template to get you going.', + languageId: '529', + themeMode: ThemeMode.dark, + themeName: ThemeName.base, + slug: QUICK_START_TEMPLATE.slug, + status: JourneyStatus.published, + template: true, + createdAt: new Date(), + publishedAt: new Date(), + teamId: 'jfp-team', + journeyCustomizationDescription: CUSTOMIZATION_DESCRIPTION + } + }) + + // Create customization fields parsed from the description + await prisma.journeyCustomizationField.create({ + data: { + journeyId: journey.id, + key: 'name', + value: null, + defaultValue: null + } + }) + + // Primary image + const primaryImageBlock = await prisma.block.create({ + data: { + journeyId: journey.id, + typename: 'ImageBlock', + src: 'https://imagedelivery.net/tMY86qEHFACTO8_0kAeRFA/e8692352-21c7-4f66-cb57-0298e86a3300/public', + alt: 'quick start primary', + width: 1152, + height: 768, + blurhash: 'UE9Qmr%MIpWCtmbH%Mxu_4xuWYoL-;oIWYt7', + parentOrder: 1 + } + }) + await prisma.journey.update({ + where: { id: journey.id }, + data: { primaryImageBlockId: primaryImageBlock.id } + }) + + // Step block + const step = await prisma.block.create({ + data: { + journeyId: journey.id, + typename: 'StepBlock', + locked: false, + parentOrder: 0 + } + }) + + // Card block + const card = await prisma.block.create({ + data: { + journeyId: journey.id, + typename: 'CardBlock', + parentBlockId: step.id, + fullscreen: false, + parentOrder: 0 + } + }) + + // Cover image for the card + const coverBlock = await prisma.block.create({ + data: { + journeyId: journey.id, + typename: 'ImageBlock', + src: 'https://imagedelivery.net/tMY86qEHFACTO8_0kAeRFA/ae95a856-1401-41e1-6f3e-7b4e6f707f00/public', + alt: 'quick start card cover', + width: 1152, + height: 768, + blurhash: 'UbLX6?~p9FtRkX.8ogD%IUj@M{adxaM_ofkW', + parentBlockId: card.id + } + }) + await prisma.block.update({ + where: { id: card.id }, + data: { coverBlockId: coverBlock.id } + }) + + // Typography blocks with customization placeholder + await prisma.block.createMany({ + data: [ + { + journeyId: journey.id, + typename: 'TypographyBlock', + parentBlockId: card.id, + content: 'Welcome, {{ name }}!', + variant: 'h3', + parentOrder: 0 + }, + { + journeyId: journey.id, + typename: 'TypographyBlock', + parentBlockId: card.id, + content: 'Start your journey here and explore what is possible.', + variant: 'body1', + parentOrder: 1 + } + ] + }) +} diff --git a/apis/api-journeys/schema.graphql b/apis/api-journeys/schema.graphql index d86f741d92c..36c79df6a51 100644 --- a/apis/api-journeys/schema.graphql +++ b/apis/api-journeys/schema.graphql @@ -231,20 +231,6 @@ extend type Query { hosts(teamId: ID!): [Host!]! integrations(teamId: ID!): [Integration!]! - """ - returns all journeys that match the provided filters - If no team id is provided and template is not true then only returns journeys - where the user is not a member of a team but is an editor or owner of the - journey - """ - adminJourneys( - status: [JourneyStatus!] - template: Boolean - teamId: ID - - """Use Last Active Team Id from JourneyProfile (if null will error)""" - useLastActiveTeamId: Boolean - ): [Journey!]! adminJourneysReport(reportType: JourneysReportType!): PowerBiEmbed adminJourney(id: ID!, idType: IdType): Journey! journeys(where: JourneysFilter, options: JourneysQueryOptions): [Journey!]! @@ -253,7 +239,6 @@ extend type Query { journeyCollections(teamId: ID!): [JourneyCollection]! journeyEventsConnection(journeyId: ID!, filter: JourneyEventsFilter, first: Int, after: String): JourneyEventsConnection! journeyEventsCount(journeyId: ID!, filter: JourneyEventsFilter): Int! - getJourneyProfile: JourneyProfile journeyTheme(journeyId: ID!): JourneyTheme """Get a list of Visitor Information by Journey""" @@ -279,7 +264,6 @@ extend type Query { teams: [Team!]! team(id: ID!): Team! userInvites(journeyId: ID!): [UserInvite!] - getUserRole: UserRole userTeams(teamId: ID!, where: UserTeamFilterInput): [UserTeam!]! userTeam(id: ID!): UserTeam! userTeamInvites(teamId: ID!): [UserTeamInvite!]! @@ -2106,7 +2090,7 @@ type IntegrationGoogle implements Integration id: ID! team: Team! type: IntegrationType! - user: User + user: AuthenticatedUser accountEmail: String } @@ -2302,7 +2286,7 @@ type UserJourney userId: ID! journeyId: ID! role: UserJourneyRole! - user: User + user: AuthenticatedUser """Date time of when the journey was first opened""" openedAt: DateTime @@ -2716,28 +2700,19 @@ enum UserJourneyRole { owner } -extend type User +extend type AuthenticatedUser @key(fields: "id") { id: ID! @external } -type UserRole - @key(fields: "id") +type AnonymousUser @shareable { id: ID! - userId: ID! - roles: [Role!] } -enum Role { - """ - User can create templates and - add them to template library - """ - publisher -} +union User = AuthenticatedUser | AnonymousUser enum UserTeamRole { manager @@ -3048,4 +3023,4 @@ type _Service { sdl: String } -union _Entity = Journey | JourneyProfile | JourneyVisitor | Language | ShortLink | Tag | Team | User | UserInvite | UserJourney | UserRole | UserTeam | Video | VideoBlock | Visitor +union _Entity = AuthenticatedUser | Journey | JourneyProfile | JourneyVisitor | Language | ShortLink | Tag | Team | UserInvite | UserJourney | UserTeam | Video | VideoBlock | Visitor diff --git a/apis/api-journeys/src/__generated__/graphql.ts b/apis/api-journeys/src/__generated__/graphql.ts index 6ae8342f590..c536beb7e19 100644 --- a/apis/api-journeys/src/__generated__/graphql.ts +++ b/apis/api-journeys/src/__generated__/graphql.ts @@ -34,6 +34,11 @@ export type Action = { parentBlockId: Scalars['ID']['output']; }; +export type AnonymousUser = { + __typename?: 'AnonymousUser'; + id: Scalars['ID']['output']; +}; + export type ArclightApiKey = { __typename?: 'ArclightApiKey'; defaultPlatform: DefaultPlatform; @@ -52,6 +57,19 @@ export type AudioPreview = { value: Scalars['String']['output']; }; +export type AuthenticatedUser = { + __typename?: 'AuthenticatedUser'; + email: Scalars['String']['output']; + emailVerified: Scalars['Boolean']['output']; + firstName: Scalars['String']['output']; + id: Scalars['ID']['output']; + imageUrl?: Maybe; + languageUserRoles: Array; + lastName?: Maybe; + mediaUserRoles: Array; + superAdmin?: Maybe; +}; + export type BaseError = { message?: Maybe; }; @@ -994,7 +1012,7 @@ export type IntegrationGoogle = Integration & { id: Scalars['ID']['output']; team: Team; type: IntegrationType; - user?: Maybe; + user?: Maybe; }; export type IntegrationGoogleCreateInput = { @@ -1817,6 +1835,8 @@ export type Mutation = { deleteMuxVideo: Scalars['Boolean']['output']; enableMuxDownload?: Maybe; fixVideoLanguages: Scalars['Boolean']['output']; + /** Triggers a backfill of the Google Sheets sync. Clears existing data and re-exports all events. */ + googleSheetsSyncBackfill: GoogleSheetsSync; googleSheetsSyncCreate: GoogleSheetsSync; googleSheetsSyncDelete: GoogleSheetsSync; hostCreate: Host; @@ -1940,7 +1960,7 @@ export type Mutation = { userTeamInviteCreate?: Maybe; userTeamInviteRemove: UserTeamInvite; userTeamUpdate: UserTeam; - validateEmail?: Maybe; + validateEmail?: Maybe; videoBlockCreate: VideoBlock; videoBlockUpdate: VideoBlock; videoCollapseEventCreate: VideoCollapseEvent; @@ -2290,6 +2310,11 @@ export type MutationFixVideoLanguagesArgs = { }; +export type MutationGoogleSheetsSyncBackfillArgs = { + id: Scalars['ID']['input']; +}; + + export type MutationGoogleSheetsSyncCreateArgs = { input: CreateGoogleSheetsSyncInput; }; @@ -3570,7 +3595,7 @@ export type Playlist = { note?: Maybe; noteSharedAt?: Maybe; noteUpdatedAt?: Maybe; - owner: User; + owner: AuthenticatedUser; sharedAt?: Maybe; slug: Scalars['String']['output']; updatedAt: Scalars['DateTime']['output']; @@ -3767,8 +3792,8 @@ export type Query = { teams: Array; templateFamilyStatsAggregate?: Maybe; templateFamilyStatsBreakdown?: Maybe>; - user?: Maybe; - userByEmail?: Maybe; + user?: Maybe; + userByEmail?: Maybe; userInvites?: Maybe>; userTeam: UserTeam; userTeamInvites: Array; @@ -4395,10 +4420,6 @@ export enum RedirectType { } export enum Role { - /** - * User can create templates and - * add them to template library - */ Publisher = 'publisher' } @@ -5094,18 +5115,7 @@ export type UnsplashUserLinks = { self: Scalars['String']['output']; }; -export type User = { - __typename?: 'User'; - email: Scalars['String']['output']; - emailVerified: Scalars['Boolean']['output']; - firstName: Scalars['String']['output']; - id: Scalars['ID']['output']; - imageUrl?: Maybe; - languageUserRoles: Array; - lastName?: Maybe; - mediaUserRoles: Array; - superAdmin?: Maybe; -}; +export type User = AnonymousUser | AuthenticatedUser; /** These types are a subset provided by the @types/ua-parser-js library. */ export type UserAgent = { @@ -5138,7 +5148,7 @@ export type UserJourney = { /** Date time of when the journey was first opened */ openedAt?: Maybe; role: UserJourneyRole; - user?: Maybe; + user?: Maybe; userId: Scalars['ID']['output']; }; diff --git a/apis/api-journeys/src/app/__generated__/graphql.ts b/apis/api-journeys/src/app/__generated__/graphql.ts index 3a791603c5b..cd262e8432f 100644 --- a/apis/api-journeys/src/app/__generated__/graphql.ts +++ b/apis/api-journeys/src/app/__generated__/graphql.ts @@ -272,10 +272,6 @@ export enum UserJourneyRole { owner = "owner" } -export enum Role { - publisher = "publisher" -} - export enum UserTeamRole { manager = "manager", member = "member" @@ -1001,8 +997,6 @@ export abstract class IQuery { abstract integrations(teamId: string): Integration[] | Promise; - abstract adminJourneys(status?: Nullable, template?: Nullable, teamId?: Nullable, useLastActiveTeamId?: Nullable): Journey[] | Promise; - abstract adminJourneysReport(reportType: JourneysReportType): Nullable | Promise>; abstract adminJourney(id: string, idType?: Nullable): Journey | Promise; @@ -1019,8 +1013,6 @@ export abstract class IQuery { abstract journeyEventsCount(journeyId: string, filter?: Nullable): number | Promise; - abstract getJourneyProfile(): Nullable | Promise>; - abstract journeyTheme(journeyId: string): Nullable | Promise>; abstract journeyVisitorsConnection(filter: JourneyVisitorFilter, first?: Nullable, after?: Nullable, sort?: Nullable): JourneyVisitorsConnection | Promise; @@ -1039,8 +1031,6 @@ export abstract class IQuery { abstract userInvites(journeyId: string): Nullable | Promise>; - abstract getUserRole(): Nullable | Promise>; - abstract userTeams(teamId: string, where?: Nullable): UserTeam[] | Promise; abstract userTeam(id: string): UserTeam | Promise; @@ -1705,7 +1695,7 @@ export class IntegrationGoogle implements Integration { id: string; team: Team; type: IntegrationType; - user?: Nullable; + user?: Nullable; accountEmail?: Nullable; } @@ -1742,7 +1732,7 @@ export class UserJourney { userId: string; journeyId: string; role: UserJourneyRole; - user?: Nullable; + user?: Nullable; openedAt?: Nullable; } @@ -1925,11 +1915,9 @@ export class UserInvite { removedAt?: Nullable; } -export class UserRole { - __typename?: 'UserRole'; +export class AnonymousUser { + __typename?: 'AnonymousUser'; id: string; - userId: string; - roles?: Nullable; } export class UserTeamInvite { @@ -2026,7 +2014,7 @@ export class ShortLink { id: string; } -export class User { +export class AuthenticatedUser { id: string; } @@ -2038,4 +2026,5 @@ export class ISchema { Mutation: IMutation; } +export type User = AuthenticatedUser | AnonymousUser; type Nullable = T | null; diff --git a/apis/api-journeys/src/app/app.module.ts b/apis/api-journeys/src/app/app.module.ts index 04609c0b357..63bfb3c6c10 100644 --- a/apis/api-journeys/src/app/app.module.ts +++ b/apis/api-journeys/src/app/app.module.ts @@ -33,7 +33,6 @@ import { QrCodeModule } from './modules/qrCode/qrCode.module' import { TeamModule } from './modules/team/team.module' import { UserInviteModule } from './modules/userInvite/userInvite.module' import { UserJourneyModule } from './modules/userJourney/userJourney.module' -import { UserRoleModule } from './modules/userRole/userRole.module' import { UserTeamModule } from './modules/userTeam/userTeam.module' import { UserTeamInviteModule } from './modules/userTeamInvite/userTeamInvite.module' import { VisitorModule } from './modules/visitor/visitor.module' @@ -62,7 +61,6 @@ import { VisitorModule } from './modules/visitor/visitor.module' TeamModule, UserJourneyModule, UserInviteModule, - UserRoleModule, UserTeamModule, UserTeamInviteModule, VisitorModule, diff --git a/apis/api-journeys/src/app/modules/integration/google/google.graphql b/apis/api-journeys/src/app/modules/integration/google/google.graphql index 9fb929288dc..461229120b8 100644 --- a/apis/api-journeys/src/app/modules/integration/google/google.graphql +++ b/apis/api-journeys/src/app/modules/integration/google/google.graphql @@ -2,6 +2,6 @@ type IntegrationGoogle implements Integration @shareable { id: ID! team: Team! type: IntegrationType! - user: User + user: AuthenticatedUser accountEmail: String } diff --git a/apis/api-journeys/src/app/modules/integration/google/google.resolver.ts b/apis/api-journeys/src/app/modules/integration/google/google.resolver.ts index deb0ac95e8b..82d863ef5db 100644 --- a/apis/api-journeys/src/app/modules/integration/google/google.resolver.ts +++ b/apis/api-journeys/src/app/modules/integration/google/google.resolver.ts @@ -7,8 +7,8 @@ export class IntegrationGoogleResolver { @ResolveField() user( @Parent() integration: Integration - ): { __typename: 'User'; id: string } | null { + ): { __typename: 'AuthenticatedUser'; id: string } | null { if (integration.userId == null) return null - return { __typename: 'User', id: integration.userId } + return { __typename: 'AuthenticatedUser', id: integration.userId } } } diff --git a/apis/api-journeys/src/app/modules/journey/journey.graphql b/apis/api-journeys/src/app/modules/journey/journey.graphql index ebc9fb460b9..07394b0ec31 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.graphql +++ b/apis/api-journeys/src/app/modules/journey/journey.graphql @@ -154,21 +154,6 @@ type PowerBiEmbed @shareable { } extend type Query { - """ - returns all journeys that match the provided filters - If no team id is provided and template is not true then only returns journeys - where the user is not a member of a team but is an editor or owner of the - journey - """ - adminJourneys( - status: [JourneyStatus!] - template: Boolean - teamId: ID - """ - Use Last Active Team Id from JourneyProfile (if null will error) - """ - useLastActiveTeamId: Boolean - ): [Journey!]! adminJourneysReport(reportType: JourneysReportType!): PowerBiEmbed adminJourney(id: ID!, idType: IdType): Journey! journeys(where: JourneysFilter, options: JourneysQueryOptions): [Journey!]! diff --git a/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts b/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts index 1787f1ad227..67f7ef30abb 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts +++ b/apis/api-journeys/src/app/modules/journey/journey.resolver.spec.ts @@ -12,7 +12,6 @@ import { Host, Journey, JourneyCollection, - JourneyProfile, Prisma, Team, ThemeMode, @@ -360,155 +359,6 @@ describe('JourneyResolver', () => { }) }) - describe('adminJourneys', () => { - const journeysSharedWithMe: Prisma.JourneyWhereInput = { - userJourneys: { - some: { - userId: 'userId', - role: { in: [UserJourneyRole.owner, UserJourneyRole.editor] } - } - }, - team: { - userTeams: { - none: { - userId: 'userId' - } - } - } - } - - beforeEach(() => { - prismaService.journey.findMany.mockResolvedValueOnce([journey]) - }) - - it('should get journeys that are shared with me', async () => { - expect( - await resolver.adminJourneys('userId', accessibleJourneys) - ).toEqual([journey]) - expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { - AND: [accessibleJourneys, journeysSharedWithMe] - } - }) - }) - - it('should get filtered journeys', async () => { - expect( - await resolver.adminJourneys( - 'userId', - accessibleJourneys, - [JourneyStatus.archived], - false, - 'teamId' - ) - ).toEqual([journey]) - expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { - AND: [ - accessibleJourneys, - { - status: { in: [JourneyStatus.archived] }, - template: false, - teamId: 'teamId' - } - ] - } - }) - }) - - describe('status', () => { - it('should get journeys that are shared with me with status', async () => { - expect( - await resolver.adminJourneys('userId', accessibleJourneys, [ - JourneyStatus.draft - ]) - ).toEqual([journey]) - expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { - AND: [ - accessibleJourneys, - { ...journeysSharedWithMe, status: { in: [JourneyStatus.draft] } } - ] - } - }) - }) - }) - - describe('template', () => { - it('should get template journeys', async () => { - expect( - await resolver.adminJourneys( - 'userId', - accessibleJourneys, - undefined, - true - ) - ).toEqual([journey]) - expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { - AND: [accessibleJourneys, { template: true }] - } - }) - }) - }) - - describe('useLastActiveTeamId', () => { - it('should get journeys belonging to last active team', async () => { - prismaService.journeyProfile.findUnique.mockResolvedValue({ - lastActiveTeamId: 'teamId' - } as unknown as JourneyProfile) - expect( - await resolver.adminJourneys( - 'userId', - accessibleJourneys, - undefined, - undefined, - undefined, - true - ) - ).toEqual([journey]) - expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { - AND: [accessibleJourneys, { teamId: 'teamId' }] - } - }) - }) - - it('should throw error if profile not found', async () => { - prismaService.journeyProfile.findUnique.mockResolvedValue(null) - await expect( - resolver.adminJourneys( - 'userId', - accessibleJourneys, - undefined, - undefined, - undefined, - true - ) - ).rejects.toThrow('journey profile not found') - }) - }) - - describe('teamId', () => { - it('should get journeys belonging to team', async () => { - expect( - await resolver.adminJourneys( - 'userId', - accessibleJourneys, - undefined, - undefined, - 'teamId' - ) - ).toEqual([journey]) - expect(prismaService.journey.findMany).toHaveBeenCalledWith({ - where: { - AND: [accessibleJourneys, { teamId: 'teamId' }] - } - }) - }) - }) - }) - describe('adminJourney', () => { it('returns journey by slug', async () => { prismaService.journey.findUnique.mockResolvedValueOnce( diff --git a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts index ee0d0d09563..96500671cce 100644 --- a/apis/api-journeys/src/app/modules/journey/journey.resolver.ts +++ b/apis/api-journeys/src/app/modules/journey/journey.resolver.ts @@ -129,56 +129,6 @@ export class JourneyResolver { } } - @Query() - @UseGuards(AppCaslGuard) - async adminJourneys( - @CurrentUserId() userId: string, - @CaslAccessible('Journey') accessibleJourneys: Prisma.JourneyWhereInput, - @Args('status') status?: JourneyStatus[], - @Args('template') template?: boolean, - @Args('teamId') teamId?: string, - @Args('useLastActiveTeamId') useLastActiveTeamId?: boolean - ): Promise { - const filter: Prisma.JourneyWhereInput = {} - if (useLastActiveTeamId === true) { - const profile = await this.prismaService.journeyProfile.findUnique({ - where: { userId } - }) - if (profile == null) - throw new GraphQLError('journey profile not found', { - extensions: { code: 'NOT_FOUND' } - }) - filter.teamId = profile.lastActiveTeamId ?? undefined - } - if (teamId != null) { - filter.teamId = teamId - } else if (template !== true && filter.teamId == null) { - // if not looking for templates then only return journeys where: - // 1. the user is an owner or editor - // 2. not a member of the team - filter.userJourneys = { - some: { - userId, - role: { in: [UserJourneyRole.owner, UserJourneyRole.editor] } - } - } - filter.team = { - userTeams: { - none: { - userId - } - } - } - } - if (template != null) filter.template = template - if (status != null) filter.status = { in: status } - return await this.prismaService.journey.findMany({ - where: { - AND: [accessibleJourneys, filter] - } - }) - } - @Query() @UseGuards(AppCaslGuard) async adminJourney( diff --git a/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.graphql b/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.graphql index b0a21fc8854..0cca5290db3 100644 --- a/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.graphql +++ b/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.graphql @@ -8,10 +8,6 @@ type JourneyProfile @key(fields: "id") @shareable { plausibleDashboardViewed: Boolean } -extend type Query { - getJourneyProfile: JourneyProfile -} - input JourneyProfileUpdateInput { lastActiveTeamId: String journeyFlowBackButtonClicked: Boolean diff --git a/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.resolver.ts b/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.resolver.ts index 71da6d56140..38a3d8e9bb4 100644 --- a/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.resolver.ts +++ b/apis/api-journeys/src/app/modules/journeyProfile/journeyProfile.resolver.ts @@ -18,11 +18,7 @@ export class JourneyProfileResolver { private readonly mailChimpService: MailChimpService ) {} - @Query() - @UseGuards(AppCaslGuard) - async getJourneyProfile( - @CurrentUserId() userId: string - ): Promise { + async getJourneyProfile(userId: string): Promise { return await this.prismaService.journeyProfile.findUnique({ where: { userId } }) diff --git a/apis/api-journeys/src/app/modules/userJourney/userJourney.graphql b/apis/api-journeys/src/app/modules/userJourney/userJourney.graphql index 00ad8492a97..058e7a66941 100644 --- a/apis/api-journeys/src/app/modules/userJourney/userJourney.graphql +++ b/apis/api-journeys/src/app/modules/userJourney/userJourney.graphql @@ -13,13 +13,13 @@ type UserJourney @key(fields: "id") @shareable { userId: ID! journeyId: ID! role: UserJourneyRole! - user: User + user: AuthenticatedUser """ Date time of when the journey was first opened """ openedAt: DateTime } -extend type User @key(fields: "id") { +extend type AuthenticatedUser @key(fields: "id") { id: ID! @external } diff --git a/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.spec.ts b/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.spec.ts index dd0a9beeb63..5a6cd437139 100644 --- a/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.spec.ts +++ b/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.spec.ts @@ -258,7 +258,7 @@ describe('UserJourneyResolver', () => { describe('user', () => { it('returns user reference', async () => { expect(await resolver.user(userJourney)).toEqual({ - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId' }) }) diff --git a/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.ts b/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.ts index e7236f2f839..9723bdb1bef 100644 --- a/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.ts +++ b/apis/api-journeys/src/app/modules/userJourney/userJourney.resolver.ts @@ -242,7 +242,7 @@ export class UserJourneyResolver { async user( @Parent() userJourney: UserJourney ): Promise<{ __typename: string; id: string }> { - return { __typename: 'User', id: userJourney.userId } + return { __typename: 'AuthenticatedUser', id: userJourney.userId } } @ResolveField('journeyNotification') diff --git a/apis/api-journeys/src/app/modules/userRole/userRole.graphql b/apis/api-journeys/src/app/modules/userRole/userRole.graphql deleted file mode 100644 index e9e438c98d3..00000000000 --- a/apis/api-journeys/src/app/modules/userRole/userRole.graphql +++ /dev/null @@ -1,17 +0,0 @@ -type UserRole @key(fields: "id") @shareable { - id: ID! - userId: ID! - roles: [Role!] -} - -enum Role { - """ - User can create templates and - add them to template library - """ - publisher -} - -extend type Query { - getUserRole: UserRole -} diff --git a/apis/api-journeys/src/app/modules/userRole/userRole.module.ts b/apis/api-journeys/src/app/modules/userRole/userRole.module.ts deleted file mode 100644 index c42dfdc28ba..00000000000 --- a/apis/api-journeys/src/app/modules/userRole/userRole.module.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { Global, Module } from '@nestjs/common' - -import { PrismaService } from '../../lib/prisma.service' - -import { UserRoleResolver } from './userRole.resolver' -import { UserRoleService } from './userRole.service' - -@Global() -@Module({ - imports: [], - providers: [UserRoleService, UserRoleResolver, PrismaService], - exports: [UserRoleService] -}) -export class UserRoleModule {} diff --git a/apis/api-journeys/src/app/modules/userRole/userRole.resolver.spec.ts b/apis/api-journeys/src/app/modules/userRole/userRole.resolver.spec.ts deleted file mode 100644 index 57c685a309f..00000000000 --- a/apis/api-journeys/src/app/modules/userRole/userRole.resolver.spec.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing' - -import { Role } from '../../__generated__/graphql' - -import { UserRoleResolver } from './userRole.resolver' -import { UserRoleService } from './userRole.service' - -describe('UserRoleResolver', () => { - let resolver: UserRoleResolver - - it('should return user', async () => { - const user = { - id: '1', - userId: 'userId' - } - - const userRoleService = { - provide: UserRoleService, - useFactory: () => ({ - getUserRoleById: jest.fn(() => user) - }) - } - - const module: TestingModule = await Test.createTestingModule({ - providers: [UserRoleResolver, userRoleService] - }).compile() - resolver = module.get(UserRoleResolver) - - expect(await resolver.getUserRole('userId')).toEqual(user) - }) - - it('should return user with roles', async () => { - const user = { - id: '1', - userId: 'userId', - roles: [Role.publisher] - } - - const userRoleService = { - provide: UserRoleService, - useFactory: () => ({ - getUserRoleById: jest.fn(() => user) - }) - } - - const module: TestingModule = await Test.createTestingModule({ - providers: [UserRoleResolver, userRoleService] - }).compile() - resolver = module.get(UserRoleResolver) - - expect(await resolver.getUserRole('userId')).toEqual(user) - }) -}) diff --git a/apis/api-journeys/src/app/modules/userRole/userRole.resolver.ts b/apis/api-journeys/src/app/modules/userRole/userRole.resolver.ts deleted file mode 100644 index a774ba7e1dd..00000000000 --- a/apis/api-journeys/src/app/modules/userRole/userRole.resolver.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { UseGuards } from '@nestjs/common' -import { Query, Resolver } from '@nestjs/graphql' - -import { UserRole } from '@core/prisma/journeys/client' - -import { CurrentUserId } from '../../lib/decorators/CurrentUserId' -import { GqlAuthGuard } from '../../lib/GqlAuthGuard' - -import { UserRoleService } from './userRole.service' - -@Resolver('UserRole') -export class UserRoleResolver { - constructor(private readonly userRoleService: UserRoleService) {} - - @Query() - @UseGuards(GqlAuthGuard) - async getUserRole(@CurrentUserId() userId: string): Promise { - return await this.userRoleService.getUserRoleById(userId) - } -} diff --git a/apis/api-journeys/src/app/modules/userRole/userRole.service.spec.ts b/apis/api-journeys/src/app/modules/userRole/userRole.service.spec.ts deleted file mode 100644 index 316a1db6ddd..00000000000 --- a/apis/api-journeys/src/app/modules/userRole/userRole.service.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing' -import { DeepMockProxy, mockDeep } from 'jest-mock-extended' - -import { Role } from '../../__generated__/graphql' -import { PrismaService } from '../../lib/prisma.service' -import { ERROR_PSQL_UNIQUE_CONSTRAINT_VIOLATED } from '../../lib/prismaErrors' - -import { UserRoleService } from './userRole.service' - -describe('userRoleService', () => { - let service: UserRoleService, prismaService: DeepMockProxy - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [ - UserRoleService, - { - provide: PrismaService, - useValue: mockDeep() - } - ] - }).compile() - - service = module.get(UserRoleService) - prismaService = module.get( - PrismaService - ) as DeepMockProxy - }) - - afterAll(() => { - jest.resetAllMocks() - }) - - const user = { - id: '1', - userId: 'userId', - roles: [Role.publisher] - } - - describe('getUserRoleById', () => { - it('should return a user role if exists', async () => { - prismaService.userRole.upsert.mockResolvedValue(user) - expect(await service.getUserRoleById('1')).toEqual(user) - expect(prismaService.userRole.upsert).toHaveBeenCalledWith({ - where: { userId: '1' }, - update: {}, - create: { userId: '1' } - }) - }) - - it('should retry if error', async () => { - prismaService.userRole.upsert.mockRejectedValueOnce({ - code: ERROR_PSQL_UNIQUE_CONSTRAINT_VIOLATED - }) - prismaService.userRole.upsert.mockResolvedValue(user) - expect(await service.getUserRoleById('1')).toEqual(user) - expect(prismaService.userRole.upsert).toHaveBeenCalledWith({ - where: { userId: '1' }, - update: {}, - create: { userId: '1' } - }) - }) - }) -}) diff --git a/apis/api-journeys/src/app/modules/userRole/userRole.service.ts b/apis/api-journeys/src/app/modules/userRole/userRole.service.ts deleted file mode 100644 index ff7e5320dfd..00000000000 --- a/apis/api-journeys/src/app/modules/userRole/userRole.service.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { Injectable } from '@nestjs/common' - -import { UserRole } from '@core/prisma/journeys/client' - -import { PrismaService } from '../../lib/prisma.service' -import { ERROR_PSQL_UNIQUE_CONSTRAINT_VIOLATED } from '../../lib/prismaErrors' - -@Injectable() -export class UserRoleService { - constructor(private readonly prismaService: PrismaService) {} - - async getUserRoleById(userId: string): Promise { - try { - return await this.prismaService.userRole.upsert({ - where: { userId }, - create: { userId }, - update: {} - }) - } catch (err) { - if (err.code !== ERROR_PSQL_UNIQUE_CONSTRAINT_VIOLATED) { - throw err - } - } - return await this.getUserRoleById(userId) - } -} diff --git a/apis/api-journeys/src/app/modules/userTeam/userTeam.graphql b/apis/api-journeys/src/app/modules/userTeam/userTeam.graphql index 296542f6398..0c41ea32e94 100644 --- a/apis/api-journeys/src/app/modules/userTeam/userTeam.graphql +++ b/apis/api-journeys/src/app/modules/userTeam/userTeam.graphql @@ -1,7 +1,13 @@ -extend type User @key(fields: "id") { +extend type AuthenticatedUser @key(fields: "id") { id: ID! @external } +type AnonymousUser @shareable { + id: ID! +} + +union User = AuthenticatedUser | AnonymousUser + enum UserTeamRole { manager member diff --git a/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.spec.ts b/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.spec.ts index 642d0ddf010..00a7784d114 100644 --- a/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.spec.ts +++ b/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.spec.ts @@ -303,7 +303,7 @@ describe('UserTeamResolver', () => { userId: 'userId' } as unknown as UserTeam) ).resolves.toEqual({ - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId' }) }) diff --git a/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.ts b/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.ts index 5d31c1a562f..901ed599287 100644 --- a/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.ts +++ b/apis/api-journeys/src/app/modules/userTeam/userTeam.resolver.ts @@ -127,7 +127,7 @@ export class UserTeamResolver { async user( @Parent() userTeam: UserTeam ): Promise<{ __typename: string; id: string }> { - return { __typename: 'User', id: userTeam.userId } + return { __typename: 'AuthenticatedUser', id: userTeam.userId } } @ResolveField('journeyNotification') diff --git a/apis/api-languages/schema.graphql b/apis/api-languages/schema.graphql index cff25a5040e..e8710a0e50d 100644 --- a/apis/api-languages/schema.graphql +++ b/apis/api-languages/schema.graphql @@ -13,6 +13,14 @@ type AudioPreview codec: String! } +type AuthenticatedUser + @key(fields: "id") + @extends +{ + id: ID! @external + languageUserRoles: [LanguageRole!]! +} + type Continent { id: ID! name(languageId: ID, primary: Boolean): [ContinentName!]! @@ -122,12 +130,4 @@ type Query { languagesCount(where: LanguagesFilter, term: String): Int! country(id: ID!): Country countries(term: String, ids: [ID!]): [Country!]! -} - -type User - @key(fields: "id") - @extends -{ - id: ID! @external - languageUserRoles: [LanguageRole!]! } \ No newline at end of file diff --git a/apis/api-languages/src/schema/user/user.spec.ts b/apis/api-languages/src/schema/user/user.spec.ts index 03ee759248f..c3eae349df8 100644 --- a/apis/api-languages/src/schema/user/user.spec.ts +++ b/apis/api-languages/src/schema/user/user.spec.ts @@ -13,8 +13,10 @@ describe('user', () => { const VIDEO_ROLES = graphql(` query VideoRoles { - _entities(representations: [{ __typename: "User", id: "id" }]) { - ... on User { + _entities( + representations: [{ __typename: "AuthenticatedUser", id: "id" }] + ) { + ... on AuthenticatedUser { id languageUserRoles } diff --git a/apis/api-languages/src/schema/user/user.ts b/apis/api-languages/src/schema/user/user.ts index 642944b581f..2378fd1ca64 100644 --- a/apis/api-languages/src/schema/user/user.ts +++ b/apis/api-languages/src/schema/user/user.ts @@ -4,23 +4,25 @@ import { builder } from '../builder' import { LanguageRole } from './enums/languageRole' -builder.externalRef('User', builder.selection<{ id: string }>('id')).implement({ - externalFields: (t) => ({ - id: t.id({ nullable: false }) - }), - fields: (t) => ({ - languageUserRoles: t.field({ - type: [LanguageRole], - nullable: false, - resolve: async (data) => { - return ( - ( - await prisma.userLanguageRole.findUnique({ - where: { userId: data.id } - }) - )?.roles ?? [] - ) - } +builder + .externalRef('AuthenticatedUser', builder.selection<{ id: string }>('id')) + .implement({ + externalFields: (t) => ({ + id: t.id({ nullable: false }) + }), + fields: (t) => ({ + languageUserRoles: t.field({ + type: [LanguageRole], + nullable: false, + resolve: async (data) => { + return ( + ( + await prisma.userLanguageRole.findUnique({ + where: { userId: data.id } + }) + )?.roles ?? [] + ) + } + }) }) }) -}) diff --git a/apis/api-media/schema.graphql b/apis/api-media/schema.graphql index 5111a0c6174..92675da72f1 100644 --- a/apis/api-media/schema.graphql +++ b/apis/api-media/schema.graphql @@ -9,6 +9,14 @@ type ArclightApiKey defaultPlatform: DefaultPlatform! } +type AuthenticatedUser + @key(fields: "id") + @extends +{ + id: ID! @external + mediaUserRoles: [MediaRole!]! +} + interface BaseError { message: String } @@ -648,7 +656,7 @@ type Playlist { createdAt: DateTime! updatedAt: DateTime! slug: String! - owner: User! + owner: AuthenticatedUser! items: [PlaylistItem!]! } @@ -1048,14 +1056,6 @@ type UnsplashUserLinks { self: String! } -type User - @key(fields: "id") - @extends -{ - id: ID! @external - mediaUserRoles: [MediaRole!]! -} - type Video @key(fields: "id primaryLanguageId") @shareable diff --git a/apis/api-media/src/schema/playlist/playlist.ts b/apis/api-media/src/schema/playlist/playlist.ts index a2eb218f250..9a230b15aac 100644 --- a/apis/api-media/src/schema/playlist/playlist.ts +++ b/apis/api-media/src/schema/playlist/playlist.ts @@ -6,7 +6,7 @@ import { Prisma, prisma } from '@core/prisma/media/client' import { builder } from '../builder' import { IdType, IdTypeShape } from '../enums/idType' import { NotFoundError } from '../error/NotFoundError' -import { UserRef } from '../user' +import { AuthenticatedUserRef } from '../user' import { PlaylistCreateInput } from './inputs/playlistCreate' import { PlaylistUpdateInput } from './inputs/playlistUpdate' @@ -52,7 +52,7 @@ export const Playlist = builder.prismaObject('Playlist', { slug: t.expose('slug', { type: 'String', nullable: false }), owner: t.field({ nullable: false, - type: UserRef, + type: AuthenticatedUserRef, resolve: async (parent) => ({ id: parent.ownerId }) }), items: t.relation('items', { diff --git a/apis/api-media/src/schema/user/index.ts b/apis/api-media/src/schema/user/index.ts index cacb05ea5af..6bcc03363bf 100644 --- a/apis/api-media/src/schema/user/index.ts +++ b/apis/api-media/src/schema/user/index.ts @@ -1,4 +1,4 @@ import './enums' import './user' -export { UserRef } from './user' +export { AuthenticatedUserRef } from './user' diff --git a/apis/api-media/src/schema/user/user.spec.ts b/apis/api-media/src/schema/user/user.spec.ts index 7ae9de04755..cd7c2c11f84 100644 --- a/apis/api-media/src/schema/user/user.spec.ts +++ b/apis/api-media/src/schema/user/user.spec.ts @@ -13,8 +13,10 @@ describe('user', () => { const VIDEO_ROLES = graphql(` query VideoRoles { - _entities(representations: [{ __typename: "User", id: "id" }]) { - ... on User { + _entities( + representations: [{ __typename: "AuthenticatedUser", id: "id" }] + ) { + ... on AuthenticatedUser { id mediaUserRoles } diff --git a/apis/api-media/src/schema/user/user.ts b/apis/api-media/src/schema/user/user.ts index f27b1297abc..41e73ffc63a 100644 --- a/apis/api-media/src/schema/user/user.ts +++ b/apis/api-media/src/schema/user/user.ts @@ -4,12 +4,12 @@ import { builder } from '../builder' import { MediaRole } from './enums/mediaRole' -export const UserRef = builder.externalRef( - 'User', +export const AuthenticatedUserRef = builder.externalRef( + 'AuthenticatedUser', builder.selection<{ id: string }>('id') ) -UserRef.implement({ +AuthenticatedUserRef.implement({ externalFields: (t) => ({ id: t.id({ nullable: false }) }), diff --git a/apis/api-users/schema.graphql b/apis/api-users/schema.graphql index e8efcd93300..2ecc09c1355 100644 --- a/apis/api-users/schema.graphql +++ b/apis/api-users/schema.graphql @@ -1,5 +1,23 @@ extend schema - @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key"]) + @link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@shareable"]) + +type AnonymousUser + @shareable +{ + id: ID! +} + +type AuthenticatedUser + @key(fields: "id") +{ + id: ID! + firstName: String! + lastName: String + email: String! + imageUrl: String + superAdmin: Boolean + emailVerified: Boolean! +} input CreateVerificationRequestInput { redirect: String @@ -12,23 +30,13 @@ input MeInput { type Mutation { userImpersonate(email: String!): String createVerificationRequest(input: CreateVerificationRequestInput): Boolean - validateEmail(email: String!, token: String!): User + validateEmail(email: String!, token: String!): AuthenticatedUser } type Query { me(input: MeInput): User - user(id: ID!): User - userByEmail(email: String!): User + user(id: ID!): AuthenticatedUser + userByEmail(email: String!): AuthenticatedUser } -type User - @key(fields: "id") -{ - id: ID! - firstName: String! - lastName: String - email: String! - imageUrl: String - superAdmin: Boolean - emailVerified: Boolean! -} \ No newline at end of file +union User = AuthenticatedUser | AnonymousUser \ No newline at end of file diff --git a/apis/api-users/src/schema/builder.ts b/apis/api-users/src/schema/builder.ts index 47632c6ca59..c4223b53921 100644 --- a/apis/api-users/src/schema/builder.ts +++ b/apis/api-users/src/schema/builder.ts @@ -47,11 +47,13 @@ export const builder = new SchemaBuilder<{ } AuthScopes: { isAuthenticated: boolean + isAnonymous: boolean isSuperAdmin: boolean isValidInterop: boolean } AuthContexts: { isAuthenticated: Extract + isAnonymous: Extract isSuperAdmin: Extract isValidInterop: Extract } @@ -68,25 +70,27 @@ export const builder = new SchemaBuilder<{ authScopes: async (context: Context) => { switch (context.type) { case 'authenticated': + // eslint-disable-next-line no-case-declarations -- This is intentional + const user = await prisma.user.findUnique({ + where: { userId: context.currentUser.id } + }) return { - isAuthenticated: true, - isSuperAdmin: - ( - await prisma.user.findUnique({ - where: { userId: context.currentUser.id } - }) - )?.superAdmin ?? false, + isAuthenticated: user?.email != null, + isAnonymous: user?.email == null, + isSuperAdmin: user?.superAdmin ?? false, isValidInterop: false } case 'interop': return { isAuthenticated: false, + isAnonymous: false, isSuperAdmin: false, isValidInterop: true } default: return { isAuthenticated: false, + isAnonymous: false, isSuperAdmin: false, isValidInterop: false } diff --git a/apis/api-users/src/schema/user/findOrFetchUser.spec.ts b/apis/api-users/src/schema/user/findOrFetchUser.spec.ts index 578ac30459e..e3dce69fb6d 100644 --- a/apis/api-users/src/schema/user/findOrFetchUser.spec.ts +++ b/apis/api-users/src/schema/user/findOrFetchUser.spec.ts @@ -67,4 +67,42 @@ describe('findOrFetchUser', () => { undefined ) }) + + it('should create anonymous user with null email', async () => { + const { auth } = jest.requireMock('@core/yoga/firebaseClient') + auth.getUser.mockReturnValueOnce({ + id: '1', + userId: '1', + createdAt: new Date('2021-01-01T00:00:00.000Z'), + displayName: 'Anonymous', + email: undefined, + photoURL: 'https://bit.ly/3Gth4', + emailVerified: false + }) + jest.mocked(verifyUser).mockClear() + + const anonymousUser = { + ...user, + email: null, + firstName: 'Anonymous', + lastName: '' + } + prismaMock.user.findUnique.mockResolvedValueOnce(null) + prismaMock.user.create.mockResolvedValueOnce(anonymousUser) + + const data = await findOrFetchUser({}, 'userId', undefined) + + expect(data).toEqual(anonymousUser) + expect(prismaMock.user.create).toHaveBeenCalledWith({ + data: { + email: null, + emailVerified: false, + firstName: 'Anonymous', + imageUrl: 'https://bit.ly/3Gth4', + lastName: '', + userId: 'userId' + } + }) + expect(verifyUser).not.toHaveBeenCalled() + }) }) diff --git a/apis/api-users/src/schema/user/findOrFetchUser.ts b/apis/api-users/src/schema/user/findOrFetchUser.ts index 4547c686b55..38d5d656395 100644 --- a/apis/api-users/src/schema/user/findOrFetchUser.ts +++ b/apis/api-users/src/schema/user/findOrFetchUser.ts @@ -17,7 +17,7 @@ export async function findOrFetchUser( if (existingUser != null && existingUser.emailVerified == null) { const user = await prisma.user.update({ where: { - id: userId + userId }, data: { emailVerified: false @@ -64,32 +64,36 @@ export async function findOrFetchUser( userId, firstName, lastName, - email: email ?? '', + email: email ?? null, imageUrl, emailVerified } let user: User | null = null - let retry = 0 let userCreated = false - // this function can run in parallel as such it is possible for multiple - // calls to reach this point and try to create the same user - // due to the earlier firebase async call. + // This function can run in parallel; multiple calls may try to create the same user. try { user = await prisma.user.create({ data }) userCreated = true - } catch (e) { - do { - user = await prisma.user.update({ - where: { - id: userId - }, + } catch { + // Create failed (e.g. P2002 unique constraint from concurrent request). Use existing row or retry create once. + user = await prisma.user.findUnique({ + where: { userId } + }) + if (user == null) { + try { + user = await prisma.user.create({ data }) - retry++ - } while (user == null && retry < 3) + userCreated = true + } catch { + user = await prisma.user.findUnique({ + where: { userId } + }) + } + } } // after user create so it is only sent once if (email != null && userCreated && !emailVerified) diff --git a/apis/api-users/src/schema/user/objects/index.ts b/apis/api-users/src/schema/user/objects/index.ts index 88377789070..56c0793fbb8 100644 --- a/apis/api-users/src/schema/user/objects/index.ts +++ b/apis/api-users/src/schema/user/objects/index.ts @@ -1,3 +1,9 @@ import './user' -export { User } from './user' +export { + AuthenticatedUser, + AnonymousUser, + User, + isAuthenticatedUser +} from './user' +export type { UserShape } from './user' diff --git a/apis/api-users/src/schema/user/objects/user.ts b/apis/api-users/src/schema/user/objects/user.ts index adc67c1c1ed..3c96f48d62d 100644 --- a/apis/api-users/src/schema/user/objects/user.ts +++ b/apis/api-users/src/schema/user/objects/user.ts @@ -1,6 +1,17 @@ +import { User as PrismaUser } from '@core/prisma/users/client' + import { builder } from '../../builder' -export const User = builder.prismaObject('User', { +// Type for anonymous user shape +interface AnonymousUserShape { + id: string +} + +// Union member type +export type UserShape = PrismaUser | AnonymousUserShape + +export const AuthenticatedUser = builder.prismaObject('User', { + name: 'AuthenticatedUser', fields: (t) => ({ id: t.exposeID('id', { nullable: false }), firstName: t.field({ @@ -18,9 +29,39 @@ export const User = builder.prismaObject('User', { } }), lastName: t.exposeString('lastName'), - email: t.exposeString('email', { nullable: false }), + email: t.field({ + type: 'String', + nullable: false, + resolve: (user) => user.email ?? '' + }), imageUrl: t.exposeString('imageUrl'), superAdmin: t.exposeBoolean('superAdmin'), emailVerified: t.exposeBoolean('emailVerified', { nullable: false }) }) }) + +const AnonymousUserRef = builder.objectRef('AnonymousUser') + +export const AnonymousUser = builder.objectType(AnonymousUserRef, { + shareable: true, + fields: (t) => ({ + id: t.exposeID('id', { nullable: false }) + }) +}) + +export const User = builder.unionType('User', { + types: [AuthenticatedUser, AnonymousUser], + resolveType: (user) => { + if ('email' in user && user.email != null) { + return AuthenticatedUser + } + return AnonymousUser + } +}) + +// Type guard for use in resolvers +export function isAuthenticatedUser( + user: PrismaUser | AnonymousUserShape +): user is PrismaUser { + return 'email' in user && user.email != null +} diff --git a/apis/api-users/src/schema/user/user.spec.ts b/apis/api-users/src/schema/user/user.spec.ts index 681b82aeca0..a28aa1f0a1c 100644 --- a/apis/api-users/src/schema/user/user.spec.ts +++ b/apis/api-users/src/schema/user/user.spec.ts @@ -61,13 +61,18 @@ describe('api-users', () => { const ME_QUERY = graphql(` query Me { me { - id - firstName - lastName - email - imageUrl - superAdmin - emailVerified + ... on AuthenticatedUser { + id + firstName + lastName + email + imageUrl + superAdmin + emailVerified + } + ... on AnonymousUser { + id + } } } `) diff --git a/apis/api-users/src/schema/user/user.ts b/apis/api-users/src/schema/user/user.ts index 3b04e525fc0..9923abfa7e3 100644 --- a/apis/api-users/src/schema/user/user.ts +++ b/apis/api-users/src/schema/user/user.ts @@ -7,17 +7,16 @@ import { builder } from '../builder' import { findOrFetchUser } from './findOrFetchUser' import { CreateVerificationRequestInput, MeInput } from './inputs' -import { User } from './objects' +import { AuthenticatedUser, User } from './objects' import { validateEmail } from './validateEmail' import { verifyUser } from './verifyUser' -builder.asEntity(User, { +builder.asEntity(AuthenticatedUser, { key: builder.selection<{ id: string }>('id'), resolveReference: async ({ id }) => { try { - const user = await prisma.user.findUnique({ where: { userId: id } }) + const user = await findOrFetchUser({}, id, undefined) - // Handle cases where user doesn't exist if (user == null) { console.warn(`Federation: User not found for userId: ${id}`) return null @@ -47,8 +46,8 @@ builder.asEntity(User, { }) builder.queryFields((t) => ({ - me: t.withAuth({ isAuthenticated: true }).prismaField({ - type: 'User', + me: t.withAuth({ $any: { isAuthenticated: true, isAnonymous: true } }).field({ + type: User, nullable: true, args: { input: t.arg({ @@ -56,16 +55,24 @@ builder.queryFields((t) => ({ required: false }) }, - resolve: async (query, _parent, { input }, ctx) => { - return await findOrFetchUser( - query, + resolve: async (_parent, { input }, ctx) => { + const user = await findOrFetchUser( + {}, ctx.currentUser.id, input?.redirect ?? undefined ) + if (user == null) return null + + // Return appropriate type based on whether user has email + if (user.email != null) { + return user + } + // Anonymous user - only return id + return { id: user.id } } }), user: t.withAuth({ isValidInterop: true }).prismaField({ - type: 'User', + type: AuthenticatedUser, nullable: true, args: { id: t.arg.id({ required: true }) @@ -77,7 +84,7 @@ builder.queryFields((t) => ({ }) }), userByEmail: t.withAuth({ isValidInterop: true }).prismaField({ - type: 'User', + type: AuthenticatedUser, nullable: true, args: { email: t.arg.string({ required: true }) @@ -135,7 +142,7 @@ builder.mutationFields((t) => ({ } }), validateEmail: t.field({ - type: User, + type: AuthenticatedUser, args: { email: t.arg.string({ required: true }), token: t.arg.string({ required: true }) @@ -152,7 +159,7 @@ builder.mutationFields((t) => ({ extensions: { code: 'NOT_FOUND' } }) - const validatedEmail = await validateEmail(user.userId, user.email, token) + const validatedEmail = await validateEmail(user.userId, email, token) if (!validatedEmail) throw new GraphQLError('Invalid token', { extensions: { code: 'FORBIDDEN' } diff --git a/apps/journeys-admin/__generated__/CreateJourney.ts b/apps/journeys-admin/__generated__/CreateJourney.ts index ab0de3c690a..b770c2831a7 100644 --- a/apps/journeys-admin/__generated__/CreateJourney.ts +++ b/apps/journeys-admin/__generated__/CreateJourney.ts @@ -22,7 +22,7 @@ export interface CreateJourney_journeyCreate_language { } export interface CreateJourney_journeyCreate_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetAdminJourney.ts b/apps/journeys-admin/__generated__/GetAdminJourney.ts index e9f42c6f046..5acadc99fa3 100644 --- a/apps/journeys-admin/__generated__/GetAdminJourney.ts +++ b/apps/journeys-admin/__generated__/GetAdminJourney.ts @@ -667,7 +667,7 @@ export interface GetAdminJourney_journey_creatorImageBlock { } export interface GetAdminJourney_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetAdminJourneyWithPlausibleToken.ts b/apps/journeys-admin/__generated__/GetAdminJourneyWithPlausibleToken.ts index 527951e0503..1f5fc16fa52 100644 --- a/apps/journeys-admin/__generated__/GetAdminJourneyWithPlausibleToken.ts +++ b/apps/journeys-admin/__generated__/GetAdminJourneyWithPlausibleToken.ts @@ -667,7 +667,7 @@ export interface GetAdminJourneyWithPlausibleToken_journey_creatorImageBlock { } export interface GetAdminJourneyWithPlausibleToken_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetAdminJourneys.ts b/apps/journeys-admin/__generated__/GetAdminJourneys.ts index da9104ed839..014c09aa5b4 100644 --- a/apps/journeys-admin/__generated__/GetAdminJourneys.ts +++ b/apps/journeys-admin/__generated__/GetAdminJourneys.ts @@ -22,7 +22,7 @@ export interface GetAdminJourneys_journeys_language { } export interface GetAdminJourneys_journeys_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetCurrentUser.ts b/apps/journeys-admin/__generated__/GetCurrentUser.ts index a5dc3ae8fb3..bd9dbac32ca 100644 --- a/apps/journeys-admin/__generated__/GetCurrentUser.ts +++ b/apps/journeys-admin/__generated__/GetCurrentUser.ts @@ -7,12 +7,19 @@ // GraphQL query operation: GetCurrentUser // ==================================================== -export interface GetCurrentUser_me { - __typename: "User"; +export interface GetCurrentUser_me_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; email: string; } +export interface GetCurrentUser_me_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetCurrentUser_me = GetCurrentUser_me_AuthenticatedUser | GetCurrentUser_me_AnonymousUser; + export interface GetCurrentUser { me: GetCurrentUser_me | null; } diff --git a/apps/journeys-admin/__generated__/GetIntegration.ts b/apps/journeys-admin/__generated__/GetIntegration.ts index cf18665550d..adcebe2dedf 100644 --- a/apps/journeys-admin/__generated__/GetIntegration.ts +++ b/apps/journeys-admin/__generated__/GetIntegration.ts @@ -36,7 +36,7 @@ export interface GetIntegration_integrations_IntegrationGoogle_team { } export interface GetIntegration_integrations_IntegrationGoogle_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; email: string; } diff --git a/apps/journeys-admin/__generated__/GetJourney.ts b/apps/journeys-admin/__generated__/GetJourney.ts index b6b660e62b8..91902e97140 100644 --- a/apps/journeys-admin/__generated__/GetJourney.ts +++ b/apps/journeys-admin/__generated__/GetJourney.ts @@ -667,7 +667,7 @@ export interface GetJourney_journey_creatorImageBlock { } export interface GetJourney_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetJourneyWithPermissions.ts b/apps/journeys-admin/__generated__/GetJourneyWithPermissions.ts index b90d98d0951..bdb90716946 100644 --- a/apps/journeys-admin/__generated__/GetJourneyWithPermissions.ts +++ b/apps/journeys-admin/__generated__/GetJourneyWithPermissions.ts @@ -9,8 +9,8 @@ import { UserTeamRole, UserJourneyRole } from "./globalTypes"; // GraphQL query operation: GetJourneyWithPermissions // ==================================================== -export interface GetJourneyWithPermissions_journey_team_userTeams_user { - __typename: "User"; +export interface GetJourneyWithPermissions_journey_team_userTeams_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; email: string; firstName: string; id: string; @@ -18,6 +18,13 @@ export interface GetJourneyWithPermissions_journey_team_userTeams_user { lastName: string | null; } +export interface GetJourneyWithPermissions_journey_team_userTeams_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetJourneyWithPermissions_journey_team_userTeams_user = GetJourneyWithPermissions_journey_team_userTeams_user_AuthenticatedUser | GetJourneyWithPermissions_journey_team_userTeams_user_AnonymousUser; + export interface GetJourneyWithPermissions_journey_team_userTeams_journeyNotification { __typename: "JourneyNotification"; id: string; @@ -39,7 +46,7 @@ export interface GetJourneyWithPermissions_journey_team { } export interface GetJourneyWithPermissions_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetJourneyWithUserRoles.ts b/apps/journeys-admin/__generated__/GetJourneyWithUserRoles.ts index 6afdac52f88..c32e7659d40 100644 --- a/apps/journeys-admin/__generated__/GetJourneyWithUserRoles.ts +++ b/apps/journeys-admin/__generated__/GetJourneyWithUserRoles.ts @@ -10,7 +10,7 @@ import { UserJourneyRole } from "./globalTypes"; // ==================================================== export interface GetJourneyWithUserRoles_adminJourney_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; email: string; } diff --git a/apps/journeys-admin/__generated__/GetJourneys.ts b/apps/journeys-admin/__generated__/GetJourneys.ts index 67e0d5cfb78..486737ad7a6 100644 --- a/apps/journeys-admin/__generated__/GetJourneys.ts +++ b/apps/journeys-admin/__generated__/GetJourneys.ts @@ -31,7 +31,7 @@ export interface GetJourneys_journeys_journeyCustomizationFields { } export interface GetJourneys_journeys_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetLastActiveTeamIdAndTeams.ts b/apps/journeys-admin/__generated__/GetLastActiveTeamIdAndTeams.ts index 134d854294c..7df3c60dcdf 100644 --- a/apps/journeys-admin/__generated__/GetLastActiveTeamIdAndTeams.ts +++ b/apps/journeys-admin/__generated__/GetLastActiveTeamIdAndTeams.ts @@ -15,8 +15,8 @@ export interface GetLastActiveTeamIdAndTeams_getJourneyProfile { lastActiveTeamId: string | null; } -export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { - __typename: "User"; +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; @@ -24,6 +24,13 @@ export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { email: string; } +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetLastActiveTeamIdAndTeams_teams_userTeams_user = GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser | GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser; + export interface GetLastActiveTeamIdAndTeams_teams_userTeams { __typename: "UserTeam"; id: string; diff --git a/apps/journeys-admin/__generated__/GetMe.ts b/apps/journeys-admin/__generated__/GetMe.ts index e13fe28cfec..8127e590049 100644 --- a/apps/journeys-admin/__generated__/GetMe.ts +++ b/apps/journeys-admin/__generated__/GetMe.ts @@ -9,8 +9,8 @@ import { MeInput } from "./globalTypes"; // GraphQL query operation: GetMe // ==================================================== -export interface GetMe_me { - __typename: "User"; +export interface GetMe_me_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; @@ -20,6 +20,13 @@ export interface GetMe_me { emailVerified: boolean; } +export interface GetMe_me_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetMe_me = GetMe_me_AuthenticatedUser | GetMe_me_AnonymousUser; + export interface GetMe { me: GetMe_me | null; } diff --git a/apps/journeys-admin/__generated__/GetPublisherTemplate.ts b/apps/journeys-admin/__generated__/GetPublisherTemplate.ts index fa137c6cf3f..883ba964a83 100644 --- a/apps/journeys-admin/__generated__/GetPublisherTemplate.ts +++ b/apps/journeys-admin/__generated__/GetPublisherTemplate.ts @@ -667,7 +667,7 @@ export interface GetPublisherTemplate_publisherTemplate_creatorImageBlock { } export interface GetPublisherTemplate_publisherTemplate_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/GetUserTeamsAndInvites.ts b/apps/journeys-admin/__generated__/GetUserTeamsAndInvites.ts index 991c9dbcf6c..e892faa2710 100644 --- a/apps/journeys-admin/__generated__/GetUserTeamsAndInvites.ts +++ b/apps/journeys-admin/__generated__/GetUserTeamsAndInvites.ts @@ -9,8 +9,8 @@ import { UserTeamFilterInput, UserTeamRole } from "./globalTypes"; // GraphQL query operation: GetUserTeamsAndInvites // ==================================================== -export interface GetUserTeamsAndInvites_userTeams_user { - __typename: "User"; +export interface GetUserTeamsAndInvites_userTeams_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; email: string; firstName: string; id: string; @@ -18,6 +18,13 @@ export interface GetUserTeamsAndInvites_userTeams_user { lastName: string | null; } +export interface GetUserTeamsAndInvites_userTeams_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetUserTeamsAndInvites_userTeams_user = GetUserTeamsAndInvites_userTeams_user_AuthenticatedUser | GetUserTeamsAndInvites_userTeams_user_AnonymousUser; + export interface GetUserTeamsAndInvites_userTeams { __typename: "UserTeam"; id: string; diff --git a/apps/journeys-admin/__generated__/JourneyFields.ts b/apps/journeys-admin/__generated__/JourneyFields.ts index e4ab828d645..57a5d7ba854 100644 --- a/apps/journeys-admin/__generated__/JourneyFields.ts +++ b/apps/journeys-admin/__generated__/JourneyFields.ts @@ -667,7 +667,7 @@ export interface JourneyFields_creatorImageBlock { } export interface JourneyFields_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys-admin/__generated__/TeamCreate.ts b/apps/journeys-admin/__generated__/TeamCreate.ts index 9f96c886156..fe2a241b49e 100644 --- a/apps/journeys-admin/__generated__/TeamCreate.ts +++ b/apps/journeys-admin/__generated__/TeamCreate.ts @@ -9,8 +9,8 @@ import { TeamCreateInput, UserTeamRole } from "./globalTypes"; // GraphQL mutation operation: TeamCreate // ==================================================== -export interface TeamCreate_teamCreate_userTeams_user { - __typename: "User"; +export interface TeamCreate_teamCreate_userTeams_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; @@ -18,6 +18,13 @@ export interface TeamCreate_teamCreate_userTeams_user { email: string; } +export interface TeamCreate_teamCreate_userTeams_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type TeamCreate_teamCreate_userTeams_user = TeamCreate_teamCreate_userTeams_user_AuthenticatedUser | TeamCreate_teamCreate_userTeams_user_AnonymousUser; + export interface TeamCreate_teamCreate_userTeams { __typename: "UserTeam"; id: string; diff --git a/apps/journeys-admin/__generated__/UserTeamUpdate.ts b/apps/journeys-admin/__generated__/UserTeamUpdate.ts index c0d0baef764..59590db4f58 100644 --- a/apps/journeys-admin/__generated__/UserTeamUpdate.ts +++ b/apps/journeys-admin/__generated__/UserTeamUpdate.ts @@ -9,11 +9,18 @@ import { UserTeamUpdateInput, UserTeamRole } from "./globalTypes"; // GraphQL mutation operation: UserTeamUpdate // ==================================================== -export interface UserTeamUpdate_userTeamUpdate_user { - __typename: "User"; +export interface UserTeamUpdate_userTeamUpdate_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; } +export interface UserTeamUpdate_userTeamUpdate_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type UserTeamUpdate_userTeamUpdate_user = UserTeamUpdate_userTeamUpdate_user_AuthenticatedUser | UserTeamUpdate_userTeamUpdate_user_AnonymousUser; + export interface UserTeamUpdate_userTeamUpdate { __typename: "UserTeam"; role: UserTeamRole; diff --git a/apps/journeys-admin/__generated__/ValidateEmail.ts b/apps/journeys-admin/__generated__/ValidateEmail.ts index 268e2f2c1e5..4134ca0e524 100644 --- a/apps/journeys-admin/__generated__/ValidateEmail.ts +++ b/apps/journeys-admin/__generated__/ValidateEmail.ts @@ -8,7 +8,7 @@ // ==================================================== export interface ValidateEmail_validateEmail { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; emailVerified: boolean; } diff --git a/apps/journeys-admin/pages/journeys/[journeyId]/reports/visitors.tsx b/apps/journeys-admin/pages/journeys/[journeyId]/reports/visitors.tsx index f325a0cb0c5..1eb560ca5ff 100644 --- a/apps/journeys-admin/pages/journeys/[journeyId]/reports/visitors.tsx +++ b/apps/journeys-admin/pages/journeys/[journeyId]/reports/visitors.tsx @@ -198,7 +198,9 @@ function JourneyVisitorsPage({ () => !!journey?.team && !!userTeamsData?.userTeams?.some( - (userTeam) => userTeam.user.email === user.email + (userTeam) => + userTeam.user.__typename === 'AuthenticatedUser' && + userTeam.user.email === user.email ), [journey?.team, userTeamsData?.userTeams, user.email] ) diff --git a/apps/journeys-admin/pages/templates/[journeyId]/customize.tsx b/apps/journeys-admin/pages/templates/[journeyId]/customize.tsx index ca099c44110..e8ef22bbf54 100644 --- a/apps/journeys-admin/pages/templates/[journeyId]/customize.tsx +++ b/apps/journeys-admin/pages/templates/[journeyId]/customize.tsx @@ -111,5 +111,5 @@ export const getServerSideProps = withUserTokenSSR()(async ({ export default withUser({ // TODO: remove this after anon user is implemented - whenUnauthedBeforeInit: AuthAction.REDIRECT_TO_LOGIN + // whenUnauthedBeforeInit: AuthAction.REDIRECT_TO_LOGIN })(CustomizePage) diff --git a/apps/journeys-admin/pages/templates/index.tsx b/apps/journeys-admin/pages/templates/index.tsx index 00ae2733d9b..e0c96fc31b5 100644 --- a/apps/journeys-admin/pages/templates/index.tsx +++ b/apps/journeys-admin/pages/templates/index.tsx @@ -34,7 +34,11 @@ function TemplateIndexPage(): ReactElement { const router = useRouter() const { data } = useQuery(GET_ME) const { query } = useTeam() - if (data?.me?.id != null && !data?.me?.emailVerified) { + if ( + data?.me?.__typename === 'AuthenticatedUser' && + data?.me?.id != null && + !data?.me?.emailVerified + ) { void router.push('/users/verify?redirect=/templates') } diff --git a/apps/journeys-admin/pages/users/verify.tsx b/apps/journeys-admin/pages/users/verify.tsx index 3f50e3e703b..82df0d0811e 100644 --- a/apps/journeys-admin/pages/users/verify.tsx +++ b/apps/journeys-admin/pages/users/verify.tsx @@ -234,7 +234,10 @@ export const getServerSideProps = withUserTokenSSR({ query: GET_ME, variables: { input: { redirect: query.redirect ?? undefined } } }) - if (apiUser.data?.me?.emailVerified ?? false) { + if ( + apiUser.data?.me?.__typename === 'AuthenticatedUser' && + (apiUser.data?.me?.emailVerified ?? false) + ) { return { redirect: { permanent: false, diff --git a/apps/journeys-admin/src/components/AccessAvatars/AccessAvatars.stories.tsx b/apps/journeys-admin/src/components/AccessAvatars/AccessAvatars.stories.tsx index e3bc6ddd1e4..5b2c7d06417 100644 --- a/apps/journeys-admin/src/components/AccessAvatars/AccessAvatars.stories.tsx +++ b/apps/journeys-admin/src/components/AccessAvatars/AccessAvatars.stories.tsx @@ -36,7 +36,7 @@ const noImageUserJourneys: UserJourney[] = [ { ...userJourney1, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '1', firstName: 'Amin', lastName: 'One', @@ -46,7 +46,7 @@ const noImageUserJourneys: UserJourney[] = [ { ...userJourney2, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '2', firstName: 'Horace', lastName: 'Two', @@ -56,7 +56,7 @@ const noImageUserJourneys: UserJourney[] = [ { ...userJourney3, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '3', firstName: 'Coral', lastName: 'Three', diff --git a/apps/journeys-admin/src/components/AccessAvatars/data.ts b/apps/journeys-admin/src/components/AccessAvatars/data.ts index 3eb8c66ae4d..12b9c222758 100644 --- a/apps/journeys-admin/src/components/AccessAvatars/data.ts +++ b/apps/journeys-admin/src/components/AccessAvatars/data.ts @@ -7,7 +7,7 @@ export const userJourney1: UserJourney = { role: UserJourneyRole.owner, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '1', firstName: 'Amin', lastName: 'One', @@ -21,7 +21,7 @@ export const userJourney2: UserJourney = { role: UserJourneyRole.owner, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '2', firstName: 'Horace', lastName: 'Two', @@ -34,7 +34,7 @@ export const userJourney3: UserJourney = { role: UserJourneyRole.owner, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '3', firstName: 'Coral', lastName: 'Three', @@ -47,7 +47,7 @@ export const userJourney4: UserJourney = { role: UserJourneyRole.owner, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '4', firstName: 'Effie', lastName: 'Four', @@ -60,7 +60,7 @@ export const userJourney5: UserJourney = { role: UserJourneyRole.owner, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '5', firstName: 'Janelle', lastName: 'Five', @@ -74,7 +74,7 @@ export const userJourney6: UserJourney = { role: UserJourneyRole.owner, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '6', firstName: 'Drake', lastName: 'Six', @@ -88,7 +88,7 @@ export const userJourney7: UserJourney = { role: UserJourneyRole.inviteRequested, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '6', firstName: 'Drake', lastName: 'Six', diff --git a/apps/journeys-admin/src/components/AccessDialog/AccessDialog.spec.tsx b/apps/journeys-admin/src/components/AccessDialog/AccessDialog.spec.tsx index dff5a14963d..01e0d21e117 100644 --- a/apps/journeys-admin/src/components/AccessDialog/AccessDialog.spec.tsx +++ b/apps/journeys-admin/src/components/AccessDialog/AccessDialog.spec.tsx @@ -18,7 +18,7 @@ jest.mock('../../libs/useCurrentUserLazyQuery', () => ({ useCurrentUserLazyQuery: jest.fn().mockReturnValue({ loadUser: jest.fn(), data: { - __typename: 'User', + __typename: 'AuthenticatedUser', ...user1 } }) @@ -45,7 +45,7 @@ const mocks = [ id: 'userTeamId', role: 'manager', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'kujojotaro@example.com', firstName: 'Jotaro', id: 'userId', @@ -63,7 +63,7 @@ const mocks = [ id: 'userTeamId1', role: 'member', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'josukehigashikata@example.com', firstName: 'Josuke', id: 'userId1', @@ -80,7 +80,7 @@ const mocks = [ id: 'userTeamId2', role: 'member', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'KoichiHirose@example.com', firstName: 'Koichi', id: 'userId2', @@ -147,7 +147,7 @@ const mocks = [ id: 'userJourneyId4', role: 'editor', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'kujojotaro@example.com', firstName: 'Jotaro', id: 'userId', diff --git a/apps/journeys-admin/src/components/AccessDialog/AccessDialog.tsx b/apps/journeys-admin/src/components/AccessDialog/AccessDialog.tsx index 3f6b7b791f9..b888c5bada3 100644 --- a/apps/journeys-admin/src/components/AccessDialog/AccessDialog.tsx +++ b/apps/journeys-admin/src/components/AccessDialog/AccessDialog.tsx @@ -36,11 +36,16 @@ export const GET_JOURNEY_WITH_PERMISSIONS = gql` id role user { - email - firstName - id - imageUrl - lastName + ... on AuthenticatedUser { + email + firstName + id + imageUrl + lastName + } + ... on AnonymousUser { + id + } } journeyNotification(journeyId: $id) { id @@ -93,13 +98,20 @@ export function AccessDialog({ const currentUserJourney = useMemo(() => { return data?.journey?.userJourneys?.find( - (userJourney) => userJourney.user?.email === user.email + (userJourney) => + userJourney.user?.__typename === 'AuthenticatedUser' && + user?.__typename === 'AuthenticatedUser' && + userJourney.user?.email === user.email ) }, [data?.journey?.userJourneys, user]) const currentUserTeam: UserTeam | undefined = useMemo(() => { - return data?.journey?.team?.userTeams.find(({ user: { email } }) => { - return email === user?.email + return data?.journey?.team?.userTeams.find(({ user: teamUser }) => { + return ( + user?.__typename === 'AuthenticatedUser' && + teamUser.__typename === 'AuthenticatedUser' && + teamUser.email === user.email + ) }) }, [data?.journey?.team?.userTeams, user]) diff --git a/apps/journeys-admin/src/components/AccessDialog/UserList/UserList.spec.tsx b/apps/journeys-admin/src/components/AccessDialog/UserList/UserList.spec.tsx index d3140bb234b..af35b812480 100644 --- a/apps/journeys-admin/src/components/AccessDialog/UserList/UserList.spec.tsx +++ b/apps/journeys-admin/src/components/AccessDialog/UserList/UserList.spec.tsx @@ -14,7 +14,7 @@ describe('UserList', () => { __typename: 'UserJourney', role: UserJourneyRole.editor, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'user1.id', firstName: 'firstName1', lastName: 'lastName1', @@ -29,7 +29,7 @@ describe('UserList', () => { __typename: 'UserJourney', role: UserJourneyRole.owner, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'user2.id', firstName: 'firstName2', lastName: 'lastName2', diff --git a/apps/journeys-admin/src/components/AccessDialog/UserList/UserListItem/UserListItem.spec.tsx b/apps/journeys-admin/src/components/AccessDialog/UserList/UserListItem/UserListItem.spec.tsx index eaf4b40cbf7..d449e131b94 100644 --- a/apps/journeys-admin/src/components/AccessDialog/UserList/UserListItem/UserListItem.spec.tsx +++ b/apps/journeys-admin/src/components/AccessDialog/UserList/UserListItem/UserListItem.spec.tsx @@ -18,7 +18,7 @@ const owner: UserJourney = { id: 'userJourneyOwner.id', role: UserJourneyRole.owner, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'owner.id', firstName: 'ownerFirstName', lastName: 'ownerLastName', @@ -33,7 +33,7 @@ const editor: UserJourney = { id: 'userJourneyEditor.id', role: UserJourneyRole.editor, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'editor.id', firstName: 'editorFirstName', lastName: 'editorLastName', @@ -48,7 +48,7 @@ const editor2: UserJourney = { id: 'userJourneyEditor2.id', role: UserJourneyRole.editor, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'editor2.id', firstName: 'editorFirstName', lastName: 'editorLastName', @@ -63,7 +63,7 @@ const userRequest: UserJourney = { id: 'userJourneyRequest.id', role: UserJourneyRole.inviteRequested, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'request.id', firstName: 'requestFirstName', lastName: 'requestLastName', diff --git a/apps/journeys-admin/src/components/Avatar/Avatar.spec.tsx b/apps/journeys-admin/src/components/Avatar/Avatar.spec.tsx index 7ee66730961..a9242556097 100644 --- a/apps/journeys-admin/src/components/Avatar/Avatar.spec.tsx +++ b/apps/journeys-admin/src/components/Avatar/Avatar.spec.tsx @@ -6,7 +6,7 @@ import { Avatar } from '.' describe('Avatar', () => { const apiUser: ApiUser = { - __typename: 'User', + __typename: 'AuthenticatedUser', id: '1', firstName: 'Person', lastName: 'One', diff --git a/apps/journeys-admin/src/components/Avatar/Avatar.stories.tsx b/apps/journeys-admin/src/components/Avatar/Avatar.stories.tsx index 4691b03ed90..a4c14032ef6 100644 --- a/apps/journeys-admin/src/components/Avatar/Avatar.stories.tsx +++ b/apps/journeys-admin/src/components/Avatar/Avatar.stories.tsx @@ -20,7 +20,7 @@ const Template: StoryObj = { ({ __esModule: true, @@ -47,7 +51,7 @@ describe('Host', () => { __typename: 'UserTeam', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: user1.email, firstName: 'User', id: user1.id, diff --git a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.stories.tsx b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.stories.tsx index 3ba95f49c69..69224cb9c22 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.stories.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.stories.tsx @@ -42,7 +42,7 @@ const userTeam: UserTeam = { __typename: 'UserTeam', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: user.email, firstName: 'User', id: user.id, diff --git a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.tsx b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.tsx index e70fae4fa84..3af251efa0e 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/Host.tsx @@ -75,8 +75,12 @@ export function Host(): ReactElement { const userInTeam = data == null || data.userTeams.length === 0 || journey?.team == null ? false - : data.userTeams.find((userTeam) => userTeam.user.email === user.email) != - null + : data.userTeams.find( + (userTeam) => + userTeam.user?.__typename === 'AuthenticatedUser' && + user?.__typename === 'AuthenticatedUser' && + userTeam.user.email === user.email + ) != null // Fetch all hosts made for a team const [getAllTeamHosts, { data: teamHosts }] = useLazyQuery< diff --git a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.spec.tsx b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.spec.tsx index 3f4b7c793b3..68adb4eb638 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.spec.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.spec.tsx @@ -25,7 +25,7 @@ describe('HostSelection', () => { __typename: 'UserTeam', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: user.email, firstName: 'User', id: user.id, diff --git a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.stories.tsx b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.stories.tsx index dce98f5a2d7..16260f0a548 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.stories.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/Host/HostSelection/HostSelection.stories.tsx @@ -36,7 +36,7 @@ const userTeam: UserTeam = { __typename: 'UserTeam', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: user.email, firstName: 'User', id: user.id, diff --git a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/JourneyAppearance.spec.tsx b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/JourneyAppearance.spec.tsx index dcdfb166d52..56777190f19 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/JourneyAppearance.spec.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/Settings/CanvasDetails/JourneyAppearance/JourneyAppearance.spec.tsx @@ -47,7 +47,7 @@ describe('JourneyAppearance', () => { request: { query: GET_CURRENT_USER }, result: { data: { - me: { __typename: 'User', id: 'u1', email: 'u1@example.com' } + me: { __typename: 'AuthenticatedUser', id: 'u1', email: 'u1@example.com' } } } }, @@ -102,7 +102,7 @@ describe('JourneyAppearance', () => { request: { query: GET_CURRENT_USER }, result: { data: { - me: { __typename: 'User', id: 'u1', email: 'u1@example.com' } + me: { __typename: 'AuthenticatedUser', id: 'u1', email: 'u1@example.com' } } } }, @@ -160,7 +160,7 @@ describe('JourneyAppearance', () => { request: { query: GET_CURRENT_USER }, result: { data: { - me: { __typename: 'User', id: 'u1', email: 'u1@example.com' } + me: { __typename: 'AuthenticatedUser', id: 'u1', email: 'u1@example.com' } } } }, diff --git a/apps/journeys-admin/src/components/EmailVerification/EmailVerification.tsx b/apps/journeys-admin/src/components/EmailVerification/EmailVerification.tsx index d26a9fd52d1..deb0992338e 100644 --- a/apps/journeys-admin/src/components/EmailVerification/EmailVerification.tsx +++ b/apps/journeys-admin/src/components/EmailVerification/EmailVerification.tsx @@ -43,7 +43,9 @@ export function EmailVerification({ } return ( { id: 'userJourney1.id', role: UserJourneyRole.owner, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'current-user-id', email: 'current@example.com' } @@ -623,7 +623,7 @@ describe('DefaultMenu', () => { id: 'userJourney1.id', role: UserJourneyRole.editor, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'current-user-id' } } @@ -686,7 +686,7 @@ describe('DefaultMenu', () => { id: 'userJourney1.id', role: UserJourneyRole.editor, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'current-user-id' } } @@ -751,7 +751,7 @@ describe('DefaultMenu', () => { id: 'userJourney1.id', role: UserJourneyRole.editor, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'current-user-id' } } @@ -998,7 +998,7 @@ describe('DefaultMenu', () => { id: 'userJourney1.id', role: UserJourneyRole.editor, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'current-user-id' } } diff --git a/apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/DefaultMenu/DefaultMenu.tsx b/apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/DefaultMenu/DefaultMenu.tsx index 37952f146d5..346247c3448 100644 --- a/apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/DefaultMenu/DefaultMenu.tsx +++ b/apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/DefaultMenu/DefaultMenu.tsx @@ -122,6 +122,12 @@ export function DefaultMenu({ const { loadUser, data: currentUser } = useCurrentUserLazyQuery() + const isAnonymousUser = currentUser?.__typename === 'AnonymousUser' + const currentUserEmail = + currentUser?.__typename === 'AuthenticatedUser' + ? currentUser.email + : undefined + // Lazy query for journey data if context is missing const [loadJourney, { data: journeyFromLazyQuery }] = useJourneyForSharingLazyQuery() @@ -141,8 +147,8 @@ export function DefaultMenu({ [journeyWithUserRoles?.journey?.userJourneys] ) const isOwner = useMemo( - () => owner?.user?.email === currentUser?.email, - [currentUser?.email, owner?.user?.email] + () => owner?.user?.email === currentUserEmail, + [currentUserEmail, owner?.user?.email] ) useEffect(() => { @@ -155,15 +161,17 @@ export function DefaultMenu({ // Determine the current user's role in the team const teamRole = useMemo(() => { - if (activeTeam?.userTeams == null || currentUser?.email == null) + if (activeTeam?.userTeams == null || currentUserEmail == null) return undefined const userTeam = activeTeam.userTeams.find( - (userTeam) => userTeam.user.email === currentUser.email + (userTeam) => + userTeam.user.__typename === 'AuthenticatedUser' && + userTeam.user.email === currentUserEmail ) return userTeam?.role - }, [activeTeam?.userTeams, currentUser?.email]) + }, [activeTeam?.userTeams, currentUserEmail]) const isPublisher = userRoleData?.getUserRole?.roles?.includes(Role.publisher) === true @@ -178,6 +186,10 @@ export function DefaultMenu({ const isLocalTemplate = journey?.template === true && journey?.team?.id !== 'jfp-team' + if (isAnonymousUser) { + return <> + } + return ( <> = __typename: 'UserJourney', id: 'user-journey-id', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'user-id1', firstName: 'Admin', lastName: 'One', diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.spec.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.spec.tsx index 423ba27727b..faf522d035d 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.spec.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.spec.tsx @@ -100,7 +100,7 @@ describe('NavigationDrawer', () => { email: 'amin@email.com', superAdmin: true, emailVerified: true, - __typename: 'User' + __typename: 'AuthenticatedUser' } } } diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.stories.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.stories.tsx index 8930c946431..fe8d90ef096 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.stories.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/NavigationDrawer.stories.tsx @@ -58,7 +58,7 @@ const getMeMock: MockedResponse = { email: 'amin@email.com', superAdmin: true, emailVerified: true, - __typename: 'User' + __typename: 'AuthenticatedUser' } } } diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserMenu/UserMenu.spec.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserMenu/UserMenu.spec.tsx index f83aed61ca6..bfe154d8260 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserMenu/UserMenu.spec.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserMenu/UserMenu.spec.tsx @@ -43,7 +43,7 @@ describe('UserMenu', () => { { { { void diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.spec.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.spec.tsx index 51aa8b4bcb5..b74f48312c7 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.spec.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.spec.tsx @@ -70,6 +70,7 @@ jest.mock('../../../../libs/useAdminJourneysSuspenseQuery', () => ({ describe('UserNavigation', () => { const user = { + __typename: 'AuthenticatedUser', id: 'userId', displayName: 'Amin One', photoURL: 'https://bit.ly/3Gth4Yf', @@ -84,6 +85,7 @@ describe('UserNavigation', () => { mockUseSuspenseQuery.mockReturnValue({ data: { me: { + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Amin', lastName: 'One', @@ -165,6 +167,7 @@ describe('UserNavigation', () => { mockUseSuspenseQuery.mockReturnValueOnce({ data: { me: { + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Amin', lastName: 'One', diff --git a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx index 8e04420fa80..ac3781ebd96 100644 --- a/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx +++ b/apps/journeys-admin/src/components/PageWrapper/NavigationDrawer/UserNavigation/UserNavigation.tsx @@ -43,13 +43,18 @@ const UserMenu = dynamic( export const GET_ME = gql` query GetMe($input: MeInput) { me(input: $input) { - id - firstName - lastName - email - imageUrl - superAdmin - emailVerified + ... on AuthenticatedUser { + id + firstName + lastName + email + imageUrl + superAdmin + emailVerified + } + ... on AnonymousUser { + id + } } } ` @@ -130,45 +135,49 @@ export function UserNavigation({ /> )} - {data.me.superAdmin === true && ( + {data.me?.__typename === 'AuthenticatedUser' && + data.me.superAdmin === true && ( + + + + + + + )} + {data.me?.__typename === 'AuthenticatedUser' && ( - + )} - - - - - - - {profileAnchorEl !== undefined && ( - - )} + )} {impersonateOpen != null && ( { - return activeTeam?.userTeams?.find(({ user: { email } }) => { - return email === currentUser?.email - })?.role + if (currentUser?.__typename === 'AuthenticatedUser') { + return activeTeam?.userTeams?.find(({ user }) => { + return ( + user.__typename === 'AuthenticatedUser' && + user.email === currentUser?.email + ) + })?.role + } + return undefined }, [activeTeam, currentUser]) return ( diff --git a/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.spec.tsx b/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.spec.tsx index 226a2b727d8..34c78e00283 100644 --- a/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.spec.tsx +++ b/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.spec.tsx @@ -12,7 +12,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Joe', lastName: 'Bloggs', @@ -25,7 +25,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Mike', lastName: 'The Guy', @@ -38,7 +38,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Bob', lastName: 'The Builder', @@ -54,7 +54,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Joe', lastName: 'Bloggs', @@ -67,7 +67,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Mike', lastName: 'The Guy', @@ -80,7 +80,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Bob', lastName: 'The Builder', @@ -93,7 +93,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Hello', lastName: 'Kitty', @@ -106,7 +106,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Serena', lastName: 'Williams', @@ -119,7 +119,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Jonathan', lastName: 'G', @@ -132,7 +132,7 @@ describe('TeamAvatars', () => { __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Meme', lastName: 'Guy', diff --git a/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.stories.tsx b/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.stories.tsx index 827dcd71c5a..de6b2ac36e3 100644 --- a/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.stories.tsx +++ b/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.stories.tsx @@ -19,7 +19,7 @@ const userTeam: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Joe', lastName: 'Bloggs', @@ -32,7 +32,7 @@ const userTeam: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Mike', lastName: 'The Guy', @@ -45,7 +45,7 @@ const userTeam: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Bob', lastName: 'The Builder', @@ -61,7 +61,7 @@ const userTeamOverflow: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Joe', lastName: 'Bloggs', @@ -74,7 +74,7 @@ const userTeamOverflow: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Mike', lastName: 'The Guy', @@ -87,7 +87,7 @@ const userTeamOverflow: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Bob', lastName: 'The Builder', @@ -100,7 +100,7 @@ const userTeamOverflow: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Hello', lastName: 'Kitty', @@ -113,7 +113,7 @@ const userTeamOverflow: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Serena', lastName: 'Williams', @@ -126,7 +126,7 @@ const userTeamOverflow: UserTeams[] = [ __typename: 'UserTeam', id: 'userTeamId1', user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', firstName: 'Jonathan', lastName: 'G', diff --git a/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.tsx b/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.tsx index 8f7b9d077f5..4ca624a51b3 100644 --- a/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.tsx +++ b/apps/journeys-admin/src/components/Team/TeamAvatars/TeamAvatars.tsx @@ -49,9 +49,11 @@ export function TeamAvatars({ } }} > - {take(userTeams, 5).map(({ user }) => ( - - ))} + {take(userTeams, 5).map(({ user }) => + user.__typename === 'AuthenticatedUser' ? ( + + ) : null + )} {onClick != null && ( ({ useCurrentUserLazyQuery: jest.fn().mockReturnValue({ loadUser: jest.fn(), data: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', email: 'miguelohara@example.com' } @@ -55,7 +55,7 @@ describe('TeamManageDialog', () => { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', diff --git a/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.spec.tsx b/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.spec.tsx index 2151eb98d0e..dd592e7a9fc 100644 --- a/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.spec.tsx +++ b/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.spec.tsx @@ -18,12 +18,15 @@ jest.mock('../../../../libs/useCurrentUserLazyQuery', () => ({ useCurrentUserLazyQuery: jest.fn().mockReturnValue({ loadUser: jest.fn(), data: { - __typename: 'User', ...user1 } }) })) -const user1 = { id: 'userId', email: 'miguelohara@example.com' } +const user1 = { + id: 'userId', + email: 'miguelohara@example.com', + __typename: 'AuthenticatedUser' +} describe('TeamMembersList', () => { const getUserTeamMock1: MockedResponse = { @@ -42,7 +45,7 @@ describe('TeamMembersList', () => { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', diff --git a/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.tsx b/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.tsx index b7b6a90749f..e0f0153e8a7 100644 --- a/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.tsx +++ b/apps/journeys-admin/src/components/Team/TeamManageDialog/TeamManageWrapper/TeamManageWrapper.tsx @@ -48,9 +48,15 @@ export function TeamManageWrapper({ }, [activeTeam, getUserTeamsAndInvites]) const currentUserTeam: UserTeam | undefined = useMemo(() => { - return data?.userTeams?.find(({ user: { email } }) => { - return email === currentUser?.email - }) + if (currentUser?.__typename === 'AuthenticatedUser') { + return data?.userTeams?.find(({ user }) => { + return ( + user.__typename === 'AuthenticatedUser' && + user.email === currentUser.email + ) + }) + } + return undefined }, [data, currentUser]) return ( diff --git a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamInviteList/UserTeamInviteList.spec.tsx b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamInviteList/UserTeamInviteList.spec.tsx index 5b4a2511483..376b4a5d49e 100644 --- a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamInviteList/UserTeamInviteList.spec.tsx +++ b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamInviteList/UserTeamInviteList.spec.tsx @@ -16,7 +16,7 @@ describe('UserTeamInviteList', () => { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', @@ -29,7 +29,7 @@ describe('UserTeamInviteList', () => { id: 'userTeamId2', role: UserTeamRole.member, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'hobiebrown@example.com', firstName: 'Hobie', id: 'userId2', @@ -65,7 +65,7 @@ describe('UserTeamInviteList', () => { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', @@ -79,7 +79,7 @@ describe('UserTeamInviteList', () => { id: 'userTeamId2', role: UserTeamRole.member, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'hobiebrown@example.com', firstName: 'Hobie', id: 'userId2', diff --git a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.spec.tsx b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.spec.tsx index 422a07b2379..215e9c8ed19 100644 --- a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.spec.tsx +++ b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.spec.tsx @@ -19,7 +19,7 @@ describe('UserTeamList', () => { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', @@ -32,7 +32,7 @@ describe('UserTeamList', () => { id: 'userTeamId2', role: UserTeamRole.member, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'hobiebrown@example.com', firstName: 'Hobie', id: 'userId2', @@ -55,7 +55,7 @@ describe('UserTeamList', () => { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', @@ -69,7 +69,7 @@ describe('UserTeamList', () => { id: 'userTeamId2', role: UserTeamRole.member, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'hobiebrown@example.com', firstName: 'Hobie', id: 'userId2', diff --git a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.tsx b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.tsx index 1056a6046e7..1bbe2af83e2 100644 --- a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.tsx +++ b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamList.tsx @@ -73,13 +73,22 @@ export function UserTeamList({ {sortedUserTeams.length > 0 && currentUserTeam != null && ( <> {sortedUserTeams.map((userTeam) => { + const currentUserEmail = + currentUserTeam.user.__typename === 'AuthenticatedUser' + ? currentUserTeam.user.email + : undefined + const userTeamEmail = + userTeam.user.__typename === 'AuthenticatedUser' + ? userTeam.user.email + : undefined return ( { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', @@ -51,7 +51,7 @@ describe('UserTeamListItem', () => { id: 'userTeamId', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'miguelohara@example.com', firstName: 'Miguel', id: 'userId', diff --git a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamListItem/UserTeamListItem.tsx b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamListItem/UserTeamListItem.tsx index c8d23cab35a..ee2b1e4953d 100644 --- a/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamListItem/UserTeamListItem.tsx +++ b/apps/journeys-admin/src/components/Team/TeamManageDialog/UserTeamList/UserTeamListItem/UserTeamListItem.tsx @@ -35,7 +35,12 @@ export const USER_TEAM_UPDATE = gql` role id user { - id + ... on AuthenticatedUser { + id + } + ... on AnonymousUser { + id + } } } } @@ -53,15 +58,16 @@ export function UserTeamListItem({ const [userTeamUpdate] = useMutation(USER_TEAM_UPDATE) const { id, email, displayName, imageUrl, role, userId } = useMemo(() => { + const user = listItem?.user + const isAuthenticated = user?.__typename === 'AuthenticatedUser' return { id: listItem.id, - email: listItem?.user?.email, - displayName: compact([ - listItem?.user?.firstName, - listItem?.user?.lastName - ]).join(' '), - userId: listItem?.user?.id, - imageUrl: listItem?.user?.imageUrl, + email: isAuthenticated ? user.email : undefined, + displayName: isAuthenticated + ? compact([user.firstName, user.lastName]).join(' ') + : '', + userId: user?.id, + imageUrl: isAuthenticated ? user.imageUrl : undefined, role: listItem.role } }, [listItem]) @@ -91,10 +97,10 @@ export function UserTeamListItem({ <> - - {displayName != null + + {displayName != null && displayName !== '' ? displayName.charAt(0)?.toUpperCase() - : email.charAt(0).toUpperCase()} + : email?.charAt(0)?.toUpperCase() ?? '?'} @@ -119,7 +125,11 @@ export function UserTeamListItem({ {journeyId != null && ( ({ useCurrentUserLazyQuery: jest.fn().mockReturnValue({ loadUser: jest.fn(), data: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'userId', email: 'siyangguccigang@example.com' } diff --git a/apps/journeys-admin/src/components/Team/TeamUpdateDialog/TeamUpdateDialog.tsx b/apps/journeys-admin/src/components/Team/TeamUpdateDialog/TeamUpdateDialog.tsx index 9c7154acce5..8a43a2d588e 100644 --- a/apps/journeys-admin/src/components/Team/TeamUpdateDialog/TeamUpdateDialog.tsx +++ b/apps/journeys-admin/src/components/Team/TeamUpdateDialog/TeamUpdateDialog.tsx @@ -54,9 +54,15 @@ export function TeamUpdateDialog({ }, [loadUser]) const currentUserTeamRole: UserTeamRole | undefined = useMemo(() => { - return activeTeam?.userTeams?.find(({ user: { email } }) => { - return email === currentUser?.email - })?.role + if (currentUser?.__typename === 'AuthenticatedUser') { + return activeTeam?.userTeams?.find(({ user }) => { + return ( + user.__typename === 'AuthenticatedUser' && + user.email === currentUser.email + ) + })?.role + } + return undefined }, [activeTeam, currentUser]) async function handleSubmit( diff --git a/apps/journeys-admin/src/components/Team/UserTeamInviteForm/UserTeamInviteForm.spec.tsx b/apps/journeys-admin/src/components/Team/UserTeamInviteForm/UserTeamInviteForm.spec.tsx index 9c719489413..0b4088d85a5 100644 --- a/apps/journeys-admin/src/components/Team/UserTeamInviteForm/UserTeamInviteForm.spec.tsx +++ b/apps/journeys-admin/src/components/Team/UserTeamInviteForm/UserTeamInviteForm.spec.tsx @@ -23,7 +23,7 @@ jest.mock('../../../libs/useCurrentUserLazyQuery', () => ({ useCurrentUserLazyQuery: jest.fn().mockReturnValue({ loadUser: jest.fn(), data: { - __typename: 'User', + __typename: 'AuthenticatedUser', ...user1 } }) @@ -92,7 +92,7 @@ describe('UserTeamInviteForm', () => { __typename: 'UserTeam', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'siyangguccigang@example.com', firstName: 'Siyang', id: 'userId', diff --git a/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.spec.tsx b/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.spec.tsx index 2a313241293..1a7aa25920e 100644 --- a/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.spec.tsx +++ b/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.spec.tsx @@ -157,11 +157,6 @@ describe('LanguageScreen', () => { ) - await waitFor(() => - expect(screen.getByRole('combobox', { name: 'Team' })).toHaveTextContent( - 'Team One' - ) - ) fireEvent.click(screen.getByTestId('CustomizeFlowNextButton')) await waitFor(() => expect(mockJourneyDuplicateMockResult).toHaveBeenCalled() @@ -266,12 +261,6 @@ describe('LanguageScreen', () => { await waitFor(() => expect(mockGetParentJourneysFromTemplateIdMockResult).toHaveBeenCalled() ) - await waitFor(() => - expect(screen.getByRole('combobox', { name: 'Team' })).toHaveTextContent( - 'Team One' - ) - ) - fireEvent.focus(screen.getByTestId('LanguageAutocompleteInput')) fireEvent.keyDown(screen.getByTestId('LanguageAutocompleteInput'), { key: 'ArrowDown' @@ -321,11 +310,6 @@ describe('LanguageScreen', () => { ) - await waitFor(() => - expect(screen.getByRole('combobox', { name: 'Team' })).toHaveTextContent( - 'Team One' - ) - ) fireEvent.click(screen.getByTestId('CustomizeFlowNextButton')) await waitFor(() => expect(mockJourneyDuplicateMockResult).toHaveBeenCalled() @@ -422,10 +406,6 @@ describe('LanguageScreen', () => { expect(screen.getAllByText('Select a language')).toHaveLength(2) expect(screen.getByTestId('LanguageAutocompleteInput')).toBeInTheDocument() - expect(screen.getAllByText('Select a team')).toHaveLength(2) - await waitFor(() => { - expect(screen.getByRole('combobox', { name: 'Team' })).toBeInTheDocument() - }) expect(screen.getByTestId('CustomizeFlowNextButton')).toBeInTheDocument() expect(screen.getByTestId('CustomizeFlowNextButton')).toHaveTextContent( diff --git a/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx b/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx index ce20b29bad7..18cee381add 100644 --- a/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx +++ b/apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx @@ -1,13 +1,16 @@ +import { gql, useMutation } from '@apollo/client' import FormControl from '@mui/material/FormControl' import Stack from '@mui/material/Stack' import Typography from '@mui/material/Typography' +import { getApp } from 'firebase/app' +import { getAuth, signInAnonymously } from 'firebase/auth' import { Form, Formik, FormikValues } from 'formik' import { useRouter } from 'next/router' import { useUser } from 'next-firebase-auth' import { useTranslation } from 'next-i18next' import { useSnackbar } from 'notistack' -import { ReactElement, useState } from 'react' -import { object, string } from 'yup' +import { ReactElement, useEffect, useState } from 'react' +import { object } from 'yup' import { useJourney } from '@core/journeys/ui/JourneyProvider' import { useTeam } from '@core/journeys/ui/TeamProvider' @@ -15,12 +18,27 @@ import { SocialImage } from '@core/journeys/ui/TemplateView/TemplateViewHeader/S import { useJourneyDuplicateMutation } from '@core/journeys/ui/useJourneyDuplicateMutation' import { LanguageAutocomplete } from '@core/shared/ui/LanguageAutocomplete' +import { JourneyProfileCreate } from '../../../../../../__generated__/JourneyProfileCreate' +import { useCurrentUserLazyQuery } from '../../../../../libs/useCurrentUserLazyQuery' import { useGetChildTemplateJourneyLanguages } from '../../../../../libs/useGetChildTemplateJourneyLanguages' import { useGetParentTemplateJourneyLanguages } from '../../../../../libs/useGetParentTemplateJourneyLanguages' +import { + useTeamCreateMutation, + useTeamCreateMutationGuest +} from '../../../../../libs/useTeamCreateMutation' import { CustomizationScreen } from '../../../utils/getCustomizeFlowConfig' import { CustomizeFlowNextButton } from '../../CustomizeFlowNextButton' -import { JourneyCustomizeTeamSelect } from './JourneyCustomizeTeamSelect' + +// const JOURNEY_PROFILE_CREATE = gql` +// mutation JourneyProfileCreate { +// journeyProfileCreate { +// id +// userId +// acceptedTermsAt +// } +// } +// ` interface LanguageScreenProps { handleNext: () => void @@ -42,6 +60,14 @@ export function LanguageScreen({ const isSignedIn = user?.email != null && user?.id != null const { query } = useTeam() + useEffect(() => { + //TODO: delete this effect + const firebaseUserId = user?.id ?? null + const isAnonymous = user?.firebaseUser?.isAnonymous ?? false + console.log('[LanguageScreen] Firebase user id:', firebaseUserId) + console.log('[LanguageScreen] Is anonymous user:', isAnonymous) + }, [user?.id, user?.firebaseUser?.isAnonymous]) + const isParentTemplate = journey?.fromTemplateId == null const { @@ -93,12 +119,9 @@ export function LanguageScreen({ ...childJourneyLanguagesJourneyMap } - const validationSchema = object({ - teamSelect: string().required() - }) + const validationSchema = object({}) const initialValues = { - teamSelect: query?.data?.getJourneyProfile?.lastActiveTeamId ?? '', languageSelect: { id: journey?.language?.id, localName: journey?.language?.name.find((name) => name.primary)?.value, @@ -107,44 +130,223 @@ export function LanguageScreen({ } const [journeyDuplicate] = useJourneyDuplicateMutation() + const { loadUser } = useCurrentUserLazyQuery() + // const [journeyProfileCreate] = useMutation( + // JOURNEY_PROFILE_CREATE + // ) + const [teamCreate] = useTeamCreateMutation() + const [teamCreateGuest] = useTeamCreateMutationGuest() const FORM_SM_BREAKPOINT_WIDTH = '390px' + async function createGuestUser(): Promise<{ teamId: string } | null> { + try { + console.log('[createGuestUser] 1. start', { + isAnonymous: user?.firebaseUser?.isAnonymous ?? false + }) + const isAnonymous = user?.firebaseUser?.isAnonymous ?? false + if (!isAnonymous) { + console.log('[createGuestUser] 2. calling signInAnonymously') + await signInAnonymously(getAuth(getApp())) + console.log('[createGuestUser] 3. signInAnonymously done') + } else { + console.log('[createGuestUser] 2. already anonymous, skip signInAnonymously') + } + + const teamName = t('My Team') + + let meResult: Awaited> | null = null + try { + console.log('[createGuestUser] 4. calling loadUser') + meResult = await loadUser() + console.log('[createGuestUser] 5. loadUser done', { + hasMe: meResult?.data?.me != null, + __typename: meResult?.data?.me?.__typename + }) + } catch (e) { + console.error('[createGuestUser] loadUser failed:', e) + } + + // let profileResult: Awaited> | null = + // null + // try { + // profileResult = await journeyProfileCreate() + // } catch (e) { + // console.error('[createGuestUser] journeyProfileCreate failed:', e) + // } + + let teamResult: Awaited> | null = null + try { + console.log('[createGuestUser] 6. calling teamCreate', { teamName }) + teamResult = await teamCreateGuest({ + variables: { + input: { title: teamName, publicTitle: teamName } + } + }) + console.log('[createGuestUser] 7. teamCreate done', { + teamId: teamResult?.data?.teamCreate?.id + }) + } catch (e) { + const err = e as { + graphQLErrors?: Array<{ message: string; extensions?: unknown }> + networkError?: unknown + message?: string + cause?: unknown + } + console.error('[createGuestUser] teamCreate failed:', err?.message ?? e) + if (err?.graphQLErrors?.length) { + err.graphQLErrors.forEach((g, i) => { + console.error( + `[createGuestUser] graphQLErrors[${i}]:`, + g.message, + g.extensions + ) + }) + } + if (err?.networkError) { + console.error('[createGuestUser] networkError:', err.networkError) + } + if (err?.cause != null) { + console.error('[createGuestUser] cause:', err.cause) + } + return null + } + + if (teamResult?.data?.teamCreate == null) { + console.log('[createGuestUser] 8. returning null (team missing)', { + hasMe: meResult?.data?.me != null, + hasTeam: false + }) + return null + } + + // Refetch me after teamCreate (user was created in resolveReference); optional for guest flow + if (meResult?.data?.me == null) { + try { + await loadUser() + } catch { + // ignore + } + } + + // Guest flow success: we have a team (user exists in api-users via resolveReference) + console.log('[createGuestUser] 9. success', { + teamId: teamResult.data.teamCreate.id + }) + return { teamId: teamResult.data.teamCreate.id } + } catch (e) { + console.error('[createGuestUser] unexpected error:', e) + return null + } + } + + async function duplicateJourneyAndRedirect( + journeyId: string, + teamId: string + ): Promise { + const { data } = await journeyDuplicate({ + variables: { id: journeyId, teamId, forceNonTemplate: true } + }) + if (data?.journeyDuplicate == null) return false + + await router.push( + `/templates/${data.journeyDuplicate.id}/customize`, + undefined, + { shallow: true } + ) + return true + } + async function handleSubmit(values: FormikValues) { setLoading(true) if (journey == null) { setLoading(false) return } + + const journeyId = + languagesJourneyMap?.[values.languageSelect?.id] ?? journey?.id + if (journeyId == null) { + enqueueSnackbar( + t('Unable to continue as guest. Please try again or sign in.'), + { variant: 'error' } + ) + setLoading(false) + return + } + if (isSignedIn) { - const { teamSelect: teamId } = values - const { - languageSelect: { id: languageId } - } = values - const journeyId = languagesJourneyMap?.[languageId] ?? journey.id - const { data: duplicateData } = await journeyDuplicate({ - variables: { id: journeyId, teamId, forceNonTemplate: true } - }) - if (duplicateData?.journeyDuplicate == null) { + const teams = query?.data?.teams ?? [] + const teamId = + query?.data?.getJourneyProfile?.lastActiveTeamId ?? teams[0]?.id + if (teamId == null) { enqueueSnackbar( t( - 'Failed to duplicate journey to team, please refresh the page and try again' + 'No team available. Please create a team first.' ), - { - variant: 'error' - } + { variant: 'error' } ) setLoading(false) - return } - await router.push( - `/templates/${duplicateData.journeyDuplicate.id}/customize`, - undefined, - { shallow: true } - ) - handleNext() + const success = await duplicateJourneyAndRedirect(journeyId, teamId) + if (!success) { + enqueueSnackbar( + t( + 'Failed to duplicate journey to team, please refresh the page and try again' + ), + { variant: 'error' } + ) + } else { + handleNext() + } setLoading(false) + return + } else { + try { + const guestResult = await createGuestUser() + if (guestResult == null) { + enqueueSnackbar( + t('Unable to continue as guest. Please try again or sign in.'), + { variant: 'error' } + ) + setLoading(false) + return + } + + try { + const success = await duplicateJourneyAndRedirect( + journeyId, + guestResult.teamId + ) + if (!success) { + enqueueSnackbar( + t( + 'Failed to duplicate journey to team, please refresh the page and try again' + ), + { variant: 'error' } + ) + } else { + handleNext() + } + } catch (e) { + console.error('[LanguageScreen] duplicateJourneyAndRedirect error:', e) + enqueueSnackbar( + t( + 'Failed to duplicate journey to team, please refresh the page and try again' + ), + { variant: 'error' } + ) + } + } catch (e) { + console.error('[LanguageScreen] createGuestUser error:', e) + enqueueSnackbar( + t('Unable to continue as guest. Please try again or sign in.'), + { variant: 'error' } + ) + } finally { + setLoading(false) + } } } @@ -216,21 +418,6 @@ export function LanguageScreen({ }))} onChange={(value) => setFieldValue('languageSelect', value)} /> - - {t('Select a team')} - - - {t('Select a team')} - - {isSignedIn && } handleSubmit()} diff --git a/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.spec.tsx b/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.spec.tsx index fcf5a81ea1c..359d4847536 100644 --- a/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.spec.tsx +++ b/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.spec.tsx @@ -9,6 +9,7 @@ import { checkConditionalRedirect } from '.' const meData = { me: { + __typename: 'AuthenticatedUser', emailVerified: true } } @@ -38,7 +39,11 @@ describe('checkConditionalRedirect', () => { query: jest .fn() .mockResolvedValue({ data }) - .mockResolvedValueOnce({ data: { me: { emailVerified: false } } }) + .mockResolvedValueOnce({ + data: { + me: { emailVerified: false, __typename: 'AuthenticatedUser' } + } + }) } as unknown as ApolloClient expect( await checkConditionalRedirect({ @@ -57,7 +62,11 @@ describe('checkConditionalRedirect', () => { query: jest .fn() .mockResolvedValue({ data }) - .mockResolvedValueOnce({ data: { me: { emailVerified: false } } }) + .mockResolvedValueOnce({ + data: { + me: { emailVerified: false, __typename: 'AuthenticatedUser' } + } + }) } as unknown as ApolloClient expect( await checkConditionalRedirect({ apolloClient, resolvedUrl: '/' }) diff --git a/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.ts b/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.ts index d0747d76416..a52ef64ef00 100644 --- a/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.ts +++ b/apps/journeys-admin/src/libs/checkConditionalRedirect/checkConditionalRedirect.ts @@ -52,11 +52,13 @@ export async function checkConditionalRedirect({ variables: { input: { redirect } } }) - if (!(me.me?.emailVerified ?? false)) { - if (resolvedUrl.startsWith('/users/verify')) return - return { - destination: `/users/verify${redirect}`, - permanent: false + if (me.me?.__typename === 'AuthenticatedUser') { + if (!(me.me?.emailVerified ?? false)) { + if (resolvedUrl.startsWith('/users/verify')) return + return { + destination: `/users/verify${redirect}`, + permanent: false + } } } diff --git a/apps/journeys-admin/src/libs/initAndAuthApp/initAndAuthApp.ts b/apps/journeys-admin/src/libs/initAndAuthApp/initAndAuthApp.ts index 3a10c318ccd..9dd96768eb1 100644 --- a/apps/journeys-admin/src/libs/initAndAuthApp/initAndAuthApp.ts +++ b/apps/journeys-admin/src/libs/initAndAuthApp/initAndAuthApp.ts @@ -108,7 +108,10 @@ export async function initAndAuthApp({ }) : undefined - if (!(redirect?.destination.startsWith('/users/verify') ?? false)) + if ( + !(redirect?.destination.startsWith('/users/verify') ?? false) && + user?.email != null + ) await apolloClient.mutate({ mutation: ACCEPT_ALL_INVITES }) diff --git a/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUser.spec.tsx b/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUser.spec.tsx index ea0121e0f28..ea5c33845bf 100644 --- a/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUser.spec.tsx +++ b/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUser.spec.tsx @@ -20,7 +20,8 @@ describe('useCurrentUserLazyQuery', () => { data: { me: { id: 'user.id', - email: 'test@email.com' + email: 'test@email.com', + __typename: 'AuthenticatedUser' } } } @@ -38,6 +39,7 @@ describe('useCurrentUserLazyQuery', () => { await waitFor(() => expect(result.current.data).toEqual({ + __typename: 'AuthenticatedUser', id: 'user.id', email: 'test@email.com' }) @@ -57,7 +59,8 @@ describe('useCurrentUserLazyQuery', () => { data: { me: { id: 'user.id', - email: 'test@email.com' + email: 'test@email.com', + __typename: 'AuthenticatedUser' } } } @@ -70,7 +73,7 @@ describe('useCurrentUserLazyQuery', () => { }) expect(result.current.data).toEqual({ - __typename: 'User', + __typename: 'AuthenticatedUser', id: '', email: '' }) diff --git a/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.mock.ts b/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.mock.ts index dd8bfad7514..951a6295037 100644 --- a/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.mock.ts +++ b/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.mock.ts @@ -11,7 +11,7 @@ export const mockUseCurrentUserLazyQuery: MockedResponse = { result: { data: { me: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'user.id', email: 'test@email.com' } diff --git a/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.ts b/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.ts index f78251e6b17..06acd4e5281 100644 --- a/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.ts +++ b/apps/journeys-admin/src/libs/useCurrentUserLazyQuery/useCurrentUserLazyQuery.ts @@ -13,21 +13,25 @@ import { export const GET_CURRENT_USER = gql` query GetCurrentUser { me { - id - email + ... on AuthenticatedUser { + id + email + } + ... on AnonymousUser { + id + } } } ` export function useCurrentUserLazyQuery(): { loadUser: LazyQueryExecFunction - data: ApiUser + data?: ApiUser | null } { const [loadUser, { data }] = useLazyQuery(GET_CURRENT_USER) - if (data?.me != null) { - return { loadUser, data: data.me } + return { + loadUser, + data: data?.me } - - return { loadUser, data: { __typename: 'User', id: '', email: '' } } } diff --git a/apps/journeys-admin/src/libs/useTeamCreateMutation/index.ts b/apps/journeys-admin/src/libs/useTeamCreateMutation/index.ts index 38ddb0db05b..01b7632c399 100644 --- a/apps/journeys-admin/src/libs/useTeamCreateMutation/index.ts +++ b/apps/journeys-admin/src/libs/useTeamCreateMutation/index.ts @@ -1 +1,4 @@ -export { useTeamCreateMutation } from './useTeamCreateMutation' +export { + useTeamCreateMutation, + useTeamCreateMutationGuest +} from './useTeamCreateMutation' diff --git a/apps/journeys-admin/src/libs/useTeamCreateMutation/useTeamCreateMutation.tsx b/apps/journeys-admin/src/libs/useTeamCreateMutation/useTeamCreateMutation.tsx index 18205ba7f4a..8a27f4257fa 100644 --- a/apps/journeys-admin/src/libs/useTeamCreateMutation/useTeamCreateMutation.tsx +++ b/apps/journeys-admin/src/libs/useTeamCreateMutation/useTeamCreateMutation.tsx @@ -21,11 +21,43 @@ export const TEAM_CREATE = gql` userTeams { id user { - id - firstName - lastName - imageUrl - email + ... on AuthenticatedUser { + id + firstName + lastName + imageUrl + email + } + ... on AnonymousUser { + id + } + } + role + } + customDomains { + id + name + } + } + } +` + +/** Minimal selection for guest flow: only user.id to avoid User entity field resolution errors in federation. */ +export const TEAM_CREATE_GUEST = gql` + mutation TeamCreateGuest($input: TeamCreateInput!) { + teamCreate(input: $input) { + id + title + publicTitle + userTeams { + id + user { + ... on AuthenticatedUser { + id + } + ... on AnonymousUser { + id + } } role } @@ -71,3 +103,37 @@ export function useTeamCreateMutation( return mutation } + +/** Use for guest flow: only requests user.id to avoid federation User field resolution errors. */ +export function useTeamCreateMutationGuest( + options?: MutationHookOptions +): MutationTuple { + const { setActiveTeam } = useTeam() + return useMutation(TEAM_CREATE_GUEST, { + update(cache, { data }) { + if (data?.teamCreate != null) { + cache.modify({ + fields: { + teams(existingTeams = []) { + const newTeamRef = cache.writeFragment({ + data: data.teamCreate, + fragment: gql` + fragment NewTeam on Team { + id + } + ` + }) + return [...existingTeams, newTeamRef] + } + } + }) + } + }, + onCompleted(data) { + if (data?.teamCreate != null) { + setActiveTeam(data.teamCreate) + } + }, + ...options + }) +} diff --git a/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.spec.tsx b/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.spec.tsx index 096ed38f368..aef3046c524 100644 --- a/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.spec.tsx +++ b/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.spec.tsx @@ -16,7 +16,7 @@ describe('useUserTeamsAndInvitesLazyQuery', () => { id: 'ut1', role: UserTeamRole.manager, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'userTeam1@example.com', firstName: 'User', lastName: 'One', @@ -29,7 +29,7 @@ describe('useUserTeamsAndInvitesLazyQuery', () => { id: 'ut2', role: UserTeamRole.member, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', email: 'userTeam2@example.com', firstName: 'User', lastName: 'Two', diff --git a/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.ts b/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.ts index 8df5175e943..c1a8b36f4b2 100644 --- a/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.ts +++ b/apps/journeys-admin/src/libs/useUserTeamsAndInvitesLazyQuery/useUserTeamsAndInvitesLazyQuery.ts @@ -21,7 +21,13 @@ export function useUserTeamsAndInvitesLazyQuery(): { >(GET_USER_TEAMS_AND_INVITES, { onCompleted: ({ userTeams, userTeamInvites }) => { setEmails([ - ...userTeams.map(({ user: { email } }) => email.toLowerCase()), + ...userTeams + .filter(({ user }) => user.__typename === 'AuthenticatedUser') + .map(({ user }) => + user.__typename === 'AuthenticatedUser' + ? user.email.toLowerCase() + : '' + ), ...userTeamInvites.map(({ email }) => email.toLowerCase()) ]) } diff --git a/apps/journeys-admin/src/libs/useUserTeamsAndInvitesQuery/useUserTeamsAndInvitesQuery.ts b/apps/journeys-admin/src/libs/useUserTeamsAndInvitesQuery/useUserTeamsAndInvitesQuery.ts index 5c072b4e80d..5544209eb77 100644 --- a/apps/journeys-admin/src/libs/useUserTeamsAndInvitesQuery/useUserTeamsAndInvitesQuery.ts +++ b/apps/journeys-admin/src/libs/useUserTeamsAndInvitesQuery/useUserTeamsAndInvitesQuery.ts @@ -12,11 +12,16 @@ export const GET_USER_TEAMS_AND_INVITES = gql` id role user { - email - firstName - id - imageUrl - lastName + ... on AuthenticatedUser { + email + firstName + id + imageUrl + lastName + } + ... on AnonymousUser { + id + } } } userTeamInvites(teamId: $teamId) { @@ -41,9 +46,13 @@ export function useUserTeamsAndInvitesQuery( const emails = useMemo(() => { return [ - ...(query.data?.userTeams.map(({ user: { email } }) => - email.toLowerCase() - ) ?? []), + ...(query.data?.userTeams + .filter(({ user }) => user.__typename === 'AuthenticatedUser') + .map(({ user }) => + user.__typename === 'AuthenticatedUser' + ? user.email.toLowerCase() + : '' + ) ?? []), ...(query.data?.userTeamInvites.map(({ email }) => email.toLowerCase()) ?? []) ] diff --git a/apps/journeys/__generated__/GetJourney.ts b/apps/journeys/__generated__/GetJourney.ts index b6b660e62b8..91902e97140 100644 --- a/apps/journeys/__generated__/GetJourney.ts +++ b/apps/journeys/__generated__/GetJourney.ts @@ -667,7 +667,7 @@ export interface GetJourney_journey_creatorImageBlock { } export interface GetJourney_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys/__generated__/GetJourneys.ts b/apps/journeys/__generated__/GetJourneys.ts index 67e0d5cfb78..486737ad7a6 100644 --- a/apps/journeys/__generated__/GetJourneys.ts +++ b/apps/journeys/__generated__/GetJourneys.ts @@ -31,7 +31,7 @@ export interface GetJourneys_journeys_journeyCustomizationFields { } export interface GetJourneys_journeys_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/journeys/__generated__/GetLastActiveTeamIdAndTeams.ts b/apps/journeys/__generated__/GetLastActiveTeamIdAndTeams.ts index 134d854294c..7df3c60dcdf 100644 --- a/apps/journeys/__generated__/GetLastActiveTeamIdAndTeams.ts +++ b/apps/journeys/__generated__/GetLastActiveTeamIdAndTeams.ts @@ -15,8 +15,8 @@ export interface GetLastActiveTeamIdAndTeams_getJourneyProfile { lastActiveTeamId: string | null; } -export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { - __typename: "User"; +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; @@ -24,6 +24,13 @@ export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { email: string; } +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetLastActiveTeamIdAndTeams_teams_userTeams_user = GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser | GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser; + export interface GetLastActiveTeamIdAndTeams_teams_userTeams { __typename: "UserTeam"; id: string; diff --git a/apps/journeys/__generated__/JourneyFields.ts b/apps/journeys/__generated__/JourneyFields.ts index e4ab828d645..57a5d7ba854 100644 --- a/apps/journeys/__generated__/JourneyFields.ts +++ b/apps/journeys/__generated__/JourneyFields.ts @@ -667,7 +667,7 @@ export interface JourneyFields_creatorImageBlock { } export interface JourneyFields_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/resources/__generated__/GetJourney.ts b/apps/resources/__generated__/GetJourney.ts index b6b660e62b8..91902e97140 100644 --- a/apps/resources/__generated__/GetJourney.ts +++ b/apps/resources/__generated__/GetJourney.ts @@ -667,7 +667,7 @@ export interface GetJourney_journey_creatorImageBlock { } export interface GetJourney_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/resources/__generated__/GetJourneys.ts b/apps/resources/__generated__/GetJourneys.ts index 67e0d5cfb78..486737ad7a6 100644 --- a/apps/resources/__generated__/GetJourneys.ts +++ b/apps/resources/__generated__/GetJourneys.ts @@ -31,7 +31,7 @@ export interface GetJourneys_journeys_journeyCustomizationFields { } export interface GetJourneys_journeys_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/resources/__generated__/GetLastActiveTeamIdAndTeams.ts b/apps/resources/__generated__/GetLastActiveTeamIdAndTeams.ts index 134d854294c..7df3c60dcdf 100644 --- a/apps/resources/__generated__/GetLastActiveTeamIdAndTeams.ts +++ b/apps/resources/__generated__/GetLastActiveTeamIdAndTeams.ts @@ -15,8 +15,8 @@ export interface GetLastActiveTeamIdAndTeams_getJourneyProfile { lastActiveTeamId: string | null; } -export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { - __typename: "User"; +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; @@ -24,6 +24,13 @@ export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { email: string; } +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetLastActiveTeamIdAndTeams_teams_userTeams_user = GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser | GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser; + export interface GetLastActiveTeamIdAndTeams_teams_userTeams { __typename: "UserTeam"; id: string; diff --git a/apps/resources/__generated__/JourneyFields.ts b/apps/resources/__generated__/JourneyFields.ts index e4ab828d645..57a5d7ba854 100644 --- a/apps/resources/__generated__/JourneyFields.ts +++ b/apps/resources/__generated__/JourneyFields.ts @@ -667,7 +667,7 @@ export interface JourneyFields_creatorImageBlock { } export interface JourneyFields_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/watch/__generated__/GetJourney.ts b/apps/watch/__generated__/GetJourney.ts index b6b660e62b8..91902e97140 100644 --- a/apps/watch/__generated__/GetJourney.ts +++ b/apps/watch/__generated__/GetJourney.ts @@ -667,7 +667,7 @@ export interface GetJourney_journey_creatorImageBlock { } export interface GetJourney_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/watch/__generated__/GetJourneys.ts b/apps/watch/__generated__/GetJourneys.ts index 67e0d5cfb78..486737ad7a6 100644 --- a/apps/watch/__generated__/GetJourneys.ts +++ b/apps/watch/__generated__/GetJourneys.ts @@ -31,7 +31,7 @@ export interface GetJourneys_journeys_journeyCustomizationFields { } export interface GetJourneys_journeys_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/apps/watch/__generated__/GetLastActiveTeamIdAndTeams.ts b/apps/watch/__generated__/GetLastActiveTeamIdAndTeams.ts index 134d854294c..7df3c60dcdf 100644 --- a/apps/watch/__generated__/GetLastActiveTeamIdAndTeams.ts +++ b/apps/watch/__generated__/GetLastActiveTeamIdAndTeams.ts @@ -15,8 +15,8 @@ export interface GetLastActiveTeamIdAndTeams_getJourneyProfile { lastActiveTeamId: string | null; } -export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { - __typename: "User"; +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser { + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; @@ -24,6 +24,13 @@ export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user { email: string; } +export interface GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser { + __typename: "AnonymousUser"; + id: string; +} + +export type GetLastActiveTeamIdAndTeams_teams_userTeams_user = GetLastActiveTeamIdAndTeams_teams_userTeams_user_AuthenticatedUser | GetLastActiveTeamIdAndTeams_teams_userTeams_user_AnonymousUser; + export interface GetLastActiveTeamIdAndTeams_teams_userTeams { __typename: "UserTeam"; id: string; diff --git a/apps/watch/__generated__/JourneyFields.ts b/apps/watch/__generated__/JourneyFields.ts index e4ab828d645..57a5d7ba854 100644 --- a/apps/watch/__generated__/JourneyFields.ts +++ b/apps/watch/__generated__/JourneyFields.ts @@ -667,7 +667,7 @@ export interface JourneyFields_creatorImageBlock { } export interface JourneyFields_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/libs/journeys/ui/src/components/TeamProvider/TeamProvider.mock.ts b/libs/journeys/ui/src/components/TeamProvider/TeamProvider.mock.ts index 7e4a81408cd..5602762603c 100644 --- a/libs/journeys/ui/src/components/TeamProvider/TeamProvider.mock.ts +++ b/libs/journeys/ui/src/components/TeamProvider/TeamProvider.mock.ts @@ -23,7 +23,7 @@ export const getLastActiveTeamIdAndTeamsMock: MockedResponse => { // For menu-item variant, assume user is signed in - if (variant === 'menu-item' || signedIn) { - setLoading(true) - await handleCustomizeNavigation() - } else { - setOpenAccountDialog(true) - } + // if (variant === 'menu-item' || signedIn) { + setLoading(true) + await handleCustomizeNavigation() + // } else { + // setOpenAccountDialog(true) + // } } const handleSignIn = (login: boolean): void => { diff --git a/libs/journeys/ui/src/components/TemplateView/data.ts b/libs/journeys/ui/src/components/TemplateView/data.ts index 0e1b0707988..9530a16a050 100644 --- a/libs/journeys/ui/src/components/TemplateView/data.ts +++ b/libs/journeys/ui/src/components/TemplateView/data.ts @@ -73,7 +73,7 @@ export const defaultJourney: Journey = { openedAt: null, user: { id: '1', - __typename: 'User', + __typename: 'AuthenticatedUser', firstName: 'Amin', lastName: 'One', imageUrl: 'https://bit.ly/3Gth4Yf' @@ -86,7 +86,7 @@ export const defaultJourney: Journey = { openedAt: null, user: { id: '2', - __typename: 'User', + __typename: 'AuthenticatedUser', firstName: 'Horace', lastName: 'Two', imageUrl: 'https://bit.ly/3rgHd6a' @@ -99,7 +99,7 @@ export const defaultJourney: Journey = { openedAt: null, user: { id: '3', - __typename: 'User', + __typename: 'AuthenticatedUser', firstName: 'Coral', lastName: 'Three', imageUrl: 'https://bit.ly/3nlwUwJ' diff --git a/libs/journeys/ui/src/libs/JourneyProvider/__generated__/JourneyFields.ts b/libs/journeys/ui/src/libs/JourneyProvider/__generated__/JourneyFields.ts index 6567c2dd2c3..6ecf9c2dd4d 100644 --- a/libs/journeys/ui/src/libs/JourneyProvider/__generated__/JourneyFields.ts +++ b/libs/journeys/ui/src/libs/JourneyProvider/__generated__/JourneyFields.ts @@ -667,7 +667,7 @@ export interface JourneyFields_creatorImageBlock { } export interface JourneyFields_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/libs/journeys/ui/src/libs/useJourneyQuery/__generated__/GetJourney.ts b/libs/journeys/ui/src/libs/useJourneyQuery/__generated__/GetJourney.ts index 7ed6ccdd69b..69d2a558e63 100644 --- a/libs/journeys/ui/src/libs/useJourneyQuery/__generated__/GetJourney.ts +++ b/libs/journeys/ui/src/libs/useJourneyQuery/__generated__/GetJourney.ts @@ -667,7 +667,7 @@ export interface GetJourney_journey_creatorImageBlock { } export interface GetJourney_journey_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/libs/journeys/ui/src/libs/useJourneyQuery/useJourneyQuery.spec.tsx b/libs/journeys/ui/src/libs/useJourneyQuery/useJourneyQuery.spec.tsx index 45720777f92..4d28081526f 100644 --- a/libs/journeys/ui/src/libs/useJourneyQuery/useJourneyQuery.spec.tsx +++ b/libs/journeys/ui/src/libs/useJourneyQuery/useJourneyQuery.spec.tsx @@ -62,7 +62,7 @@ describe('useJourneyQuery', () => { role: UserJourneyRole.owner, openedAt: null, user: { - __typename: 'User', + __typename: 'AuthenticatedUser', id: 'user-id1', firstName: 'Amin', lastName: 'One', diff --git a/libs/journeys/ui/src/libs/useJourneysQuery/__generated__/GetJourneys.ts b/libs/journeys/ui/src/libs/useJourneysQuery/__generated__/GetJourneys.ts index 1b35d8b6af7..0dbf7df0436 100644 --- a/libs/journeys/ui/src/libs/useJourneysQuery/__generated__/GetJourneys.ts +++ b/libs/journeys/ui/src/libs/useJourneysQuery/__generated__/GetJourneys.ts @@ -31,7 +31,7 @@ export interface GetJourneys_journeys_journeyCustomizationFields { } export interface GetJourneys_journeys_userJourneys_user { - __typename: "User"; + __typename: "AuthenticatedUser"; id: string; firstName: string; lastName: string | null; diff --git a/libs/prisma/users/db/migrations/20250201120000_make_user_email_nullable/migration.sql b/libs/prisma/users/db/migrations/20250201120000_make_user_email_nullable/migration.sql new file mode 100644 index 00000000000..f5908591047 --- /dev/null +++ b/libs/prisma/users/db/migrations/20250201120000_make_user_email_nullable/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "User" ALTER COLUMN "email" DROP NOT NULL; diff --git a/libs/prisma/users/db/schema.prisma b/libs/prisma/users/db/schema.prisma index 7647ef9a255..44820f4e454 100644 --- a/libs/prisma/users/db/schema.prisma +++ b/libs/prisma/users/db/schema.prisma @@ -29,7 +29,7 @@ model User { userId String @unique firstName String lastName String? - email String @unique + email String? @unique imageUrl String? createdAt DateTime @default(now()) superAdmin Boolean @default(false) diff --git a/libs/shared/gql/src/__generated__/graphql-env.d.ts b/libs/shared/gql/src/__generated__/graphql-env.d.ts index d223f3a8132..843b5c05a3c 100644 --- a/libs/shared/gql/src/__generated__/graphql-env.d.ts +++ b/libs/shared/gql/src/__generated__/graphql-env.d.ts @@ -3,8 +3,10 @@ export type introspection_types = { 'Action': { kind: 'INTERFACE'; name: 'Action'; fields: { 'gtmEventName': { name: 'gtmEventName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'parentBlock': { name: 'parentBlock'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; } }; 'parentBlockId': { name: 'parentBlockId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; possibleTypes: 'ChatAction' | 'EmailAction' | 'LinkAction' | 'NavigateToBlockAction' | 'PhoneAction'; }; + 'AnonymousUser': { kind: 'OBJECT'; name: 'AnonymousUser'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; 'ArclightApiKey': { kind: 'OBJECT'; name: 'ArclightApiKey'; fields: { 'defaultPlatform': { name: 'defaultPlatform'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'DefaultPlatform'; ofType: null; }; } }; 'desc': { name: 'desc'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'key': { name: 'key'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'AudioPreview': { kind: 'OBJECT'; name: 'AudioPreview'; fields: { 'bitrate': { name: 'bitrate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'codec': { name: 'codec'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'duration': { name: 'duration'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'language': { name: 'language'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Language'; ofType: null; }; } }; 'languageId': { name: 'languageId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'size': { name: 'size'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'value': { name: 'value'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; + 'AuthenticatedUser': { kind: 'OBJECT'; name: 'AuthenticatedUser'; fields: { 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'emailVerified': { name: 'emailVerified'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'firstName': { name: 'firstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'imageUrl': { name: 'imageUrl'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'languageUserRoles': { name: 'languageUserRoles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'LanguageRole'; ofType: null; }; }; }; } }; 'lastName': { name: 'lastName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'mediaUserRoles': { name: 'mediaUserRoles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'MediaRole'; ofType: null; }; }; }; } }; 'superAdmin': { name: 'superAdmin'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; }; }; 'BaseError': { kind: 'INTERFACE'; name: 'BaseError'; fields: { 'message': { name: 'message'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; possibleTypes: 'Error' | 'ForeignKeyConstraintError' | 'NotFoundError' | 'NotUniqueError' | 'ZodError'; }; 'BibleBook': { kind: 'OBJECT'; name: 'BibleBook'; fields: { 'alternateName': { name: 'alternateName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'isNewTestament': { name: 'isNewTestament'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BibleBookName'; ofType: null; }; }; }; } }; 'order': { name: 'order'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'osisId': { name: 'osisId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'paratextAbbreviation': { name: 'paratextAbbreviation'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'BibleBookName': { kind: 'OBJECT'; name: 'BibleBookName'; fields: { 'language': { name: 'language'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Language'; ofType: null; }; } }; 'primary': { name: 'primary'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'value': { name: 'value'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; @@ -105,7 +107,7 @@ export type introspection_types = { 'ImageInput': { kind: 'INPUT_OBJECT'; name: 'ImageInput'; isOneOf: false; inputFields: [{ name: 'aspectRatio'; type: { kind: 'ENUM'; name: 'ImageAspectRatio'; ofType: null; }; defaultValue: null }, { name: 'videoId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }]; }; 'Int': unknown; 'Integration': { kind: 'INTERFACE'; name: 'Integration'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'team': { name: 'team'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'type': { name: 'type'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'IntegrationType'; ofType: null; }; } }; }; possibleTypes: 'IntegrationGoogle' | 'IntegrationGrowthSpaces'; }; - 'IntegrationGoogle': { kind: 'OBJECT'; name: 'IntegrationGoogle'; fields: { 'accountEmail': { name: 'accountEmail'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'team': { name: 'team'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'type': { name: 'type'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'IntegrationType'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; }; }; + 'IntegrationGoogle': { kind: 'OBJECT'; name: 'IntegrationGoogle'; fields: { 'accountEmail': { name: 'accountEmail'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'team': { name: 'team'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'type': { name: 'type'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'IntegrationType'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; } }; }; }; 'IntegrationGoogleCreateInput': { kind: 'INPUT_OBJECT'; name: 'IntegrationGoogleCreateInput'; isOneOf: false; inputFields: [{ name: 'teamId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'code'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'redirectUri'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; 'IntegrationGoogleUpdateInput': { kind: 'INPUT_OBJECT'; name: 'IntegrationGoogleUpdateInput'; isOneOf: false; inputFields: [{ name: 'code'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'redirectUri'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; 'IntegrationGrowthSpaces': { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; fields: { 'accessId': { name: 'accessId'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'accessSecretPart': { name: 'accessSecretPart'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'routes': { name: 'routes'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpacesRoute'; ofType: null; }; }; } }; 'team': { name: 'team'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'type': { name: 'type'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'IntegrationType'; ofType: null; }; } }; }; }; @@ -178,7 +180,7 @@ export type introspection_types = { 'MultiselectOptionBlockUpdateInput': { kind: 'INPUT_OBJECT'; name: 'MultiselectOptionBlockUpdateInput'; isOneOf: false; inputFields: [{ name: 'parentBlockId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'label'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'MultiselectSubmissionEvent': { kind: 'OBJECT'; name: 'MultiselectSubmissionEvent'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'label': { name: 'label'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'value': { name: 'value'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; }; }; 'MultiselectSubmissionEventCreateInput': { kind: 'INPUT_OBJECT'; name: 'MultiselectSubmissionEventCreateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'blockId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'stepId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'label'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'values'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; }; }; defaultValue: null }]; }; - 'Mutation': { kind: 'OBJECT'; name: 'Mutation'; fields: { 'audioPreviewCreate': { name: 'audioPreviewCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewDelete': { name: 'audioPreviewDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewUpdate': { name: 'audioPreviewUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'bibleCitationCreate': { name: 'bibleCitationCreate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'bibleCitationDelete': { name: 'bibleCitationDelete'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'bibleCitationUpdate': { name: 'bibleCitationUpdate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'blockDelete': { name: 'blockDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockDeleteAction': { name: 'blockDeleteAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; } }; 'blockDuplicate': { name: 'blockDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockOrderUpdate': { name: 'blockOrderUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockRestore': { name: 'blockRestore'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockUpdateAction': { name: 'blockUpdateAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Action'; ofType: null; }; } }; 'blockUpdateChatAction': { name: 'blockUpdateChatAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatAction'; ofType: null; }; } }; 'blockUpdateEmailAction': { name: 'blockUpdateEmailAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'EmailAction'; ofType: null; }; } }; 'blockUpdateLinkAction': { name: 'blockUpdateLinkAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'LinkAction'; ofType: null; }; } }; 'blockUpdateNavigateToBlockAction': { name: 'blockUpdateNavigateToBlockAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'NavigateToBlockAction'; ofType: null; }; } }; 'blockUpdatePhoneAction': { name: 'blockUpdatePhoneAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PhoneAction'; ofType: null; }; } }; 'buttonBlockCreate': { name: 'buttonBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; }; } }; 'buttonBlockUpdate': { name: 'buttonBlockUpdate'; type: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; } }; 'buttonClickEventCreate': { name: 'buttonClickEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonClickEvent'; ofType: null; }; } }; 'cardBlockCreate': { name: 'cardBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'cardBlockUpdate': { name: 'cardBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'chatButtonCreate': { name: 'chatButtonCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonRemove': { name: 'chatButtonRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonUpdate': { name: 'chatButtonUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatOpenEventCreate': { name: 'chatOpenEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatOpenEvent'; ofType: null; }; } }; 'cloudflareR2CompleteMultipart': { name: 'cloudflareR2CompleteMultipart'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Create': { name: 'cloudflareR2Create'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Delete': { name: 'cloudflareR2Delete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2MultipartPrepare': { name: 'cloudflareR2MultipartPrepare'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2MultipartPrepared'; ofType: null; }; } }; 'cloudflareUploadComplete': { name: 'cloudflareUploadComplete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'createCloudflareImageFromPrompt': { name: 'createCloudflareImageFromPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByFile': { name: 'createCloudflareUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByUrl': { name: 'createCloudflareUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createImageBySegmindPrompt': { name: 'createImageBySegmindPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createJourneyEventsExportLog': { name: 'createJourneyEventsExportLog'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyEventsExportLog'; ofType: null; }; } }; 'createKeyword': { name: 'createKeyword'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Keyword'; ofType: null; }; } }; 'createMuxVideoAndQueueUpload': { name: 'createMuxVideoAndQueueUpload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByFile': { name: 'createMuxVideoUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByUrl': { name: 'createMuxVideoUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createVerificationRequest': { name: 'createVerificationRequest'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'customDomainCheck': { name: 'customDomainCheck'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomainCheck'; ofType: null; }; } }; 'customDomainCreate': { name: 'customDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainDelete': { name: 'customDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainUpdate': { name: 'customDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'deleteCloudflareImage': { name: 'deleteCloudflareImage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'deleteMuxVideo': { name: 'deleteMuxVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'enableMuxDownload': { name: 'enableMuxDownload'; type: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; } }; 'fixVideoLanguages': { name: 'fixVideoLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'googleSheetsSyncCreate': { name: 'googleSheetsSyncCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'googleSheetsSyncDelete': { name: 'googleSheetsSyncDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'hostCreate': { name: 'hostCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostDelete': { name: 'hostDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostUpdate': { name: 'hostUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'iconBlockCreate': { name: 'iconBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'iconBlockUpdate': { name: 'iconBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'imageBlockCreate': { name: 'imageBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'imageBlockUpdate': { name: 'imageBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'integrationDelete': { name: 'integrationDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Integration'; ofType: null; }; } }; 'integrationGoogleCreate': { name: 'integrationGoogleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGoogleUpdate': { name: 'integrationGoogleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGrowthSpacesCreate': { name: 'integrationGrowthSpacesCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'integrationGrowthSpacesUpdate': { name: 'integrationGrowthSpacesUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'journeyAiTranslateCreate': { name: 'journeyAiTranslateCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCollectionCreate': { name: 'journeyCollectionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionDelete': { name: 'journeyCollectionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionUpdate': { name: 'journeyCollectionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCreate': { name: 'journeyCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCustomizationFieldPublisherUpdate': { name: 'journeyCustomizationFieldPublisherUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyCustomizationFieldUserUpdate': { name: 'journeyCustomizationFieldUserUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyDuplicate': { name: 'journeyDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyFeature': { name: 'journeyFeature'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeyLanguageAiDetect': { name: 'journeyLanguageAiDetect'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'journeyNotificationUpdate': { name: 'journeyNotificationUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; }; } }; 'journeyProfileCreate': { name: 'journeyProfileCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyProfileUpdate': { name: 'journeyProfileUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyPublish': { name: 'journeyPublish'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeySimpleUpdate': { name: 'journeySimpleUpdate'; type: { kind: 'SCALAR'; name: 'Json'; ofType: null; } }; 'journeyTemplate': { name: 'journeyTemplate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyThemeCreate': { name: 'journeyThemeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeDelete': { name: 'journeyThemeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeUpdate': { name: 'journeyThemeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyUpdate': { name: 'journeyUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyViewEventCreate': { name: 'journeyViewEventCreate'; type: { kind: 'OBJECT'; name: 'JourneyViewEvent'; ofType: null; } }; 'journeyVisitorExportToGoogleSheet': { name: 'journeyVisitorExportToGoogleSheet'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyVisitorGoogleSheetExportResult'; ofType: null; }; } }; 'journeysArchive': { name: 'journeysArchive'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysDelete': { name: 'journeysDelete'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysRestore': { name: 'journeysRestore'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysTrash': { name: 'journeysTrash'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'multiselectBlockCreate': { name: 'multiselectBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectBlockUpdate': { name: 'multiselectBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectOptionBlockCreate': { name: 'multiselectOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectOptionBlockUpdate': { name: 'multiselectOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectSubmissionEventCreate': { name: 'multiselectSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectSubmissionEvent'; ofType: null; }; } }; 'playlistCreate': { name: 'playlistCreate'; type: { kind: 'UNION'; name: 'MutationPlaylistCreateResult'; ofType: null; } }; 'playlistDelete': { name: 'playlistDelete'; type: { kind: 'UNION'; name: 'MutationPlaylistDeleteResult'; ofType: null; } }; 'playlistItemAdd': { name: 'playlistItemAdd'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddResult'; ofType: null; } }; 'playlistItemAddWithVideoAndLanguageIds': { name: 'playlistItemAddWithVideoAndLanguageIds'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddWithVideoAndLanguageIdsResult'; ofType: null; } }; 'playlistItemRemove': { name: 'playlistItemRemove'; type: { kind: 'UNION'; name: 'MutationPlaylistItemRemoveResult'; ofType: null; } }; 'playlistItemsReorder': { name: 'playlistItemsReorder'; type: { kind: 'UNION'; name: 'MutationPlaylistItemsReorderResult'; ofType: null; } }; 'playlistUpdate': { name: 'playlistUpdate'; type: { kind: 'UNION'; name: 'MutationPlaylistUpdateResult'; ofType: null; } }; 'qrCodeCreate': { name: 'qrCodeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeDelete': { name: 'qrCodeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeUpdate': { name: 'qrCodeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'radioOptionBlockCreate': { name: 'radioOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioOptionBlockUpdate': { name: 'radioOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioQuestionBlockCreate': { name: 'radioQuestionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionBlockUpdate': { name: 'radioQuestionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionSubmissionEventCreate': { name: 'radioQuestionSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionSubmissionEvent'; ofType: null; }; } }; 'shortLinkCreate': { name: 'shortLinkCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkCreateResult'; ofType: null; }; } }; 'shortLinkDelete': { name: 'shortLinkDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDeleteResult'; ofType: null; }; } }; 'shortLinkDomainCreate': { name: 'shortLinkDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainCreateResult'; ofType: null; }; } }; 'shortLinkDomainDelete': { name: 'shortLinkDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainDeleteResult'; ofType: null; }; } }; 'shortLinkDomainUpdate': { name: 'shortLinkDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainUpdateResult'; ofType: null; }; } }; 'shortLinkUpdate': { name: 'shortLinkUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkUpdateResult'; ofType: null; }; } }; 'signUpBlockCreate': { name: 'signUpBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; }; } }; 'signUpBlockUpdate': { name: 'signUpBlockUpdate'; type: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; } }; 'signUpSubmissionEventCreate': { name: 'signUpSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpSubmissionEvent'; ofType: null; }; } }; 'siteCreate': { name: 'siteCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationSiteCreateResult'; ofType: null; }; } }; 'spacerBlockCreate': { name: 'spacerBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'spacerBlockUpdate': { name: 'spacerBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'stepBlockCreate': { name: 'stepBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepBlockPositionUpdate': { name: 'stepBlockPositionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; }; }; } }; 'stepBlockUpdate': { name: 'stepBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepNextEventCreate': { name: 'stepNextEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepNextEvent'; ofType: null; }; } }; 'stepPreviousEventCreate': { name: 'stepPreviousEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepPreviousEvent'; ofType: null; }; } }; 'stepViewEventCreate': { name: 'stepViewEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepViewEvent'; ofType: null; }; } }; 'teamCreate': { name: 'teamCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'teamUpdate': { name: 'teamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'textResponseBlockCreate': { name: 'textResponseBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; }; } }; 'textResponseBlockUpdate': { name: 'textResponseBlockUpdate'; type: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; } }; 'textResponseSubmissionEventCreate': { name: 'textResponseSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseSubmissionEvent'; ofType: null; }; } }; 'triggerUnsplashDownload': { name: 'triggerUnsplashDownload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'typographyBlockCreate': { name: 'typographyBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'typographyBlockUpdate': { name: 'typographyBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'updateJourneysEmailPreference': { name: 'updateJourneysEmailPreference'; type: { kind: 'OBJECT'; name: 'JourneysEmailPreference'; ofType: null; } }; 'updateVideoAlgoliaIndex': { name: 'updateVideoAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'updateVideoVariantAlgoliaIndex': { name: 'updateVideoVariantAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'userImpersonate': { name: 'userImpersonate'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'userInviteAcceptAll': { name: 'userInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; }; }; } }; 'userInviteCreate': { name: 'userInviteCreate'; type: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; } }; 'userInviteRemove': { name: 'userInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; } }; 'userJourneyApprove': { name: 'userJourneyApprove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyOpen': { name: 'userJourneyOpen'; type: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; } }; 'userJourneyPromote': { name: 'userJourneyPromote'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemove': { name: 'userJourneyRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemoveAll': { name: 'userJourneyRemoveAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; }; }; } }; 'userJourneyRequest': { name: 'userJourneyRequest'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userTeamDelete': { name: 'userTeamDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'userTeamInviteAcceptAll': { name: 'userTeamInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; }; }; } }; 'userTeamInviteCreate': { name: 'userTeamInviteCreate'; type: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; } }; 'userTeamInviteRemove': { name: 'userTeamInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; } }; 'userTeamUpdate': { name: 'userTeamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'validateEmail': { name: 'validateEmail'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'videoBlockCreate': { name: 'videoBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoBlockUpdate': { name: 'videoBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoCollapseEventCreate': { name: 'videoCollapseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCollapseEvent'; ofType: null; }; } }; 'videoCompleteEventCreate': { name: 'videoCompleteEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCompleteEvent'; ofType: null; }; } }; 'videoCreate': { name: 'videoCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDelete': { name: 'videoDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDescriptionCreate': { name: 'videoDescriptionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionDelete': { name: 'videoDescriptionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionUpdate': { name: 'videoDescriptionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoEditionCreate': { name: 'videoEditionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionDelete': { name: 'videoEditionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionUpdate': { name: 'videoEditionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoExpandEventCreate': { name: 'videoExpandEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoExpandEvent'; ofType: null; }; } }; 'videoImageAltCreate': { name: 'videoImageAltCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltDelete': { name: 'videoImageAltDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltUpdate': { name: 'videoImageAltUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoOriginCreate': { name: 'videoOriginCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginDelete': { name: 'videoOriginDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginUpdate': { name: 'videoOriginUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoPauseEventCreate': { name: 'videoPauseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPauseEvent'; ofType: null; }; } }; 'videoPlayEventCreate': { name: 'videoPlayEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPlayEvent'; ofType: null; }; } }; 'videoProgressEventCreate': { name: 'videoProgressEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoProgressEvent'; ofType: null; }; } }; 'videoPublishChildren': { name: 'videoPublishChildren'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenResult'; ofType: null; }; } }; 'videoPublishChildrenAndLanguages': { name: 'videoPublishChildrenAndLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenAndLanguagesResult'; ofType: null; }; } }; 'videoSnippetCreate': { name: 'videoSnippetCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetDelete': { name: 'videoSnippetDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetUpdate': { name: 'videoSnippetUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoStartEventCreate': { name: 'videoStartEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStartEvent'; ofType: null; }; } }; 'videoStudyQuestionCreate': { name: 'videoStudyQuestionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionDelete': { name: 'videoStudyQuestionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionUpdate': { name: 'videoStudyQuestionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoSubtitleCreate': { name: 'videoSubtitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleDelete': { name: 'videoSubtitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleUpdate': { name: 'videoSubtitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoTitleCreate': { name: 'videoTitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleDelete': { name: 'videoTitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleUpdate': { name: 'videoTitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoUpdate': { name: 'videoUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoVariantCreate': { name: 'videoVariantCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDelete': { name: 'videoVariantDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDownloadCreate': { name: 'videoVariantDownloadCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadDelete': { name: 'videoVariantDownloadDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadUpdate': { name: 'videoVariantDownloadUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantUpdate': { name: 'videoVariantUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'visitorUpdate': { name: 'visitorUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; 'visitorUpdateForCurrentUser': { name: 'visitorUpdateForCurrentUser'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; }; }; + 'Mutation': { kind: 'OBJECT'; name: 'Mutation'; fields: { 'audioPreviewCreate': { name: 'audioPreviewCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewDelete': { name: 'audioPreviewDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'audioPreviewUpdate': { name: 'audioPreviewUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AudioPreview'; ofType: null; }; } }; 'bibleCitationCreate': { name: 'bibleCitationCreate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'bibleCitationDelete': { name: 'bibleCitationDelete'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'bibleCitationUpdate': { name: 'bibleCitationUpdate'; type: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; } }; 'blockDelete': { name: 'blockDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockDeleteAction': { name: 'blockDeleteAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; } }; 'blockDuplicate': { name: 'blockDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockOrderUpdate': { name: 'blockOrderUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockRestore': { name: 'blockRestore'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'blockUpdateAction': { name: 'blockUpdateAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Action'; ofType: null; }; } }; 'blockUpdateChatAction': { name: 'blockUpdateChatAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatAction'; ofType: null; }; } }; 'blockUpdateEmailAction': { name: 'blockUpdateEmailAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'EmailAction'; ofType: null; }; } }; 'blockUpdateLinkAction': { name: 'blockUpdateLinkAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'LinkAction'; ofType: null; }; } }; 'blockUpdateNavigateToBlockAction': { name: 'blockUpdateNavigateToBlockAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'NavigateToBlockAction'; ofType: null; }; } }; 'blockUpdatePhoneAction': { name: 'blockUpdatePhoneAction'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PhoneAction'; ofType: null; }; } }; 'buttonBlockCreate': { name: 'buttonBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; }; } }; 'buttonBlockUpdate': { name: 'buttonBlockUpdate'; type: { kind: 'OBJECT'; name: 'ButtonBlock'; ofType: null; } }; 'buttonClickEventCreate': { name: 'buttonClickEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ButtonClickEvent'; ofType: null; }; } }; 'cardBlockCreate': { name: 'cardBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'cardBlockUpdate': { name: 'cardBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CardBlock'; ofType: null; }; } }; 'chatButtonCreate': { name: 'chatButtonCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonRemove': { name: 'chatButtonRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatButtonUpdate': { name: 'chatButtonUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatButton'; ofType: null; }; } }; 'chatOpenEventCreate': { name: 'chatOpenEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ChatOpenEvent'; ofType: null; }; } }; 'cloudflareR2CompleteMultipart': { name: 'cloudflareR2CompleteMultipart'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Create': { name: 'cloudflareR2Create'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2Delete': { name: 'cloudflareR2Delete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2'; ofType: null; }; } }; 'cloudflareR2MultipartPrepare': { name: 'cloudflareR2MultipartPrepare'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareR2MultipartPrepared'; ofType: null; }; } }; 'cloudflareUploadComplete': { name: 'cloudflareUploadComplete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'createCloudflareImageFromPrompt': { name: 'createCloudflareImageFromPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByFile': { name: 'createCloudflareUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createCloudflareUploadByUrl': { name: 'createCloudflareUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createImageBySegmindPrompt': { name: 'createImageBySegmindPrompt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'createJourneyEventsExportLog': { name: 'createJourneyEventsExportLog'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyEventsExportLog'; ofType: null; }; } }; 'createKeyword': { name: 'createKeyword'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Keyword'; ofType: null; }; } }; 'createMuxVideoAndQueueUpload': { name: 'createMuxVideoAndQueueUpload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByFile': { name: 'createMuxVideoUploadByFile'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createMuxVideoUploadByUrl': { name: 'createMuxVideoUploadByUrl'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'createVerificationRequest': { name: 'createVerificationRequest'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; 'customDomainCheck': { name: 'customDomainCheck'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomainCheck'; ofType: null; }; } }; 'customDomainCreate': { name: 'customDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainDelete': { name: 'customDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomainUpdate': { name: 'customDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'deleteCloudflareImage': { name: 'deleteCloudflareImage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'deleteMuxVideo': { name: 'deleteMuxVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'enableMuxDownload': { name: 'enableMuxDownload'; type: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; } }; 'fixVideoLanguages': { name: 'fixVideoLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'googleSheetsSyncBackfill': { name: 'googleSheetsSyncBackfill'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'googleSheetsSyncCreate': { name: 'googleSheetsSyncCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'googleSheetsSyncDelete': { name: 'googleSheetsSyncDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; } }; 'hostCreate': { name: 'hostCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostDelete': { name: 'hostDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'hostUpdate': { name: 'hostUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; } }; 'iconBlockCreate': { name: 'iconBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'iconBlockUpdate': { name: 'iconBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IconBlock'; ofType: null; }; } }; 'imageBlockCreate': { name: 'imageBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'imageBlockUpdate': { name: 'imageBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ImageBlock'; ofType: null; }; } }; 'integrationDelete': { name: 'integrationDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Integration'; ofType: null; }; } }; 'integrationGoogleCreate': { name: 'integrationGoogleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGoogleUpdate': { name: 'integrationGoogleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGoogle'; ofType: null; }; } }; 'integrationGrowthSpacesCreate': { name: 'integrationGrowthSpacesCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'integrationGrowthSpacesUpdate': { name: 'integrationGrowthSpacesUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'IntegrationGrowthSpaces'; ofType: null; }; } }; 'journeyAiTranslateCreate': { name: 'journeyAiTranslateCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCollectionCreate': { name: 'journeyCollectionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionDelete': { name: 'journeyCollectionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollectionUpdate': { name: 'journeyCollectionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCreate': { name: 'journeyCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCustomizationFieldPublisherUpdate': { name: 'journeyCustomizationFieldPublisherUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyCustomizationFieldUserUpdate': { name: 'journeyCustomizationFieldUserUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCustomizationField'; ofType: null; }; }; }; } }; 'journeyDuplicate': { name: 'journeyDuplicate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyFeature': { name: 'journeyFeature'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeyLanguageAiDetect': { name: 'journeyLanguageAiDetect'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'journeyNotificationUpdate': { name: 'journeyNotificationUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; }; } }; 'journeyProfileCreate': { name: 'journeyProfileCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyProfileUpdate': { name: 'journeyProfileUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; }; } }; 'journeyPublish': { name: 'journeyPublish'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeySimpleUpdate': { name: 'journeySimpleUpdate'; type: { kind: 'SCALAR'; name: 'Json'; ofType: null; } }; 'journeyTemplate': { name: 'journeyTemplate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyThemeCreate': { name: 'journeyThemeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeDelete': { name: 'journeyThemeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyThemeUpdate': { name: 'journeyThemeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; }; } }; 'journeyUpdate': { name: 'journeyUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyViewEventCreate': { name: 'journeyViewEventCreate'; type: { kind: 'OBJECT'; name: 'JourneyViewEvent'; ofType: null; } }; 'journeyVisitorExportToGoogleSheet': { name: 'journeyVisitorExportToGoogleSheet'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyVisitorGoogleSheetExportResult'; ofType: null; }; } }; 'journeysArchive': { name: 'journeysArchive'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysDelete': { name: 'journeysDelete'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysRestore': { name: 'journeysRestore'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeysTrash': { name: 'journeysTrash'; type: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'multiselectBlockCreate': { name: 'multiselectBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectBlockUpdate': { name: 'multiselectBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectBlock'; ofType: null; }; } }; 'multiselectOptionBlockCreate': { name: 'multiselectOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectOptionBlockUpdate': { name: 'multiselectOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectOptionBlock'; ofType: null; }; } }; 'multiselectSubmissionEventCreate': { name: 'multiselectSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MultiselectSubmissionEvent'; ofType: null; }; } }; 'playlistCreate': { name: 'playlistCreate'; type: { kind: 'UNION'; name: 'MutationPlaylistCreateResult'; ofType: null; } }; 'playlistDelete': { name: 'playlistDelete'; type: { kind: 'UNION'; name: 'MutationPlaylistDeleteResult'; ofType: null; } }; 'playlistItemAdd': { name: 'playlistItemAdd'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddResult'; ofType: null; } }; 'playlistItemAddWithVideoAndLanguageIds': { name: 'playlistItemAddWithVideoAndLanguageIds'; type: { kind: 'UNION'; name: 'MutationPlaylistItemAddWithVideoAndLanguageIdsResult'; ofType: null; } }; 'playlistItemRemove': { name: 'playlistItemRemove'; type: { kind: 'UNION'; name: 'MutationPlaylistItemRemoveResult'; ofType: null; } }; 'playlistItemsReorder': { name: 'playlistItemsReorder'; type: { kind: 'UNION'; name: 'MutationPlaylistItemsReorderResult'; ofType: null; } }; 'playlistUpdate': { name: 'playlistUpdate'; type: { kind: 'UNION'; name: 'MutationPlaylistUpdateResult'; ofType: null; } }; 'qrCodeCreate': { name: 'qrCodeCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeDelete': { name: 'qrCodeDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodeUpdate': { name: 'qrCodeUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'radioOptionBlockCreate': { name: 'radioOptionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioOptionBlockUpdate': { name: 'radioOptionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioOptionBlock'; ofType: null; }; } }; 'radioQuestionBlockCreate': { name: 'radioQuestionBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionBlockUpdate': { name: 'radioQuestionBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionBlock'; ofType: null; }; } }; 'radioQuestionSubmissionEventCreate': { name: 'radioQuestionSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'RadioQuestionSubmissionEvent'; ofType: null; }; } }; 'shortLinkCreate': { name: 'shortLinkCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkCreateResult'; ofType: null; }; } }; 'shortLinkDelete': { name: 'shortLinkDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDeleteResult'; ofType: null; }; } }; 'shortLinkDomainCreate': { name: 'shortLinkDomainCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainCreateResult'; ofType: null; }; } }; 'shortLinkDomainDelete': { name: 'shortLinkDomainDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainDeleteResult'; ofType: null; }; } }; 'shortLinkDomainUpdate': { name: 'shortLinkDomainUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkDomainUpdateResult'; ofType: null; }; } }; 'shortLinkUpdate': { name: 'shortLinkUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationShortLinkUpdateResult'; ofType: null; }; } }; 'signUpBlockCreate': { name: 'signUpBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; }; } }; 'signUpBlockUpdate': { name: 'signUpBlockUpdate'; type: { kind: 'OBJECT'; name: 'SignUpBlock'; ofType: null; } }; 'signUpSubmissionEventCreate': { name: 'signUpSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SignUpSubmissionEvent'; ofType: null; }; } }; 'siteCreate': { name: 'siteCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'MutationSiteCreateResult'; ofType: null; }; } }; 'spacerBlockCreate': { name: 'spacerBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'spacerBlockUpdate': { name: 'spacerBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'SpacerBlock'; ofType: null; }; } }; 'stepBlockCreate': { name: 'stepBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepBlockPositionUpdate': { name: 'stepBlockPositionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; }; }; } }; 'stepBlockUpdate': { name: 'stepBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepBlock'; ofType: null; }; } }; 'stepNextEventCreate': { name: 'stepNextEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepNextEvent'; ofType: null; }; } }; 'stepPreviousEventCreate': { name: 'stepPreviousEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepPreviousEvent'; ofType: null; }; } }; 'stepViewEventCreate': { name: 'stepViewEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'StepViewEvent'; ofType: null; }; } }; 'teamCreate': { name: 'teamCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'teamUpdate': { name: 'teamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'textResponseBlockCreate': { name: 'textResponseBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; }; } }; 'textResponseBlockUpdate': { name: 'textResponseBlockUpdate'; type: { kind: 'OBJECT'; name: 'TextResponseBlock'; ofType: null; } }; 'textResponseSubmissionEventCreate': { name: 'textResponseSubmissionEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TextResponseSubmissionEvent'; ofType: null; }; } }; 'triggerUnsplashDownload': { name: 'triggerUnsplashDownload'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'typographyBlockCreate': { name: 'typographyBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'typographyBlockUpdate': { name: 'typographyBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TypographyBlock'; ofType: null; }; } }; 'updateJourneysEmailPreference': { name: 'updateJourneysEmailPreference'; type: { kind: 'OBJECT'; name: 'JourneysEmailPreference'; ofType: null; } }; 'updateVideoAlgoliaIndex': { name: 'updateVideoAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'updateVideoVariantAlgoliaIndex': { name: 'updateVideoVariantAlgoliaIndex'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'userImpersonate': { name: 'userImpersonate'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'userInviteAcceptAll': { name: 'userInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; }; }; } }; 'userInviteCreate': { name: 'userInviteCreate'; type: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; } }; 'userInviteRemove': { name: 'userInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; } }; 'userJourneyApprove': { name: 'userJourneyApprove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyOpen': { name: 'userJourneyOpen'; type: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; } }; 'userJourneyPromote': { name: 'userJourneyPromote'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemove': { name: 'userJourneyRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userJourneyRemoveAll': { name: 'userJourneyRemoveAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; }; }; } }; 'userJourneyRequest': { name: 'userJourneyRequest'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserJourney'; ofType: null; }; } }; 'userTeamDelete': { name: 'userTeamDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'userTeamInviteAcceptAll': { name: 'userTeamInviteAcceptAll'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; }; }; } }; 'userTeamInviteCreate': { name: 'userTeamInviteCreate'; type: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; } }; 'userTeamInviteRemove': { name: 'userTeamInviteRemove'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; } }; 'userTeamUpdate': { name: 'userTeamUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'validateEmail': { name: 'validateEmail'; type: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; } }; 'videoBlockCreate': { name: 'videoBlockCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoBlockUpdate': { name: 'videoBlockUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoBlock'; ofType: null; }; } }; 'videoCollapseEventCreate': { name: 'videoCollapseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCollapseEvent'; ofType: null; }; } }; 'videoCompleteEventCreate': { name: 'videoCompleteEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoCompleteEvent'; ofType: null; }; } }; 'videoCreate': { name: 'videoCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDelete': { name: 'videoDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoDescriptionCreate': { name: 'videoDescriptionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionDelete': { name: 'videoDescriptionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoDescriptionUpdate': { name: 'videoDescriptionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoDescription'; ofType: null; }; } }; 'videoEditionCreate': { name: 'videoEditionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionDelete': { name: 'videoEditionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoEditionUpdate': { name: 'videoEditionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; } }; 'videoExpandEventCreate': { name: 'videoExpandEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoExpandEvent'; ofType: null; }; } }; 'videoImageAltCreate': { name: 'videoImageAltCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltDelete': { name: 'videoImageAltDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoImageAltUpdate': { name: 'videoImageAltUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoImageAlt'; ofType: null; }; } }; 'videoOriginCreate': { name: 'videoOriginCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginDelete': { name: 'videoOriginDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoOriginUpdate': { name: 'videoOriginUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; } }; 'videoPauseEventCreate': { name: 'videoPauseEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPauseEvent'; ofType: null; }; } }; 'videoPlayEventCreate': { name: 'videoPlayEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPlayEvent'; ofType: null; }; } }; 'videoProgressEventCreate': { name: 'videoProgressEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoProgressEvent'; ofType: null; }; } }; 'videoPublishChildren': { name: 'videoPublishChildren'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenResult'; ofType: null; }; } }; 'videoPublishChildrenAndLanguages': { name: 'videoPublishChildrenAndLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoPublishChildrenAndLanguagesResult'; ofType: null; }; } }; 'videoSnippetCreate': { name: 'videoSnippetCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetDelete': { name: 'videoSnippetDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoSnippetUpdate': { name: 'videoSnippetUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSnippet'; ofType: null; }; } }; 'videoStartEventCreate': { name: 'videoStartEventCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStartEvent'; ofType: null; }; } }; 'videoStudyQuestionCreate': { name: 'videoStudyQuestionCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionDelete': { name: 'videoStudyQuestionDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoStudyQuestionUpdate': { name: 'videoStudyQuestionUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoStudyQuestion'; ofType: null; }; } }; 'videoSubtitleCreate': { name: 'videoSubtitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleDelete': { name: 'videoSubtitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoSubtitleUpdate': { name: 'videoSubtitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoSubtitle'; ofType: null; }; } }; 'videoTitleCreate': { name: 'videoTitleCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleDelete': { name: 'videoTitleDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoTitleUpdate': { name: 'videoTitleUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoTitle'; ofType: null; }; } }; 'videoUpdate': { name: 'videoUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoVariantCreate': { name: 'videoVariantCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDelete': { name: 'videoVariantDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariantDownloadCreate': { name: 'videoVariantDownloadCreate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadDelete': { name: 'videoVariantDownloadDelete'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantDownloadUpdate': { name: 'videoVariantDownloadUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariantDownload'; ofType: null; }; } }; 'videoVariantUpdate': { name: 'videoVariantUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'visitorUpdate': { name: 'visitorUpdate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; 'visitorUpdateForCurrentUser': { name: 'visitorUpdateForCurrentUser'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; }; }; 'MutationAudioPreviewCreateInput': { kind: 'INPUT_OBJECT'; name: 'MutationAudioPreviewCreateInput'; isOneOf: false; inputFields: [{ name: 'languageId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'value'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'duration'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'size'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'bitrate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'codec'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; 'MutationAudioPreviewUpdateInput': { kind: 'INPUT_OBJECT'; name: 'MutationAudioPreviewUpdateInput'; isOneOf: false; inputFields: [{ name: 'languageId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'value'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'duration'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'size'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'bitrate'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'codec'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'MutationBibleCitationCreateInput': { kind: 'INPUT_OBJECT'; name: 'MutationBibleCitationCreateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'osisId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'videoId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'bibleBookId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'chapterStart'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }, { name: 'chapterEnd'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'verseStart'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'verseEnd'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'order'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; }; defaultValue: null }]; }; @@ -239,7 +241,7 @@ export type introspection_types = { 'PlausibleStatsBreakdownFilter': { kind: 'INPUT_OBJECT'; name: 'PlausibleStatsBreakdownFilter'; isOneOf: false; inputFields: [{ name: 'property'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'period'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'date'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'limit'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'page'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; defaultValue: null }, { name: 'filters'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'PlausibleStatsResponse': { kind: 'OBJECT'; name: 'PlausibleStatsResponse'; fields: { 'bounceRate': { name: 'bounceRate'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'conversionRate': { name: 'conversionRate'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'events': { name: 'events'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'pageviews': { name: 'pageviews'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'property': { name: 'property'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'timeOnPage': { name: 'timeOnPage'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'viewsPerVisit': { name: 'viewsPerVisit'; type: { kind: 'SCALAR'; name: 'Float'; ofType: null; } }; 'visitDuration': { name: 'visitDuration'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'visitors': { name: 'visitors'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'visits': { name: 'visits'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; }; }; 'PlausibleStatsTimeseriesFilter': { kind: 'INPUT_OBJECT'; name: 'PlausibleStatsTimeseriesFilter'; isOneOf: false; inputFields: [{ name: 'period'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'date'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'filters'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'interval'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; - 'Playlist': { kind: 'OBJECT'; name: 'Playlist'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'items': { name: 'items'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlaylistItem'; ofType: null; }; }; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'note': { name: 'note'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'noteSharedAt': { name: 'noteSharedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'noteUpdatedAt': { name: 'noteUpdatedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'owner': { name: 'owner'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'User'; ofType: null; }; } }; 'sharedAt': { name: 'sharedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'slug': { name: 'slug'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; }; }; + 'Playlist': { kind: 'OBJECT'; name: 'Playlist'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'items': { name: 'items'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlaylistItem'; ofType: null; }; }; }; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'note': { name: 'note'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'noteSharedAt': { name: 'noteSharedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'noteUpdatedAt': { name: 'noteUpdatedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'owner': { name: 'owner'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; }; } }; 'sharedAt': { name: 'sharedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'slug': { name: 'slug'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; }; }; 'PlaylistCreateInput': { kind: 'INPUT_OBJECT'; name: 'PlaylistCreateInput'; isOneOf: false; inputFields: [{ name: 'id'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'note'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'noteSharedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; defaultValue: null }, { name: 'sharedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; defaultValue: null }, { name: 'slug'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'PlaylistItem': { kind: 'OBJECT'; name: 'PlaylistItem'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'order': { name: 'order'; type: { kind: 'SCALAR'; name: 'Int'; ofType: null; } }; 'playlist': { name: 'playlist'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Playlist'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'videoVariant': { name: 'videoVariant'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; }; }; 'PlaylistItemVideoInput': { kind: 'INPUT_OBJECT'; name: 'PlaylistItemVideoInput'; isOneOf: false; inputFields: [{ name: 'videoId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }, { name: 'languageId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; @@ -249,7 +251,7 @@ export type introspection_types = { 'QrCodeCreateInput': { kind: 'INPUT_OBJECT'; name: 'QrCodeCreateInput'; isOneOf: false; inputFields: [{ name: 'teamId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }, { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; }; defaultValue: null }]; }; 'QrCodeUpdateInput': { kind: 'INPUT_OBJECT'; name: 'QrCodeUpdateInput'; isOneOf: false; inputFields: [{ name: 'to'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'color'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }, { name: 'backgroundColor'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; }; defaultValue: null }]; }; 'QrCodesFilter': { kind: 'INPUT_OBJECT'; name: 'QrCodesFilter'; isOneOf: false; inputFields: [{ name: 'journeyId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }, { name: 'teamId'; type: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; defaultValue: null }]; }; - 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'adminJourney': { name: 'adminJourney'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'adminJourneys': { name: 'adminJourneys'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; }; }; } }; 'adminJourneysReport': { name: 'adminJourneysReport'; type: { kind: 'OBJECT'; name: 'PowerBiEmbed'; ofType: null; } }; 'adminVideo': { name: 'adminVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'adminVideos': { name: 'adminVideos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; }; }; } }; 'adminVideosCount': { name: 'adminVideosCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'arclightApiKeyByKey': { name: 'arclightApiKeyByKey'; type: { kind: 'OBJECT'; name: 'ArclightApiKey'; ofType: null; } }; 'arclightApiKeys': { name: 'arclightApiKeys'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ArclightApiKey'; ofType: null; }; }; }; } }; 'bibleBooks': { name: 'bibleBooks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BibleBook'; ofType: null; }; }; }; } }; 'bibleCitation': { name: 'bibleCitation'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; }; } }; 'bibleCitations': { name: 'bibleCitations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; }; }; }; } }; 'block': { name: 'block'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; } }; 'blocks': { name: 'blocks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'checkVideoInAlgolia': { name: 'checkVideoInAlgolia'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckVideoInAlgoliaResult'; ofType: null; }; } }; 'checkVideoVariantsInAlgolia': { name: 'checkVideoVariantsInAlgolia'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckVideoVariantsInAlgoliaResult'; ofType: null; }; } }; 'countries': { name: 'countries'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Country'; ofType: null; }; }; }; } }; 'country': { name: 'country'; type: { kind: 'OBJECT'; name: 'Country'; ofType: null; } }; 'customDomain': { name: 'customDomain'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomains': { name: 'customDomains'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; }; }; } }; 'getJourneyProfile': { name: 'getJourneyProfile'; type: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; } }; 'getMuxVideo': { name: 'getMuxVideo'; type: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; } }; 'getMyCloudflareImage': { name: 'getMyCloudflareImage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'getMyCloudflareImages': { name: 'getMyCloudflareImages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; }; }; } }; 'getMyGeneratedMuxSubtitleTrack': { name: 'getMyGeneratedMuxSubtitleTrack'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryGetMyGeneratedMuxSubtitleTrackResult'; ofType: null; }; } }; 'getMyMuxVideo': { name: 'getMyMuxVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'getMyMuxVideos': { name: 'getMyMuxVideos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; }; }; } }; 'getUserRole': { name: 'getUserRole'; type: { kind: 'OBJECT'; name: 'UserRole'; ofType: null; } }; 'googleSheetsSyncs': { name: 'googleSheetsSyncs'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; }; }; } }; 'hosts': { name: 'hosts'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; }; }; } }; 'integrationGooglePickerToken': { name: 'integrationGooglePickerToken'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'integrations': { name: 'integrations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Integration'; ofType: null; }; }; }; } }; 'journey': { name: 'journey'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCollection': { name: 'journeyCollection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollections': { name: 'journeyCollections'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; }; } }; 'journeyEventsConnection': { name: 'journeyEventsConnection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyEventsConnection'; ofType: null; }; } }; 'journeyEventsCount': { name: 'journeyEventsCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeySimpleGet': { name: 'journeySimpleGet'; type: { kind: 'SCALAR'; name: 'Json'; ofType: null; } }; 'journeyTheme': { name: 'journeyTheme'; type: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; } }; 'journeyVisitorCount': { name: 'journeyVisitorCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeyVisitorExport': { name: 'journeyVisitorExport'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'journeyVisitorsConnection': { name: 'journeyVisitorsConnection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyVisitorsConnection'; ofType: null; }; } }; 'journeys': { name: 'journeys'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; }; }; } }; 'journeysEmailPreference': { name: 'journeysEmailPreference'; type: { kind: 'OBJECT'; name: 'JourneysEmailPreference'; ofType: null; } }; 'journeysPlausibleStatsAggregate': { name: 'journeysPlausibleStatsAggregate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlausibleStatsAggregateResponse'; ofType: null; }; } }; 'journeysPlausibleStatsBreakdown': { name: 'journeysPlausibleStatsBreakdown'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlausibleStatsResponse'; ofType: null; }; }; }; } }; 'journeysPlausibleStatsRealtimeVisitors': { name: 'journeysPlausibleStatsRealtimeVisitors'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeysPlausibleStatsTimeseries': { name: 'journeysPlausibleStatsTimeseries'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlausibleStatsResponse'; ofType: null; }; }; }; } }; 'keywords': { name: 'keywords'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Keyword'; ofType: null; }; }; }; } }; 'language': { name: 'language'; type: { kind: 'OBJECT'; name: 'Language'; ofType: null; } }; 'languages': { name: 'languages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Language'; ofType: null; }; }; }; } }; 'languagesCount': { name: 'languagesCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'listUnsplashCollectionPhotos': { name: 'listUnsplashCollectionPhotos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UnsplashPhoto'; ofType: null; }; }; }; } }; 'me': { name: 'me'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'node': { name: 'node'; type: { kind: 'INTERFACE'; name: 'Node'; ofType: null; } }; 'nodes': { name: 'nodes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'INTERFACE'; name: 'Node'; ofType: null; }; }; } }; 'playlist': { name: 'playlist'; type: { kind: 'UNION'; name: 'QueryPlaylistResult'; ofType: null; } }; 'playlists': { name: 'playlists'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Playlist'; ofType: null; }; }; } }; 'qrCode': { name: 'qrCode'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodes': { name: 'qrCodes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; }; }; } }; 'searchUnsplashPhotos': { name: 'searchUnsplashPhotos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UnsplashQueryResponse'; ofType: null; }; } }; 'shortLink': { name: 'shortLink'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryShortLinkResult'; ofType: null; }; } }; 'shortLinkByPath': { name: 'shortLinkByPath'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryShortLinkByPathResult'; ofType: null; }; } }; 'shortLinkDomain': { name: 'shortLinkDomain'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryShortLinkDomainResult'; ofType: null; }; } }; 'shortLinkDomains': { name: 'shortLinkDomains'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QueryShortLinkDomainsConnection'; ofType: null; }; } }; 'shortLinks': { name: 'shortLinks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QueryShortLinksConnection'; ofType: null; }; } }; 'tags': { name: 'tags'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; }; } }; 'taxonomies': { name: 'taxonomies'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Taxonomy'; ofType: null; }; }; }; } }; 'team': { name: 'team'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'teams': { name: 'teams'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; }; }; } }; 'templateFamilyStatsAggregate': { name: 'templateFamilyStatsAggregate'; type: { kind: 'OBJECT'; name: 'TemplateFamilyStatsAggregateResponse'; ofType: null; } }; 'templateFamilyStatsBreakdown': { name: 'templateFamilyStatsBreakdown'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TemplateFamilyStatsBreakdownResponse'; ofType: null; }; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'userByEmail': { name: 'userByEmail'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'userInvites': { name: 'userInvites'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; }; } }; 'userTeam': { name: 'userTeam'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'userTeamInvites': { name: 'userTeamInvites'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; }; }; } }; 'userTeams': { name: 'userTeams'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; }; }; } }; 'video': { name: 'video'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoEdition': { name: 'videoEdition'; type: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; } }; 'videoEditions': { name: 'videoEditions'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; }; }; } }; 'videoOrigins': { name: 'videoOrigins'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; }; }; } }; 'videoVariant': { name: 'videoVariant'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariants': { name: 'videoVariants'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; }; }; } }; 'videos': { name: 'videos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; }; }; } }; 'videosCount': { name: 'videosCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'visitor': { name: 'visitor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; 'visitorsConnection': { name: 'visitorsConnection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VisitorsConnection'; ofType: null; }; } }; 'youtubeClosedCaptionLanguages': { name: 'youtubeClosedCaptionLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryYoutubeClosedCaptionLanguagesResult'; ofType: null; }; } }; }; }; + 'Query': { kind: 'OBJECT'; name: 'Query'; fields: { 'adminJourney': { name: 'adminJourney'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'adminJourneys': { name: 'adminJourneys'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; }; }; } }; 'adminJourneysReport': { name: 'adminJourneysReport'; type: { kind: 'OBJECT'; name: 'PowerBiEmbed'; ofType: null; } }; 'adminVideo': { name: 'adminVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'adminVideos': { name: 'adminVideos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; }; }; } }; 'adminVideosCount': { name: 'adminVideosCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'arclightApiKeyByKey': { name: 'arclightApiKeyByKey'; type: { kind: 'OBJECT'; name: 'ArclightApiKey'; ofType: null; } }; 'arclightApiKeys': { name: 'arclightApiKeys'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'ArclightApiKey'; ofType: null; }; }; }; } }; 'bibleBooks': { name: 'bibleBooks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BibleBook'; ofType: null; }; }; }; } }; 'bibleCitation': { name: 'bibleCitation'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; }; } }; 'bibleCitations': { name: 'bibleCitations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'BibleCitation'; ofType: null; }; }; }; } }; 'block': { name: 'block'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; } }; 'blocks': { name: 'blocks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Block'; ofType: null; }; }; }; } }; 'checkVideoInAlgolia': { name: 'checkVideoInAlgolia'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckVideoInAlgoliaResult'; ofType: null; }; } }; 'checkVideoVariantsInAlgolia': { name: 'checkVideoVariantsInAlgolia'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CheckVideoVariantsInAlgoliaResult'; ofType: null; }; } }; 'countries': { name: 'countries'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Country'; ofType: null; }; }; }; } }; 'country': { name: 'country'; type: { kind: 'OBJECT'; name: 'Country'; ofType: null; } }; 'customDomain': { name: 'customDomain'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; } }; 'customDomains': { name: 'customDomains'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CustomDomain'; ofType: null; }; }; }; } }; 'getJourneyProfile': { name: 'getJourneyProfile'; type: { kind: 'OBJECT'; name: 'JourneyProfile'; ofType: null; } }; 'getMuxVideo': { name: 'getMuxVideo'; type: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; } }; 'getMyCloudflareImage': { name: 'getMyCloudflareImage'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; } }; 'getMyCloudflareImages': { name: 'getMyCloudflareImages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'CloudflareImage'; ofType: null; }; }; }; } }; 'getMyGeneratedMuxSubtitleTrack': { name: 'getMyGeneratedMuxSubtitleTrack'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryGetMyGeneratedMuxSubtitleTrackResult'; ofType: null; }; } }; 'getMyMuxVideo': { name: 'getMyMuxVideo'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; } }; 'getMyMuxVideos': { name: 'getMyMuxVideos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxVideo'; ofType: null; }; }; }; } }; 'getUserRole': { name: 'getUserRole'; type: { kind: 'OBJECT'; name: 'UserRole'; ofType: null; } }; 'googleSheetsSyncs': { name: 'googleSheetsSyncs'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'GoogleSheetsSync'; ofType: null; }; }; }; } }; 'hosts': { name: 'hosts'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Host'; ofType: null; }; }; }; } }; 'integrationGooglePickerToken': { name: 'integrationGooglePickerToken'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'integrations': { name: 'integrations'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'INTERFACE'; name: 'Integration'; ofType: null; }; }; }; } }; 'journey': { name: 'journey'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; } }; 'journeyCollection': { name: 'journeyCollection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; } }; 'journeyCollections': { name: 'journeyCollections'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyCollection'; ofType: null; }; }; } }; 'journeyEventsConnection': { name: 'journeyEventsConnection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyEventsConnection'; ofType: null; }; } }; 'journeyEventsCount': { name: 'journeyEventsCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeySimpleGet': { name: 'journeySimpleGet'; type: { kind: 'SCALAR'; name: 'Json'; ofType: null; } }; 'journeyTheme': { name: 'journeyTheme'; type: { kind: 'OBJECT'; name: 'JourneyTheme'; ofType: null; } }; 'journeyVisitorCount': { name: 'journeyVisitorCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeyVisitorExport': { name: 'journeyVisitorExport'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'journeyVisitorsConnection': { name: 'journeyVisitorsConnection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'JourneyVisitorsConnection'; ofType: null; }; } }; 'journeys': { name: 'journeys'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Journey'; ofType: null; }; }; }; } }; 'journeysEmailPreference': { name: 'journeysEmailPreference'; type: { kind: 'OBJECT'; name: 'JourneysEmailPreference'; ofType: null; } }; 'journeysPlausibleStatsAggregate': { name: 'journeysPlausibleStatsAggregate'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlausibleStatsAggregateResponse'; ofType: null; }; } }; 'journeysPlausibleStatsBreakdown': { name: 'journeysPlausibleStatsBreakdown'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlausibleStatsResponse'; ofType: null; }; }; }; } }; 'journeysPlausibleStatsRealtimeVisitors': { name: 'journeysPlausibleStatsRealtimeVisitors'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'journeysPlausibleStatsTimeseries': { name: 'journeysPlausibleStatsTimeseries'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'PlausibleStatsResponse'; ofType: null; }; }; }; } }; 'keywords': { name: 'keywords'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Keyword'; ofType: null; }; }; }; } }; 'language': { name: 'language'; type: { kind: 'OBJECT'; name: 'Language'; ofType: null; } }; 'languages': { name: 'languages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Language'; ofType: null; }; }; }; } }; 'languagesCount': { name: 'languagesCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'listUnsplashCollectionPhotos': { name: 'listUnsplashCollectionPhotos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UnsplashPhoto'; ofType: null; }; }; }; } }; 'me': { name: 'me'; type: { kind: 'UNION'; name: 'User'; ofType: null; } }; 'node': { name: 'node'; type: { kind: 'INTERFACE'; name: 'Node'; ofType: null; } }; 'nodes': { name: 'nodes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'INTERFACE'; name: 'Node'; ofType: null; }; }; } }; 'playlist': { name: 'playlist'; type: { kind: 'UNION'; name: 'QueryPlaylistResult'; ofType: null; } }; 'playlists': { name: 'playlists'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Playlist'; ofType: null; }; }; } }; 'qrCode': { name: 'qrCode'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; } }; 'qrCodes': { name: 'qrCodes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QrCode'; ofType: null; }; }; }; } }; 'searchUnsplashPhotos': { name: 'searchUnsplashPhotos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UnsplashQueryResponse'; ofType: null; }; } }; 'shortLink': { name: 'shortLink'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryShortLinkResult'; ofType: null; }; } }; 'shortLinkByPath': { name: 'shortLinkByPath'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryShortLinkByPathResult'; ofType: null; }; } }; 'shortLinkDomain': { name: 'shortLinkDomain'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryShortLinkDomainResult'; ofType: null; }; } }; 'shortLinkDomains': { name: 'shortLinkDomains'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QueryShortLinkDomainsConnection'; ofType: null; }; } }; 'shortLinks': { name: 'shortLinks'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'QueryShortLinksConnection'; ofType: null; }; } }; 'tags': { name: 'tags'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Tag'; ofType: null; }; }; }; } }; 'taxonomies': { name: 'taxonomies'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Taxonomy'; ofType: null; }; }; }; } }; 'team': { name: 'team'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; } }; 'teams': { name: 'teams'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Team'; ofType: null; }; }; }; } }; 'templateFamilyStatsAggregate': { name: 'templateFamilyStatsAggregate'; type: { kind: 'OBJECT'; name: 'TemplateFamilyStatsAggregateResponse'; ofType: null; } }; 'templateFamilyStatsBreakdown': { name: 'templateFamilyStatsBreakdown'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'TemplateFamilyStatsBreakdownResponse'; ofType: null; }; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; } }; 'userByEmail': { name: 'userByEmail'; type: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; } }; 'userInvites': { name: 'userInvites'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserInvite'; ofType: null; }; }; } }; 'userTeam': { name: 'userTeam'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; } }; 'userTeamInvites': { name: 'userTeamInvites'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeamInvite'; ofType: null; }; }; }; } }; 'userTeams': { name: 'userTeams'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UserTeam'; ofType: null; }; }; }; } }; 'video': { name: 'video'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; } }; 'videoEdition': { name: 'videoEdition'; type: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; } }; 'videoEditions': { name: 'videoEditions'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoEdition'; ofType: null; }; }; }; } }; 'videoOrigins': { name: 'videoOrigins'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoOrigin'; ofType: null; }; }; }; } }; 'videoVariant': { name: 'videoVariant'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; } }; 'videoVariants': { name: 'videoVariants'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VideoVariant'; ofType: null; }; }; }; } }; 'videos': { name: 'videos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Video'; ofType: null; }; }; }; } }; 'videosCount': { name: 'videosCount'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'visitor': { name: 'visitor'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Visitor'; ofType: null; }; } }; 'visitorsConnection': { name: 'visitorsConnection'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'VisitorsConnection'; ofType: null; }; } }; 'youtubeClosedCaptionLanguages': { name: 'youtubeClosedCaptionLanguages'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'QueryYoutubeClosedCaptionLanguagesResult'; ofType: null; }; } }; }; }; 'QueryGetMyGeneratedMuxSubtitleTrackResult': { kind: 'UNION'; name: 'QueryGetMyGeneratedMuxSubtitleTrackResult'; fields: {}; possibleTypes: 'Error' | 'QueryGetMyGeneratedMuxSubtitleTrackSuccess'; }; 'QueryGetMyGeneratedMuxSubtitleTrackSuccess': { kind: 'OBJECT'; name: 'QueryGetMyGeneratedMuxSubtitleTrackSuccess'; fields: { 'data': { name: 'data'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'MuxSubtitleTrack'; ofType: null; }; } }; }; }; 'QueryPlaylistResult': { kind: 'UNION'; name: 'QueryPlaylistResult'; fields: {}; possibleTypes: 'NotFoundError' | 'QueryPlaylistSuccess'; }; @@ -344,14 +346,14 @@ export type introspection_types = { 'UnsplashUser': { kind: 'OBJECT'; name: 'UnsplashUser'; fields: { 'bio': { name: 'bio'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'first_name': { name: 'first_name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'instagram_username': { name: 'instagram_username'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'last_name': { name: 'last_name'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'links': { name: 'links'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UnsplashUserLinks'; ofType: null; }; } }; 'location': { name: 'location'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'name': { name: 'name'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'portfolio_url': { name: 'portfolio_url'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'profile_image': { name: 'profile_image'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'UnsplashUserImage'; ofType: null; }; } }; 'total_collections': { name: 'total_collections'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'total_likes': { name: 'total_likes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'total_photos': { name: 'total_photos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Int'; ofType: null; }; } }; 'twitter_username': { name: 'twitter_username'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'updated_at': { name: 'updated_at'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'username': { name: 'username'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'UnsplashUserImage': { kind: 'OBJECT'; name: 'UnsplashUserImage'; fields: { 'large': { name: 'large'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'medium': { name: 'medium'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'small': { name: 'small'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; 'UnsplashUserLinks': { kind: 'OBJECT'; name: 'UnsplashUserLinks'; fields: { 'followers': { name: 'followers'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'following': { name: 'following'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'html': { name: 'html'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'likes': { name: 'likes'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'photos': { name: 'photos'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'portfolio': { name: 'portfolio'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'self': { name: 'self'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; }; }; - 'User': { kind: 'OBJECT'; name: 'User'; fields: { 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'emailVerified': { name: 'emailVerified'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; }; } }; 'firstName': { name: 'firstName'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'imageUrl': { name: 'imageUrl'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'languageUserRoles': { name: 'languageUserRoles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'LanguageRole'; ofType: null; }; }; }; } }; 'lastName': { name: 'lastName'; type: { kind: 'SCALAR'; name: 'String'; ofType: null; } }; 'mediaUserRoles': { name: 'mediaUserRoles'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'MediaRole'; ofType: null; }; }; }; } }; 'superAdmin': { name: 'superAdmin'; type: { kind: 'SCALAR'; name: 'Boolean'; ofType: null; } }; }; }; + 'User': { kind: 'UNION'; name: 'User'; fields: {}; possibleTypes: 'AnonymousUser' | 'AuthenticatedUser'; }; 'UserAgent': { kind: 'OBJECT'; name: 'UserAgent'; fields: { 'browser': { name: 'browser'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Browser'; ofType: null; }; } }; 'device': { name: 'device'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'Device'; ofType: null; }; } }; 'os': { name: 'os'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'OperatingSystem'; ofType: null; }; } }; }; }; 'UserInvite': { kind: 'OBJECT'; name: 'UserInvite'; fields: { 'acceptedAt': { name: 'acceptedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'removedAt': { name: 'removedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'senderId': { name: 'senderId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; 'UserInviteCreateInput': { kind: 'INPUT_OBJECT'; name: 'UserInviteCreateInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; }; - 'UserJourney': { kind: 'OBJECT'; name: 'UserJourney'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journey': { name: 'journey'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyNotification': { name: 'journeyNotification'; type: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; } }; 'openedAt': { name: 'openedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'role': { name: 'role'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'UserJourneyRole'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'User'; ofType: null; } }; 'userId': { name: 'userId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; + 'UserJourney': { kind: 'OBJECT'; name: 'UserJourney'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journey': { name: 'journey'; type: { kind: 'OBJECT'; name: 'Journey'; ofType: null; } }; 'journeyId': { name: 'journeyId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyNotification': { name: 'journeyNotification'; type: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; } }; 'openedAt': { name: 'openedAt'; type: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; } }; 'role': { name: 'role'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'UserJourneyRole'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'OBJECT'; name: 'AuthenticatedUser'; ofType: null; } }; 'userId': { name: 'userId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; 'UserJourneyRole': { name: 'UserJourneyRole'; enumValues: 'inviteRequested' | 'editor' | 'owner'; }; 'UserRole': { kind: 'OBJECT'; name: 'UserRole'; fields: { 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'roles': { name: 'roles'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'Role'; ofType: null; }; }; } }; 'userId': { name: 'userId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; - 'UserTeam': { kind: 'OBJECT'; name: 'UserTeam'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyNotification': { name: 'journeyNotification'; type: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; } }; 'role': { name: 'role'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'UserTeamRole'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'OBJECT'; name: 'User'; ofType: null; }; } }; }; }; + 'UserTeam': { kind: 'OBJECT'; name: 'UserTeam'; fields: { 'createdAt': { name: 'createdAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'journeyNotification': { name: 'journeyNotification'; type: { kind: 'OBJECT'; name: 'JourneyNotification'; ofType: null; } }; 'role': { name: 'role'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'UserTeamRole'; ofType: null; }; } }; 'updatedAt': { name: 'updatedAt'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'DateTime'; ofType: null; }; } }; 'user': { name: 'user'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'UNION'; name: 'User'; ofType: null; }; } }; }; }; 'UserTeamFilterInput': { kind: 'INPUT_OBJECT'; name: 'UserTeamFilterInput'; isOneOf: false; inputFields: [{ name: 'role'; type: { kind: 'LIST'; name: never; ofType: { kind: 'NON_NULL'; name: never; ofType: { kind: 'ENUM'; name: 'UserTeamRole'; ofType: null; }; }; }; defaultValue: null }]; }; 'UserTeamInvite': { kind: 'OBJECT'; name: 'UserTeamInvite'; fields: { 'email': { name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; } }; 'id': { name: 'id'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; 'teamId': { name: 'teamId'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'ID'; ofType: null; }; } }; }; }; 'UserTeamInviteCreateInput': { kind: 'INPUT_OBJECT'; name: 'UserTeamInviteCreateInput'; isOneOf: false; inputFields: [{ name: 'email'; type: { kind: 'NON_NULL'; name: never; ofType: { kind: 'SCALAR'; name: 'String'; ofType: null; }; }; defaultValue: null }]; };