Skip to content

Commit

Permalink
upcoming: [M3-7659] - Add user_type to /profile endpoint (linode#…
Browse files Browse the repository at this point in the history
…10080)


Co-authored-by: Jaalah Ramos <[email protected]>
Co-authored-by: Banks Nussman <[email protected]>
  • Loading branch information
3 people authored Jan 19, 2024
1 parent e4e38f9 commit d938bfe
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/api-v4": Upcoming Features
---

Add `user_type` to /profile endpoint for Parent/Child user roles ([#10080](https://github.com/linode/manager/pull/10080))
3 changes: 3 additions & 0 deletions packages/api-v4/src/profile/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { UserType } from '../account';

export interface Referrals {
code: string;
url: string;
Expand All @@ -22,6 +24,7 @@ export interface Profile {
two_factor_auth: boolean;
restricted: boolean;
verified_phone_number: string | null;
user_type: UserType | null;
}

export interface TokenRequest {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Upcoming Features
---

Add `user_type` to /profile endpoint for Parent/Child user roles ([#10080](https://github.com/linode/manager/pull/10080))
Original file line number Diff line number Diff line change
Expand Up @@ -277,11 +277,11 @@ describe('restricted user billing flows', () => {
it('cannot edit billing information as child account', () => {
const mockProfile = profileFactory.build({
username: randomLabel(),
user_type: 'child',
});

const mockUser = accountUserFactory.build({
username: mockProfile.username,
user_type: 'child',
});

mockGetProfile(mockProfile);
Expand Down
1 change: 1 addition & 0 deletions packages/manager/src/factories/profile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const profileFactory = Factory.Sync.makeFactory<Profile>({
timezone: 'Asia/Shanghai',
two_factor_auth: false,
uid: 9999,
user_type: null,
username: 'mock-user',
verified_phone_number: '+15555555555',
});
Expand Down
5 changes: 5 additions & 0 deletions packages/manager/src/features/Billing/BillingDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ErrorState } from 'src/components/ErrorState/ErrorState';
import { PAYPAL_CLIENT_ID } from 'src/constants';
import { useAccount } from 'src/queries/account';
import { useAllPaymentMethodsQuery } from 'src/queries/accountPayment';
import { useProfile } from 'src/queries/profile';
import { getAPIErrorOrDefault } from 'src/utilities/errorUtils';

import BillingActivityPanel from './BillingPanels/BillingActivityPanel/BillingActivityPanel';
Expand All @@ -31,6 +32,8 @@ export const BillingDetail = () => {
isLoading: accountLoading,
} = useAccount();

const { data: profile } = useProfile();

if (accountLoading) {
return <CircleProgress />;
}
Expand Down Expand Up @@ -75,6 +78,7 @@ export const BillingDetail = () => {
firstName={account.first_name}
lastName={account.last_name}
phone={account.phone}
profile={profile}
state={account.state}
taxId={account.tax_id}
zip={account.zip}
Expand All @@ -84,6 +88,7 @@ export const BillingDetail = () => {
isAkamaiCustomer={account?.billing_source === 'akamai'}
loading={paymentMethodsLoading}
paymentMethods={paymentMethods}
profile={profile}
/>
<BillingActivityPanel accountActiveSince={account?.active_since} />
</Grid>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import * as React from 'react';

import { profileFactory } from 'src/factories';
import { grantsFactory } from 'src/factories/grants';
import { accountUserFactory } from 'src/factories/accountUsers';
import { renderWithTheme } from 'src/utilities/testHelpers';

import ContactInformation from './ContactInformation';

const EDIT_BUTTON_ID = 'edit-contact-info';

const queryMocks = vi.hoisted(() => ({
useGrants: vi.fn().mockReturnValue({}),
useProfile: vi.fn().mockReturnValue({}),
}));

const props = {
address1: '123 Linode Lane',
address2: '',
Expand All @@ -18,21 +23,17 @@ const props = {
firstName: 'Linny',
lastName: 'The Platypus',
phone: '19005553221',
profile: queryMocks.useProfile().data,
state: 'PA',
taxId: '1337',
zip: '19106',
};

const queryMocks = vi.hoisted(() => ({
useAccountUser: vi.fn().mockReturnValue({}),
useGrants: vi.fn().mockReturnValue({}),
}));

vi.mock('src/queries/accountUsers', async () => {
const actual = await vi.importActual<any>('src/queries/accountUsers');
vi.mock('src/queries/profile', async () => {
const actual = await vi.importActual<any>('src/queries/profile');
return {
...actual,
useAccountUser: queryMocks.useAccountUser,
useProfile: queryMocks.useProfile,
};
});

Expand All @@ -44,19 +45,20 @@ vi.mock('src/queries/profile', async () => {
};
});

queryMocks.useAccountUser.mockReturnValue({
data: accountUserFactory.build({ user_type: 'parent' }),
});

describe('Edit Contact Information', () => {
it('should be disabled for all child users', () => {
queryMocks.useAccountUser.mockReturnValue({
data: accountUserFactory.build({ user_type: 'child' }),
queryMocks.useProfile.mockReturnValue({
data: profileFactory.build({
user_type: 'child',
}),
});

const { getByTestId } = renderWithTheme(<ContactInformation {...props} />, {
flags: { parentChildAccountAccess: true },
});
const { getByTestId } = renderWithTheme(
<ContactInformation {...props} profile={queryMocks.useProfile().data} />,
{
flags: { parentChildAccountAccess: true },
}
);

expect(getByTestId(EDIT_BUTTON_ID)).toHaveAttribute(
'aria-disabled',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ import { Typography } from 'src/components/Typography';
import { getDisabledTooltipText } from 'src/features/Billing/billingUtils';
import { EDIT_BILLING_CONTACT } from 'src/features/Billing/constants';
import { useFlags } from 'src/hooks/useFlags';
import { useAccountUser } from 'src/queries/accountUsers';
import { useGrants, useProfile } from 'src/queries/profile';
import { useGrants } from 'src/queries/profile';

import {
BillingActionButton,
Expand All @@ -18,6 +17,8 @@ import {
} from '../../BillingDetail';
import BillingContactDrawer from './EditBillingContactDrawer';

import type { Profile } from '@linode/api-v4';

interface Props {
address1: string;
address2: string;
Expand All @@ -28,6 +29,7 @@ interface Props {
firstName: string;
lastName: string;
phone: string;
profile: Profile | undefined;
state: string;
taxId: string;
zip: string;
Expand Down Expand Up @@ -57,6 +59,7 @@ const ContactInformation = (props: Props) => {
firstName,
lastName,
phone,
profile,
state,
taxId,
zip,
Expand All @@ -75,11 +78,10 @@ const ContactInformation = (props: Props) => {
const [focusEmail, setFocusEmail] = React.useState(false);

const flags = useFlags();
const { data: profile } = useProfile();
const { data: grants } = useGrants();
const { data: user } = useAccountUser(profile?.username ?? '');

const isChildUser =
flags.parentChildAccountAccess && user?.user_type === 'child';
flags.parentChildAccountAccess && profile?.user_type === 'child';

const isRestrictedUser =
isChildUser || grants?.global.account_access === 'read_only';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import { fireEvent } from '@testing-library/react';
import * as React from 'react';

import { PAYPAL_CLIENT_ID } from 'src/constants';
import { paymentMethodFactory } from 'src/factories';
import { profileFactory } from 'src/factories';
import { accountUserFactory } from 'src/factories/accountUsers';
import { paymentMethodFactory } from 'src/factories';
import { grantsFactory } from 'src/factories/grants';
import { renderWithTheme, wrapWithTheme } from 'src/utilities/testHelpers';

Expand All @@ -22,32 +21,18 @@ vi.mock('@linode/api-v4/lib/account', async () => {
});

const queryMocks = vi.hoisted(() => ({
useAccountUser: vi.fn().mockReturnValue({}),
useGrants: vi.fn().mockReturnValue({}),
useProfile: vi.fn().mockReturnValue({}),
}));

vi.mock('src/queries/accountUsers', async () => {
const actual = await vi.importActual<any>('src/queries/accountUsers');
return {
...actual,
useAccountUser: queryMocks.useAccountUser,
};
});

vi.mock('src/queries/profile', async () => {
const actual = await vi.importActual<any>('src/queries/profile');
return {
...actual,
useGrants: queryMocks.useGrants,
useProfile: queryMocks.useAccountUser,
};
});

queryMocks.useAccountUser.mockReturnValue({
data: accountUserFactory.build({ user_type: 'parent' }),
});

/*
* Build payment method list that includes 1 valid and default payment method,
* 2 valid non-default payment methods, and 1 expired payment method.
Expand All @@ -64,15 +49,18 @@ const paymentMethods = [
}),
];

const props = {
isAkamaiCustomer: false,
loading: false,
paymentMethods,
profile: queryMocks.useProfile().data,
};

describe('Payment Info Panel', () => {
it('Shows loading animation when loading', () => {
const { getByLabelText } = renderWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={false}
loading={true}
paymentMethods={paymentMethods}
/>
<PaymentInformation {...props} loading={true} />
</PayPalScriptProvider>
);

Expand All @@ -82,11 +70,7 @@ describe('Payment Info Panel', () => {
it('Shows Add Payment button for Linode customers and hides it for Akamai customers', () => {
const { getByTestId, queryByText, rerender } = renderWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={false}
loading={false}
paymentMethods={paymentMethods}
/>
<PaymentInformation {...props} loading={false} />
</PayPalScriptProvider>
);

Expand All @@ -95,11 +79,7 @@ describe('Payment Info Panel', () => {
rerender(
wrapWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={true}
loading={false}
paymentMethods={paymentMethods}
/>
<PaymentInformation {...props} isAkamaiCustomer={true} />
</PayPalScriptProvider>
)
);
Expand All @@ -110,11 +90,7 @@ describe('Payment Info Panel', () => {
it('Opens "Add Payment Method" drawer when "Add Payment Method" is clicked', () => {
const { getByTestId } = renderWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={false}
loading={false}
paymentMethods={paymentMethods}
/>
<PaymentInformation {...props} />
</PayPalScriptProvider>
);

Expand All @@ -127,11 +103,7 @@ describe('Payment Info Panel', () => {
it('Lists all payment methods for Linode customers', () => {
const { getByTestId } = renderWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={false}
loading={false}
paymentMethods={paymentMethods}
/>
<PaymentInformation {...props} />
</PayPalScriptProvider>
);

Expand All @@ -145,11 +117,7 @@ describe('Payment Info Panel', () => {
it('Hides payment methods and shows text for Akamai customers', () => {
const { getByTestId, queryByTestId } = renderWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={true}
loading={false}
paymentMethods={paymentMethods}
/>
<PaymentInformation {...props} isAkamaiCustomer={true} />
</PayPalScriptProvider>
);

Expand All @@ -166,19 +134,15 @@ describe('Payment Info Panel', () => {
queryMocks.useProfile.mockReturnValue({
data: profileFactory.build({
restricted: false,
user_type: 'child',
}),
});

queryMocks.useAccountUser.mockReturnValue({
data: accountUserFactory.build({ user_type: 'child' }),
});

const { getByTestId } = renderWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={false}
loading={false}
paymentMethods={paymentMethods}
{...props}
profile={queryMocks.useProfile().data}
/>
</PayPalScriptProvider>,
{
Expand All @@ -203,11 +167,7 @@ describe('Payment Info Panel', () => {

const { getByTestId } = renderWithTheme(
<PayPalScriptProvider options={{ 'client-id': PAYPAL_CLIENT_ID }}>
<PaymentInformation
isAkamaiCustomer={false}
loading={false}
paymentMethods={paymentMethods}
/>
<PaymentInformation {...props} />
</PayPalScriptProvider>
);

Expand Down
Loading

0 comments on commit d938bfe

Please sign in to comment.