Skip to content

Commit

Permalink
feat(sso): allow to join orga with Google
Browse files Browse the repository at this point in the history
  • Loading branch information
ansmonjol committed Apr 2, 2024
1 parent c7684ac commit 196b79c
Show file tree
Hide file tree
Showing 9 changed files with 214 additions and 50 deletions.
14 changes: 9 additions & 5 deletions src/components/auth/GoogleAuthButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import { useEffect, useState } from 'react'
import { useSearchParams } from 'react-router-dom'

import { hasDefinedGQLError } from '~/core/apolloClient'
import { addModeToUrlState } from '~/core/utils/urlUtils'
import { addValuesToUrlState } from '~/core/utils/urlUtils'
import { LagoApiError, useGetGoogleAuthUrlLazyQuery } from '~/generated/graphql'
import { useInternationalization } from '~/hooks/core/useInternationalization'

import { Alert, Button } from '../designSystem'

export type GoogleAuthModeEnum = 'login' | 'signup'
export type GoogleAuthModeEnum = 'login' | 'signup' | 'invite'

const getErrorKey = (errorCode: string): string => {
switch (errorCode) {
Expand All @@ -33,12 +33,13 @@ gql`
}
`

type GoogleAuthButtonProps = {
type BasicGoogleAuthButtonProps = {
mode: GoogleAuthModeEnum
label: string
invitationToken?: string
}

const GoogleAuthButton = ({ label, mode }: GoogleAuthButtonProps) => {
const GoogleAuthButton = ({ invitationToken, label, mode }: BasicGoogleAuthButtonProps) => {
const { translate } = useInternationalization()
let [searchParams] = useSearchParams()

Expand Down Expand Up @@ -86,7 +87,10 @@ const GoogleAuthButton = ({ label, mode }: GoogleAuthButtonProps) => {
}

if (data?.googleAuthUrl?.url) {
window.location.href = addModeToUrlState(data.googleAuthUrl.url, mode)
window.location.href = addValuesToUrlState(data.googleAuthUrl.url, {
mode,
...(!!invitationToken && { invitationToken }),
})
}
}}
>
Expand Down
11 changes: 1 addition & 10 deletions src/components/settings/members/RevokeInviteDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,7 @@ export const RevokeInviteDialog = forwardRef<RevokeInviteDialogRef>((_, ref) =>
})
}
},
update(cache, { data }) {
if (!data?.revokeInvite) return

const cacheId = cache.identify({
id: data?.revokeInvite.id,
__typename: 'Invite',
})

cache.evict({ id: cacheId })
},
refetchQueries: ['getInvites'],
})

const [inviteInfos, setInviteInfos] = useState<
Expand Down
1 change: 1 addition & 0 deletions src/core/apolloClient/graphqlResolvers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export const typeDefs = gql`
not_organization_member
# Validation errors
invite_email_mistmatch
user_already_exists
user_does_not_exists
coupon_is_not_reusable
Expand Down
20 changes: 12 additions & 8 deletions src/core/utils/__tests__/urlUtils.test.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
import { addModeToUrlState } from '../urlUtils'
import { addValuesToUrlState } from '../urlUtils'

describe('urlUtils', () => {
describe('addModeToUrlState', () => {
describe('addValuesToUrlState', () => {
it('should create state with value if not existing', () => {
const url = 'http://localhost:3000'
const mode = 'login'
const result = addModeToUrlState(url, mode)
const result = addValuesToUrlState(url, { mode, test: 'value' })

expect(result).toEqual('http://localhost:3000/?state=%7B%22mode%22%3A%22login%22%7D')
expect(result).toEqual(
'"http://localhost:3000/?state=%7B%22mode%22%3A%22login%22%2C%22test%22%3A%22value%22%7D',
)
})

it('should add mode to url state', () => {
const url = 'http://localhost:3000?state={}'
const mode = 'login'
const result = addModeToUrlState(url, mode)
const result = addValuesToUrlState(url, { mode, test: 'value' })

expect(result).toEqual('http://localhost:3000/?state=%7B%22mode%22%3A%22login%22%7D')
expect(result).toEqual(
'"http://localhost:3000/?state=%7B%22mode%22%3A%22login%22%2C%22test%22%3A%22value%22%7D',
)
})

it('should add happen mode to existing state values', () => {
const url = 'http://localhost:3000?state={"other":"value"}'
const mode = 'login'
const result = addModeToUrlState(url, mode)
const result = addValuesToUrlState(url, { mode, test: 'value' })

expect(result).toEqual(
'http://localhost:3000/?state=%7B%22other%22%3A%22value%22%2C%22mode%22%3A%22login%22%7D',
'http://localhost:3000/?state=%7B%22other%22%3A%22value%22%2C%22mode%22%3A%22login%22%2C%22test%22%3A%22value%22%7D',
)
})
})
Expand Down
4 changes: 2 additions & 2 deletions src/core/utils/urlUtils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
export const addModeToUrlState = (url: string, mode: string) => {
export const addValuesToUrlState = (url: string, values: Record<string, string>) => {
let urlObj = new URL(url)
let urlSearchParams = urlObj.searchParams
let state = JSON.parse(urlSearchParams.get('state') || ('{}' as string))

state.mode = mode
state = { ...state, ...values }

urlSearchParams.set('state', decodeURI(JSON.stringify(state)))
urlObj.search = urlSearchParams.toString()
Expand Down
63 changes: 62 additions & 1 deletion src/generated/graphql.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ export type ChargeFilterUsage = {
__typename?: 'ChargeFilterUsage';
amountCents: Scalars['BigInt']['output'];
eventsCount: Scalars['Int']['output'];
id: Scalars['ID']['output'];
id?: Maybe<Scalars['ID']['output']>;
invoiceDisplayName?: Maybe<Scalars['String']['output']>;
units: Scalars['Float']['output'];
values: Scalars['ChargeFilterValues']['output'];
Expand Down Expand Up @@ -2111,6 +2111,8 @@ export type Invoice = {
issuingDate: Scalars['ISO8601Date']['output'];
metadata?: Maybe<Array<InvoiceMetadata>>;
number: Scalars['String']['output'];
paymentDisputeLosable: Scalars['Boolean']['output'];
paymentDisputeLostAt?: Maybe<Scalars['ISO8601DateTime']['output']>;
paymentDueDate: Scalars['ISO8601Date']['output'];
paymentStatus: InvoicePaymentStatusTypeEnum;
prepaidCreditAmountCents: Scalars['BigInt']['output'];
Expand Down Expand Up @@ -2240,6 +2242,7 @@ export enum LagoApiError {
InvalidGoogleCode = 'invalid_google_code',
InvalidGoogleToken = 'invalid_google_token',
InviteAlreadyExists = 'invite_already_exists',
InviteEmailMistmatch = 'invite_email_mistmatch',
InviteNotFound = 'invite_not_found',
NotFound = 'not_found',
NotOrganizationMember = 'not_organization_member',
Expand Down Expand Up @@ -2276,6 +2279,13 @@ export type LoginUserInput = {
password: Scalars['String']['input'];
};

/** Autogenerated input type of LoseInvoiceDispute */
export type LoseInvoiceDisputeInput = {
/** A unique identifier for the client performing the mutation. */
clientMutationId?: InputMaybe<Scalars['String']['input']>;
id: Scalars['ID']['input'];
};

export type Membership = {
__typename?: 'Membership';
createdAt: Scalars['ISO8601DateTime']['output'];
Expand Down Expand Up @@ -2390,6 +2400,8 @@ export type Mutation = {
googleRegisterUser?: Maybe<RegisterUser>;
/** Opens a session for an existing user */
loginUser?: Maybe<LoginUser>;
/** Mark payment dispute as lost */
loseInvoiceDispute?: Maybe<Invoice>;
/** Refresh a draft invoice */
refreshInvoice?: Maybe<Invoice>;
/** Registers a new user and creates related organization */
Expand Down Expand Up @@ -2643,6 +2655,11 @@ export type MutationLoginUserArgs = {
};


export type MutationLoseInvoiceDisputeArgs = {
input: LoseInvoiceDisputeInput;
};


export type MutationRefreshInvoiceArgs = {
input: RefreshInvoiceInput;
};
Expand Down Expand Up @@ -5556,6 +5573,13 @@ export type AcceptInviteMutationVariables = Exact<{

export type AcceptInviteMutation = { __typename?: 'Mutation', acceptInvite?: { __typename?: 'RegisterUser', token: string, user: { __typename?: 'User', id: string, organizations?: Array<{ __typename?: 'SafeOrganization', id: string, name: string, timezone?: TimezoneEnum | null }> | null } } | null };

export type GoogleAcceptInviteMutationVariables = Exact<{
input: GoogleAcceptInviteInput;
}>;


export type GoogleAcceptInviteMutation = { __typename?: 'Mutation', googleAcceptInvite?: { __typename?: 'RegisterUser', token: string, user: { __typename?: 'User', id: string, organizations?: Array<{ __typename?: 'SafeOrganization', id: string, name: string, timezone?: TimezoneEnum | null }> | null } } | null };

export type GetInvoiceCreditNotesQueryVariables = Exact<{
invoiceId: Scalars['ID']['input'];
page?: InputMaybe<Scalars['Int']['input']>;
Expand Down Expand Up @@ -13835,6 +13859,43 @@ export function useAcceptInviteMutation(baseOptions?: Apollo.MutationHookOptions
export type AcceptInviteMutationHookResult = ReturnType<typeof useAcceptInviteMutation>;
export type AcceptInviteMutationResult = Apollo.MutationResult<AcceptInviteMutation>;
export type AcceptInviteMutationOptions = Apollo.BaseMutationOptions<AcceptInviteMutation, AcceptInviteMutationVariables>;
export const GoogleAcceptInviteDocument = gql`
mutation googleAcceptInvite($input: GoogleAcceptInviteInput!) {
googleAcceptInvite(input: $input) {
token
user {
id
...CurrentUser
}
}
}
${CurrentUserFragmentDoc}`;
export type GoogleAcceptInviteMutationFn = Apollo.MutationFunction<GoogleAcceptInviteMutation, GoogleAcceptInviteMutationVariables>;

/**
* __useGoogleAcceptInviteMutation__
*
* To run a mutation, you first call `useGoogleAcceptInviteMutation` within a React component and pass it any options that fit your needs.
* When your component renders, `useGoogleAcceptInviteMutation` returns a tuple that includes:
* - A mutate function that you can call at any time to execute the mutation
* - An object with fields that represent the current status of the mutation's execution
*
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
*
* @example
* const [googleAcceptInviteMutation, { data, loading, error }] = useGoogleAcceptInviteMutation({
* variables: {
* input: // value for 'input'
* },
* });
*/
export function useGoogleAcceptInviteMutation(baseOptions?: Apollo.MutationHookOptions<GoogleAcceptInviteMutation, GoogleAcceptInviteMutationVariables>) {
const options = {...defaultOptions, ...baseOptions}
return Apollo.useMutation<GoogleAcceptInviteMutation, GoogleAcceptInviteMutationVariables>(GoogleAcceptInviteDocument, options);
}
export type GoogleAcceptInviteMutationHookResult = ReturnType<typeof useGoogleAcceptInviteMutation>;
export type GoogleAcceptInviteMutationResult = Apollo.MutationResult<GoogleAcceptInviteMutation>;
export type GoogleAcceptInviteMutationOptions = Apollo.BaseMutationOptions<GoogleAcceptInviteMutation, GoogleAcceptInviteMutationVariables>;
export const GetInvoiceCreditNotesDocument = gql`
query getInvoiceCreditNotes($invoiceId: ID!, $page: Int, $limit: Int) {
invoiceCreditNotes(invoiceId: $invoiceId, page: $page, limit: $limit) {
Expand Down
Loading

0 comments on commit 196b79c

Please sign in to comment.