diff --git a/packages/api-v4/.changeset/pr-10176-upcoming-features-1707761577767.md b/packages/api-v4/.changeset/pr-10176-upcoming-features-1707761577767.md new file mode 100644 index 00000000000..23e5ccd8bb9 --- /dev/null +++ b/packages/api-v4/.changeset/pr-10176-upcoming-features-1707761577767.md @@ -0,0 +1,5 @@ +--- +"@linode/api-v4": Upcoming Features +--- + +Update /account and /profile UserType from `null` to `"default"` ([#10176](https://github.com/linode/manager/pull/10176)) diff --git a/packages/api-v4/src/account/types.ts b/packages/api-v4/src/account/types.ts index 6ad2d55d9a7..24594de091b 100644 --- a/packages/api-v4/src/account/types.ts +++ b/packages/api-v4/src/account/types.ts @@ -1,7 +1,7 @@ import type { APIWarning, RequestOptions } from '../types'; import type { Capabilities, Region } from '../regions'; -export type UserType = 'child' | 'parent' | 'proxy'; +export type UserType = 'child' | 'parent' | 'proxy' | 'default'; export interface User { email: string; @@ -30,7 +30,7 @@ export interface User { ssh_keys: string[]; tfa_enabled: boolean; username: string; - user_type: UserType | null; + user_type: UserType; verified_phone_number: string | null; } diff --git a/packages/api-v4/src/profile/types.ts b/packages/api-v4/src/profile/types.ts index 0d7ea3347f3..aed94d75fda 100644 --- a/packages/api-v4/src/profile/types.ts +++ b/packages/api-v4/src/profile/types.ts @@ -24,7 +24,7 @@ export interface Profile { two_factor_auth: boolean; restricted: boolean; verified_phone_number: string | null; - user_type: UserType | null; + user_type: UserType; } export interface TokenRequest { diff --git a/packages/manager/.changeset/pr-10176-tests-1707761742808.md b/packages/manager/.changeset/pr-10176-tests-1707761742808.md new file mode 100644 index 00000000000..eb49a5fd391 --- /dev/null +++ b/packages/manager/.changeset/pr-10176-tests-1707761742808.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Tests +--- + +Update Cypress tests to use `"default"` `user_type` for non-parent/child/proxy users ([#10176](https://github.com/linode/manager/pull/10176)) diff --git a/packages/manager/.changeset/pr-10176-upcoming-features-1707761677226.md b/packages/manager/.changeset/pr-10176-upcoming-features-1707761677226.md new file mode 100644 index 00000000000..3cd08f62239 --- /dev/null +++ b/packages/manager/.changeset/pr-10176-upcoming-features-1707761677226.md @@ -0,0 +1,5 @@ +--- +"@linode/manager": Upcoming Features +--- + +Update components and unit tests to use `"default"` `user_type` for non-parent/child/proxy users ([#10176](https://github.com/linode/manager/pull/10176)) diff --git a/packages/manager/cypress/e2e/core/account/account-login-history.spec.ts b/packages/manager/cypress/e2e/core/account/account-login-history.spec.ts index 8bada4f0c4a..6b213f48bf4 100644 --- a/packages/manager/cypress/e2e/core/account/account-login-history.spec.ts +++ b/packages/manager/cypress/e2e/core/account/account-login-history.spec.ts @@ -2,13 +2,10 @@ * @file Integration tests for Cloud Manager account login history flows. */ -import { accountFactory, profileFactory } from 'src/factories'; +import { profileFactory } from 'src/factories'; import { accountLoginFactory } from 'src/factories/accountLogin'; import { formatDate } from 'src/utilities/formatDate'; -import { - mockGetAccount, - mockGetAccountLogins, -} from 'support/intercepts/account'; +import { mockGetAccountLogins } from 'support/intercepts/account'; import { mockAppendFeatureFlags, mockGetFeatureFlagClientstream, @@ -24,11 +21,10 @@ describe('Account login history', () => { * - Confirm that the login table displays a mocked successful unrestricted user login. */ it('users can view the login history table', () => { - const mockAccount = accountFactory.build(); const mockProfile = profileFactory.build({ username: 'mock-user', restricted: false, - user_type: null, + user_type: 'default', }); const mockFailedLogin = accountLoginFactory.build({ status: 'failed', @@ -40,7 +36,6 @@ describe('Account login history', () => { restricted: false, }); - mockGetAccount(mockAccount).as('getAccount'); mockGetProfile(mockProfile).as('getProfile'); mockGetAccountLogins([mockFailedLogin, mockSuccessfulLogin]).as( 'getAccountLogins' @@ -54,12 +49,7 @@ describe('Account login history', () => { // Navigate to Account Login History page. cy.visitWithLogin('/account/login-history'); - cy.wait([ - '@getAccount', - '@getClientStream', - '@getFeatureFlags', - '@getProfile', - ]); + cy.wait(['@getClientStream', '@getFeatureFlags', '@getProfile']); // Confirm helper text above table is visible. cy.findByText( @@ -115,14 +105,12 @@ describe('Account login history', () => { * - Confirms that a child user sees a notice instead. */ it('child users cannot view login history', () => { - const mockAccount = accountFactory.build(); const mockProfile = profileFactory.build({ username: 'mock-child-user', restricted: false, user_type: 'child', }); - mockGetAccount(mockAccount).as('getAccount'); mockGetProfile(mockProfile).as('getProfile'); // TODO: Parent/Child - M3-7559 clean up when feature is live in prod and feature flag is removed. @@ -133,12 +121,7 @@ describe('Account login history', () => { // Navigate to Account Login History page. cy.visitWithLogin('/account/login-history'); - cy.wait([ - '@getAccount', - '@getClientStream', - '@getFeatureFlags', - '@getProfile', - ]); + cy.wait(['@getClientStream', '@getFeatureFlags', '@getProfile']); // Confirm helper text above table and table are not visible. cy.findByText( @@ -157,15 +140,13 @@ describe('Account login history', () => { * - Confirms that a restricted user sees a notice instead. */ it('restricted users cannot view login history', () => { - const mockAccount = accountFactory.build(); const mockProfile = profileFactory.build({ username: 'mock-restricted-user', restricted: true, - user_type: null, + user_type: 'default', }); mockGetProfile(mockProfile).as('getProfile'); - mockGetAccount(mockAccount).as('getAccount'); // TODO: Parent/Child - M3-7559 clean up when feature is live in prod and feature flag is removed. mockAppendFeatureFlags({ @@ -175,12 +156,7 @@ describe('Account login history', () => { // Navigate to Account Login History page. cy.visitWithLogin('/account/login-history'); - cy.wait([ - '@getAccount', - '@getClientStream', - '@getFeatureFlags', - '@getProfile', - ]); + cy.wait(['@getClientStream', '@getFeatureFlags', '@getProfile']); // Confirm helper text above table and table are not visible. cy.findByText( diff --git a/packages/manager/cypress/e2e/core/account/change-username.spec.ts b/packages/manager/cypress/e2e/core/account/change-username.spec.ts index faa509ee209..a0374fc698f 100644 --- a/packages/manager/cypress/e2e/core/account/change-username.spec.ts +++ b/packages/manager/cypress/e2e/core/account/change-username.spec.ts @@ -179,7 +179,7 @@ describe('username', () => { it('disables username/email fields for regular restricted user', () => { const mockRegularRestrictedProfile = profileFactory.build({ username: 'regular-restricted-user', - user_type: null, + user_type: 'default', restricted: true, }); diff --git a/packages/manager/cypress/e2e/core/billing/restricted-user-billing.spec.ts b/packages/manager/cypress/e2e/core/billing/restricted-user-billing.spec.ts index 5a17dd052d0..8c6594b7f46 100644 --- a/packages/manager/cypress/e2e/core/billing/restricted-user-billing.spec.ts +++ b/packages/manager/cypress/e2e/core/billing/restricted-user-billing.spec.ts @@ -208,7 +208,7 @@ describe('restricted user billing flows', () => { const mockUser = accountUserFactory.build({ username: mockProfile.username, - user_type: null, + user_type: 'default', restricted: false, }); @@ -248,7 +248,7 @@ describe('restricted user billing flows', () => { const mockUser = accountUserFactory.build({ username: mockProfile.username, restricted: true, - user_type: null, + user_type: 'default', }); const mockGrants = grantsFactory.build({ @@ -304,7 +304,7 @@ describe('restricted user billing flows', () => { const mockUserRegular = accountUserFactory.build({ username: mockProfileRegular.username, - user_type: null, + user_type: 'default', restricted: false, }); diff --git a/packages/manager/src/factories/accountUsers.ts b/packages/manager/src/factories/accountUsers.ts index 838bb124609..664cf75bd8b 100644 --- a/packages/manager/src/factories/accountUsers.ts +++ b/packages/manager/src/factories/accountUsers.ts @@ -8,7 +8,7 @@ export const accountUserFactory = Factory.Sync.makeFactory({ restricted: true, ssh_keys: [], tfa_enabled: false, - user_type: null, + user_type: 'default', username: Factory.each((i) => `user-${i}`), verified_phone_number: null, }); diff --git a/packages/manager/src/factories/profile.ts b/packages/manager/src/factories/profile.ts index 86fd7603653..8bbb442f188 100644 --- a/packages/manager/src/factories/profile.ts +++ b/packages/manager/src/factories/profile.ts @@ -25,7 +25,7 @@ export const profileFactory = Factory.Sync.makeFactory({ timezone: 'Asia/Shanghai', two_factor_auth: false, uid: 9999, - user_type: null, + user_type: 'default', username: 'mock-user', verified_phone_number: '+15555555555', }); diff --git a/packages/manager/src/features/Account/AccountLogins.tsx b/packages/manager/src/features/Account/AccountLogins.tsx index feb4d474209..422ee1974d7 100644 --- a/packages/manager/src/features/Account/AccountLogins.tsx +++ b/packages/manager/src/features/Account/AccountLogins.tsx @@ -166,7 +166,10 @@ const AccountLogins = () => { ) : ( - {getAccessRestrictedText(profile?.user_type ?? null)} + {getAccessRestrictedText( + profile?.user_type, + flags.parentChildAccountAccess + )} ); }; diff --git a/packages/manager/src/features/Account/CloseAccountSetting.tsx b/packages/manager/src/features/Account/CloseAccountSetting.tsx index 5c11d329ab1..6158a5b0970 100644 --- a/packages/manager/src/features/Account/CloseAccountSetting.tsx +++ b/packages/manager/src/features/Account/CloseAccountSetting.tsx @@ -20,7 +20,7 @@ const CloseAccountSetting = () => { // Disable the Close Account button for users with a Parent/Proxy/Child user type. const isCloseAccountDisabled = Boolean( - flags.parentChildAccountAccess && profile?.user_type !== null + flags.parentChildAccountAccess && profile?.user_type !== 'default' ); const closeAccountButtonTooltipText = isCloseAccountDisabled && profile?.user_type === 'child' diff --git a/packages/manager/src/features/Account/utils.ts b/packages/manager/src/features/Account/utils.ts index 3b899ca81da..a1cae8c084d 100644 --- a/packages/manager/src/features/Account/utils.ts +++ b/packages/manager/src/features/Account/utils.ts @@ -35,9 +35,14 @@ export const getRestrictedResourceText = ({ /** * Get an 'access restricted' message based on user type. */ -export const getAccessRestrictedText = (userType: UserType | null) => { +export const getAccessRestrictedText = ( + userType: UserType | undefined, + isParentChildFeatureEnabled?: boolean +) => { return `Access restricted. Please contact your ${ - userType === 'child' ? 'business partner' : 'account administrator' + isParentChildFeatureEnabled && userType === 'child' + ? 'business partner' + : 'account administrator' } to request the necessary permission.`; }; diff --git a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.test.tsx b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.test.tsx index d59543bfcab..a3d46dc5748 100644 --- a/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.test.tsx +++ b/packages/manager/src/features/Profile/APITokens/CreateAPITokenDrawer.test.tsx @@ -101,7 +101,7 @@ describe('Create API Token Drawer', () => { it('Should not show the Child Account Access scope for a non-parent user account with the parent/child feature flag on', () => { queryMocks.useProfile.mockReturnValue({ - data: profileFactory.build({ user_type: null }), + data: profileFactory.build({ user_type: 'default' }), }); const { queryByText } = renderWithTheme( diff --git a/packages/manager/src/features/Profile/APITokens/ViewAPITokenDrawer.test.tsx b/packages/manager/src/features/Profile/APITokens/ViewAPITokenDrawer.test.tsx index 474f0fe9c56..924501a5853 100644 --- a/packages/manager/src/features/Profile/APITokens/ViewAPITokenDrawer.test.tsx +++ b/packages/manager/src/features/Profile/APITokens/ViewAPITokenDrawer.test.tsx @@ -167,10 +167,7 @@ describe('View API Token Drawer', () => { }); describe('Parent/Child: User Roles', () => { - const setupAndRender = ( - userType: UserType | null, - enableFeatureFlag = true - ) => { + const setupAndRender = (userType: UserType, enableFeatureFlag = true) => { queryMocks.useProfile.mockReturnValue({ data: profileFactory.build({ user_type: userType }), }); @@ -181,7 +178,7 @@ describe('View API Token Drawer', () => { }; const testChildScopeNotDisplayed = ( - userType: UserType | null, + userType: UserType, enableFeatureFlag = true ) => { const { queryByText } = setupAndRender(userType, enableFeatureFlag); @@ -193,8 +190,8 @@ describe('View API Token Drawer', () => { testChildScopeNotDisplayed('parent', false); }); - it('should not display the Child Account Access scope for a user account without a parent uer type', () => { - testChildScopeNotDisplayed(null); + it('should not display the Child Account Access scope for a user account without a parent user type', () => { + testChildScopeNotDisplayed('default'); }); it('should not display the Child Account Access scope for "proxy" user type', () => { diff --git a/packages/manager/src/features/TopMenu/UserMenu/UserMenu.test.tsx b/packages/manager/src/features/TopMenu/UserMenu/UserMenu.test.tsx index 4338a1be4d4..e7a59b3f114 100644 --- a/packages/manager/src/features/TopMenu/UserMenu/UserMenu.test.tsx +++ b/packages/manager/src/features/TopMenu/UserMenu/UserMenu.test.tsx @@ -102,7 +102,10 @@ describe('UserMenu', () => { rest.get('*/profile', (req, res, ctx) => { return res( ctx.json( - profileFactory.build({ user_type: null, username: 'regular-user' }) + profileFactory.build({ + user_type: 'default', + username: 'regular-user', + }) ) ); }) diff --git a/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx b/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx index 31550b96c53..2228190c9f6 100644 --- a/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx +++ b/packages/manager/src/features/TopMenu/UserMenu/UserMenu.tsx @@ -71,7 +71,11 @@ export const UserMenu = React.memo(() => { hasParentChildAccountAccess && (isParentUser || isProxyUser); const open = Boolean(anchorEl); const id = open ? 'user-menu-popover' : undefined; - const companyName = (profile?.user_type && account?.company) ?? ''; + const companyName = + (hasParentChildAccountAccess && + profile?.user_type !== 'default' && + account?.company) ?? + ''; const showCompanyName = hasParentChildAccountAccess && companyName; // Used for fetching parent profile and account data by making a request with the parent's token. diff --git a/packages/manager/src/features/Users/UserRow.test.tsx b/packages/manager/src/features/Users/UserRow.test.tsx index 56dca8d729d..b921d65b4dd 100644 --- a/packages/manager/src/features/Users/UserRow.test.tsx +++ b/packages/manager/src/features/Users/UserRow.test.tsx @@ -117,7 +117,7 @@ describe('UserRow', () => { }), // Mock the active profile, which must NOT be of `parent` user type to hide the Child Account Access column. rest.get('*/profile', (req, res, ctx) => { - return res(ctx.json(profileFactory.build({ user_type: null }))); + return res(ctx.json(profileFactory.build({ user_type: 'default' }))); }) );