diff --git a/__mocks__/@auth0/auth0-spa-js.ts b/__mocks__/@auth0/auth0-spa-js.ts index 4ccde3a2..da907f84 100644 --- a/__mocks__/@auth0/auth0-spa-js.ts +++ b/__mocks__/@auth0/auth0-spa-js.ts @@ -4,9 +4,6 @@ import sign from 'jwt-encode'; export const FAKE_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c'; -const fakeIAT = new Date().getTime(); -const fakeEXP = fakeIAT + 30000; - /* * Pre-made fake token data that sets new creation and expiration time for that token * Creation time is `now` and expiration time is `now + 30seconds` @@ -16,8 +13,8 @@ export const fakeTokenData = { iss: 'https://sso.orfium-staging.com/', sub: 'auth0|62da8eaa586d8cd67d1746b6', aud: ['orfium', 'https://orfium-staging.us.auth0.com/userinfo'], - iat: fakeIAT, - exp: fakeEXP, + iat: new Date().getTime(), + exp: new Date().getTime() + 30000, azp: '1eWaFhQJpHS3xMDQRwrZJai3kIrF04eI', scope: 'openid profile email offline_access', org_id: 'org_WYZLEMyTm2xEbnbn', diff --git a/package.json b/package.json index cd83f123..3544e22b 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,6 @@ "main": "dist/cjs/index.js", "module": "dist/esm/index.js", "types": "dist/types/index.d.ts", - "exports": { - "import": "./dist/esm/index.js", - "require": "./dist/cjs/index.js", - "default": "./dist/cjs/index.js" - }, "files": [ "dist" ], diff --git a/src/authentication/Authentication.tsx b/src/authentication/Authentication.tsx index c29725a1..5156ea48 100644 --- a/src/authentication/Authentication.tsx +++ b/src/authentication/Authentication.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react'; import { ErrorBoundary } from 'react-error-boundary'; import { orfiumIdBaseInstance } from '../request'; -import useOrganization, { Organization } from '../store/organizations'; +import useOrganization, { Organization } from '../store/useOrganization'; import { Box, LoadingContent, Wrapper } from './Authentication.style'; import ErrorFallback from './components/ErrorFallback/ErrorFallback'; import { TopBar, TopBarProps } from './components/TopBar/TopBar'; @@ -43,16 +43,10 @@ Authentication.TopBar = TopBar; * This is the main component that is wrapped on the authentication. */ const AuthenticationWrapper: React.FunctionComponent = ({ children }) => { - const { - isLoading, - isAuthenticated, - getAccessTokenSilently, - logout, - loginWithRedirect, - organizations, - selectedOrganization, - } = useAuthentication(); - const { setOrganizations, setSelectedOrganization } = useOrganization(); + const { isLoading, isAuthenticated, getAccessTokenSilently, logout, loginWithRedirect } = + useAuthentication(); + const { organizations, setOrganizations, setSelectedOrganization, selectedOrganization } = + useOrganization(); const [systemLoading, setSystemLoading] = useState(undefined); /** @@ -78,7 +72,7 @@ const AuthenticationWrapper: React.FunctionComponent = ({ children }) => { setOrganizations(data); if (!selectedOrganization?.org_id && data?.length > 0) { - setSelectedOrganization(data[0].org_id); + setSelectedOrganization(data[0]); } // if token doesn't have an organization and the user has available organizations // set continue and set one @@ -114,31 +108,35 @@ const AuthenticationWrapper: React.FunctionComponent = ({ children }) => { if (organizations.length === 0) { return ( - -

There are no organizations to pick.

-
Go back or contact your administrator for more information.
- -
OR
-
- -
+ + +

There are no organizations to pick.

+
Go back or contact your administrator for more information.
+ +
OR
+
+ +
+
); } if (!selectedOrganization) { return ( - -

You dont have access to this Product.

-
Go back or contact your administrator for more information.
- -
OR
-
- -
+ + +

You dont have access to this Product.

+
Go back or contact your administrator for more information.
+ +
OR
+
+ +
+
); } diff --git a/src/authentication/authentication.test.tsx b/src/authentication/authentication.test.tsx index 236670aa..6f2182f3 100644 --- a/src/authentication/authentication.test.tsx +++ b/src/authentication/authentication.test.tsx @@ -1,11 +1,12 @@ import { cleanup, render, waitFor } from '@testing-library/react'; +import React from 'react'; -import { QueryClient, QueryClientProvider } from 'react-query'; import { getNewFakeToken, getTokenSilently, isAuthenticated, loginWithRedirect, + // @ts-ignore } from '../../__mocks__/@auth0/auth0-spa-js'; import { orfiumIdBaseInstance } from '../request'; import MockRequest from '../request/mock'; @@ -15,41 +16,23 @@ const TestComp = () => { }; describe('Authentication: ', () => { + let mock: MockRequest; const apiInstance = orfiumIdBaseInstance.instance; - const mock: MockRequest = new MockRequest(apiInstance); - let queryClient: QueryClient; beforeEach(() => { - queryClient = new QueryClient(); - mock.onGet('/products/').reply(200, [ - { - name: 'string', - organization_usage: 'string', - client_metadata: { - product_code: 'string', - }, - logo_url: 'string', - login_url: 'string', - }, - ]); + mock = new MockRequest(apiInstance); }); afterEach(() => { - // clear all mocks and mocked values jest.clearAllMocks(); cleanup(); - mock.reset(); }); - afterEach(() => {}); - it('renders without crashing', () => { render( - - - - - + + + ); }); @@ -59,11 +42,9 @@ describe('Authentication: ', () => { mock.onGet('/memberships/').reply(200, [{ org_id: 'a' }]); const { findByTestId } = render( - - - - - + + + ); expect(await findByTestId('orfium-auth-loading')).toBeTruthy(); @@ -77,11 +58,9 @@ describe('Authentication: ', () => { mock.onGet('/memberships/').replyOnce(200, []); render( - - - - - + + + ); await waitFor(() => expect(loginWithRedirect).toHaveBeenCalled()); @@ -91,11 +70,9 @@ describe('Authentication: ', () => { getTokenSilently.mockResolvedValue(getNewFakeToken()); isAuthenticated.mockResolvedValue(true); const { findByTestId } = render( - - - - - + + + ); expect(await findByTestId('orfium-auth-loading')).toBeTruthy(); }); @@ -106,11 +83,9 @@ describe('Authentication: ', () => { mock.onGet('/memberships/').replyOnce(200, []); const { findByTestId } = render( - - - - - + + + ); expect(await findByTestId('orfium-auth-loading')).toBeTruthy(); diff --git a/src/authentication/components/TopBar/TopBar.test.tsx b/src/authentication/components/TopBar/TopBar.test.tsx index 532b648a..5552ea96 100644 --- a/src/authentication/components/TopBar/TopBar.test.tsx +++ b/src/authentication/components/TopBar/TopBar.test.tsx @@ -1,15 +1,13 @@ import { ThemeProvider } from '@orfium/ictinus'; -import { cleanup, fireEvent, render, waitFor } from '@testing-library/react'; -import { QueryClient } from 'react-query'; +import { fireEvent, render, waitFor } from '@testing-library/react'; +import React from 'react'; // @ts-ignore -import { orfiumIdBaseInstance } from '../../../request'; -import MockRequest from '../../../request/mock'; -import { Organization } from '../../../store/organizations'; -import { TopBarWithInjectedProps } from './TopBar'; - -const mockOrganizations: Record = { - org_cEVFmcCb0XYz1ZPf: { +import { createAuth0Client as mockedCreateAuth0 } from '../../../../__mocks__/@auth0/auth0-spa-js'; +import { Organization } from '../../../store/useOrganization'; +import { Authentication } from '../../index'; +const mockOrganizations: Organization[] = [ + { org_id: 'org_cEVFmcCb0XYz1ZPf', display_name: 'NBCU', name: 'nbcu', @@ -22,7 +20,7 @@ const mockOrganizations: Record = { logo_url: '', }, }, - org_cEVFmcCb0XYF4Hpp: { + { org_id: 'org_cEVFmcCb0XYF4Hpp', display_name: 'WMG', name: 'wmg', @@ -35,9 +33,7 @@ const mockOrganizations: Record = { logo_url: '', }, }, -}; - -const mockOrganizationsList = ['org_cEVFmcCb0XYz1ZPf', 'org_cEVFmcCb0XYF4Hpp']; +]; const mockedUser = { name: 'Joe Doe', @@ -51,109 +47,55 @@ const mockedUserFn = jest const mockSetSelectedOrganization = jest.fn(); const mockLogout = jest.fn(); -jest.mock('../../../store/organizations', () => +jest.mock('../../../store/useOrganization', () => jest.fn(() => ({ organizations: mockOrganizations, - organizationsList: mockOrganizationsList, setSelectedOrganization: mockSetSelectedOrganization, - selectedOrganization: mockOrganizations[mockOrganizationsList[0]], + selectedOrganization: mockOrganizations[0], })) ); -// jest.mock('../../context', () => ({ -// useAuthentication: () => ({ -// user: mockedUserFn(), -// logout: mockLogout, -// }), -// getAuth0Client: mockedCreateAuth0, -// })); +jest.mock('../../context', () => ({ + useAuthentication: () => ({ + user: mockedUserFn(), + logout: mockLogout, + }), + getAuth0Client: mockedCreateAuth0, +})); describe('TopBar', () => { - const apiInstance = orfiumIdBaseInstance.instance; - const mock: MockRequest = new MockRequest(apiInstance); - let queryClient: QueryClient; - beforeEach(() => { - queryClient = new QueryClient(); - mock.onGet('/products/').reply(200, [ - { - name: 'string', - organization_usage: 'string', - client_metadata: { - product_code: 'string', - }, - logo_url: 'string', - login_url: 'string', - }, - ]); - mock.onGet('/memberships/').reply( - 200, - mockOrganizationsList.map((x) => mockOrganizations[x]) - ); - }); - - afterEach(() => { jest.clearAllMocks(); - cleanup(); - mock.reset(); }); it('Renders TopBar with organization selected', () => { - const selectedOrg = mockOrganizations[mockOrganizationsList[0]]; const { getByText } = render( - mockOrganizations[x])} - switchOrganization={jest.fn()} - logoIcon={} - onMenuIconClick={() => {}} - /> + } onMenuIconClick={() => {}} /> ); - expect(getByText(selectedOrg.display_name)).toBeTruthy(); + expect(getByText(mockOrganizations[0].display_name)).toBeTruthy(); }); it('Change οrganization will trigger on select', async () => { - const selectedOrg = mockOrganizations[mockOrganizationsList[0]]; - const mockedSwitchOrg = jest.fn(); const { getByText, getByTestId } = render( - mockOrganizations[x])} - switchOrganization={mockedSwitchOrg} - logoIcon={} - onMenuIconClick={() => {}} - /> + } onMenuIconClick={() => {}} /> ); - fireEvent.click(getByText(selectedOrg.display_name)); + fireEvent.click(getByText(mockOrganizations[0].display_name)); fireEvent.click(getByTestId('ictinus_list_item_0')); - await waitFor(() => expect(mockedSwitchOrg).toBeCalledTimes(1)); + await waitFor(() => expect(mockSetSelectedOrganization).toBeCalledTimes(1)); }); describe('user data', function () { it('Renders TopBar user from the data given', () => { - const mockedSwitchOrg = jest.fn(); const { getByText } = render( - mockOrganizations[x])} - switchOrganization={mockedSwitchOrg} - logoIcon={} - onMenuIconClick={() => {}} - /> + } onMenuIconClick={() => {}} /> ); @@ -164,18 +106,11 @@ describe('TopBar', () => { }); it('Renders TopBar user from the undefined data', () => { - const mockedSwitchOrg = jest.fn(); + mockedUserFn.mockReturnValue(undefined); + const { getByText } = render( - mockOrganizations[x])} - switchOrganization={mockedSwitchOrg} - logoIcon={} - onMenuIconClick={() => {}} - /> + } onMenuIconClick={() => {}} /> ); @@ -184,19 +119,9 @@ describe('TopBar', () => { }); it('Logout the user when press logout', async () => { - const selectedOrg = mockOrganizations[mockOrganizationsList[0]]; - const mockedSwitchOrg = jest.fn(); const { getByText, getByTestId } = render( - mockOrganizations[x])} - switchOrganization={mockedSwitchOrg} - logoIcon={} - onMenuIconClick={() => {}} - /> + } onMenuIconClick={() => {}} /> ); diff --git a/src/authentication/components/TopBar/TopBar.tsx b/src/authentication/components/TopBar/TopBar.tsx index 21eb7ea2..e0fb571f 100644 --- a/src/authentication/components/TopBar/TopBar.tsx +++ b/src/authentication/components/TopBar/TopBar.tsx @@ -2,38 +2,23 @@ import { Menu, TopNavBar } from '@orfium/ictinus'; import { TopAppBarProps } from '@orfium/ictinus/dist/components/TopAppBar/TopAppBar.types'; import React, { memo } from 'react'; -import { Organization } from '../../../store/organizations'; -import { useAuthentication } from '../../context'; -import { User } from '../../types'; +import useOrganization from '../../../store/useOrganization'; +import { getAuth0Client, useAuthentication } from '../../context'; export type TopBarProps = { logoIcon: JSX.Element; userMenu?: never; } & Omit; -type InjectedProps = { - user: User | undefined; - logout: () => void; - switchOrganization: (x: string) => void; - organizations: Organization[]; - selectedOrganization: Organization | null; -}; - /* * The component to represent all the information that is coming from the SSO * Based on Ictinus component */ -export const TopBarWithInjectedProps: React.FC = memo( - ({ - logoIcon, - onMenuIconClick, - additionalTools, - user, - logout, - switchOrganization, - organizations, - selectedOrganization, - }) => { +export const TopBar: React.FC = memo( + ({ logoIcon, onMenuIconClick, additionalTools }) => { + const { user, logout } = useAuthentication(); + const { organizations, setSelectedOrganization, selectedOrganization } = useOrganization(); + const userConfig = { items: ['Logout'], userName: `${user?.name}`, @@ -67,7 +52,14 @@ export const TopBarWithInjectedProps: React.FC = me onSelect={async (option: string) => { const foundOrg = organizations.find((org) => org.display_name === option); if (foundOrg) { - switchOrganization(foundOrg.org_id); + const client = await getAuth0Client(); + await client.logout({ openUrl: false }); + await client.loginWithRedirect({ + authorizationParams: { + organization: foundOrg.org_id, + }, + }); + setSelectedOrganization(foundOrg); } }} buttonText={selectedOrganization?.display_name} @@ -81,22 +73,4 @@ export const TopBarWithInjectedProps: React.FC = me ); } ); -TopBarWithInjectedProps.displayName = 'TopBarWithInjectedProps'; - -export function TopBar({ logoIcon, onMenuIconClick, additionalTools }: TopBarProps) { - const { user, logout, switchOrganization, organizations, selectedOrganization } = - useAuthentication(); - - return ( - - ); -} +TopBar.displayName = 'TopBar'; diff --git a/src/authentication/context.test.tsx b/src/authentication/context.test.tsx index 7518d732..9b9ded35 100644 --- a/src/authentication/context.test.tsx +++ b/src/authentication/context.test.tsx @@ -1,6 +1,6 @@ import { act, render, screen, waitFor } from '@testing-library/react'; import jwtDecode from 'jwt-decode'; -import { useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; // Auth0 custom error simulator. This extends a regular Error to match Auth0 Error. class CustomError extends Error { @@ -13,7 +13,6 @@ class CustomError extends Error { import { ErrorBoundary, useErrorHandler } from 'react-error-boundary'; -import { QueryClient, QueryClientProvider } from 'react-query'; import { createAuth0Client as mockedCreateAuth0, fakeTokenData, @@ -24,11 +23,10 @@ import { handleRedirectCallback as mockedHandleRedirectCallback, isAuthenticated, loginWithRedirect, + // @ts-ignore } from '../../__mocks__/@auth0/auth0-spa-js'; -import { orfiumIdBaseInstance } from '../request'; -import MockRequest from '../request/mock'; -import useOrganization from '../store/organizations'; -import useRequestToken from '../store/requestToken'; +import useOrganization from '../store/useOrganization'; +import useRequestToken from '../store/useRequestToken'; import { AuthenticationProvider, client, @@ -84,30 +82,9 @@ const TestingComponent = () => { }; describe('Context', () => { - const apiInstance = orfiumIdBaseInstance.instance; - const mock: MockRequest = new MockRequest(apiInstance); - let queryClient: QueryClient; - beforeEach(() => { - queryClient = new QueryClient(); - mock.onGet('/products/').reply(200, [ - { - name: 'string', - organization_usage: 'string', - client_metadata: { - product_code: 'string', - }, - logo_url: 'string', - login_url: 'string', - }, - ]); - mockedGetTokenSilently.mockReset(); - }); - - afterEach(() => { // clear all mocks and mocked values jest.clearAllMocks(); - mock.reset(); mockedGetTokenSilently.mockReset(); getUser.mockReset(); loginWithRedirect.mockReset(); @@ -136,11 +113,9 @@ describe('Context', () => { window.history.pushState({}, '', '?code=test'); render( - - - <> - - + + <> + ); await waitFor(() => expect(mockedHandleRedirectCallback).toBeCalledTimes(1)); @@ -157,11 +132,9 @@ describe('Context', () => {

{error.message}

} > - - - - - + + +
); @@ -181,11 +154,9 @@ describe('Context', () => {

{error.message}

} > - - - - - + + +
); @@ -217,16 +188,15 @@ describe('Context', () => { // implement testing data setToken(testToken); setOrganizations(organizationList); - setSelectedOrganization(organizationList[0].org_id); + setSelectedOrganization(organizationList[0]); await logoutAuth(); const token = useRequestToken.getState().token; - const { organizations, organizationsList, selectedOrganization } = useOrganization.getState(); + const { organizations, selectedOrganization } = useOrganization.getState(); expect(token).toBe(undefined); - expect(organizations).toStrictEqual(null); - expect(organizationsList).toStrictEqual(null); - expect(selectedOrganization).toBe(null); + expect(organizations).toStrictEqual([]); + expect(selectedOrganization).toBe(undefined); }); }); @@ -242,25 +212,21 @@ describe('Context', () => { test('with cached results', async () => { const NEW_FAKE_EXPIRED_TOKEN = getNewFakeToken(); const setToken = useRequestToken.getState().setToken; - const setOrganizations = useOrganization.getState().setOrganizations; const setSelectedOrganization = useOrganization.getState().setSelectedOrganization; setToken(NEW_FAKE_EXPIRED_TOKEN); - setOrganizations([ - { - org_id: 'org_WYZLEMyTm2xEbnbn', - display_name: 'test', - name: 'test', - can_administrate: true, - metadata: { - type: 'test', - product_codes: 'test', - }, - branding: { - logo_url: 'test', - }, + setSelectedOrganization({ + org_id: 'org_WYZLEMyTm2xEbnbn', + display_name: 'test', + name: 'test', + can_administrate: true, + metadata: { + type: 'test', + product_codes: 'test', }, - ]); - setSelectedOrganization('org_WYZLEMyTm2xEbnbn'); + branding: { + logo_url: 'test', + }, + }); const { token, decodedToken } = await getTokenSilently(); @@ -288,11 +254,9 @@ describe('Context', () => { }); const { findByText, getByTestId } = render( - - - - - + + + ); await waitFor(() => expect(findByText('John Doe')).toBeTruthy()); @@ -310,11 +274,9 @@ describe('Context', () => {

{error.message}

} > - - - - - + + +
); @@ -334,11 +296,9 @@ describe('Context', () => {

{error.message}

} > - - - - - + + +
); @@ -364,11 +324,9 @@ describe('Context', () => { await act(async () => { render( - - - - - + + + ); }); @@ -414,7 +372,7 @@ describe('Context', () => { // implement testing data setOrganizations(organizationList); - setSelectedOrganization(organizationList[1].org_id); + setSelectedOrganization(organizationList[1]); Object.defineProperty(window, 'location', { value: new URL(`http://localhost:3000/?error=access_denied&error_description=whatever`), writable: true, @@ -424,11 +382,9 @@ describe('Context', () => { await act(async () => { render( - - - - - + + + ); }); diff --git a/src/authentication/context.tsx b/src/authentication/context.tsx index 5af63000..45ae8fa2 100644 --- a/src/authentication/context.tsx +++ b/src/authentication/context.tsx @@ -6,12 +6,11 @@ import { RedirectLoginOptions, } from '@auth0/auth0-spa-js'; import jwt_decode from 'jwt-decode'; -import React, { createContext, useCallback, useEffect, useMemo, useState } from 'react'; +import React, { createContext, useEffect, useState } from 'react'; import { useErrorHandler } from 'react-error-boundary'; -import { useOrfiumProducts } from '../hooks/useOrfiumProducts'; -import useOrganization from '../store/organizations'; -import useRequestToken from '../store/requestToken'; +import useOrganization from '../store/useOrganization'; +import useRequestToken from '../store/useRequestToken'; import { config } from './config'; import { AuthenticationContextProps } from './types'; @@ -40,10 +39,6 @@ export const defaultContextValues: AuthenticationContextProps = { isAuthenticated: false, isLoading: false, user: undefined, - orfiumProducts: null, - organizations: [], - selectedOrganization: null, - switchOrganization: (__x) => {}, loginWithRedirect: () => Promise.resolve(), logout: () => Promise.resolve('logged out'), getAccessTokenSilently: () => Promise.resolve({ token: '', decodedToken: {} }), @@ -148,27 +143,15 @@ const AuthenticationProvider: React.FC = ({ children }) => { const [user, setUser] = useState>(); const [auth0Client, setAuth0Client] = useState(); const [isLoading, setIsLoading] = useState(true); - // const [products, setProducts] = useState(null); - // handleError is referentially stable, so it's safe to use as a dep in dep array - // https://github.com/bvaughn/react-error-boundary/blob/v3.1.4/src/index.tsx#L165C10-L165C18 const handleError = useErrorHandler(); const params = new URLSearchParams(window.location.search); const selectedOrganization = useOrganization((state) => state.selectedOrganization); const setSelectedOrganization = useOrganization((state) => state.setSelectedOrganization); - const organizationsDict = useOrganization((state) => state.organizations); - const organizationsList = useOrganization((state) => state.organizationsList); + const organizations = useOrganization((state) => state.organizations); const organization = params.get('organization') || selectedOrganization?.org_id; const invitation = params.get('invitation'); - const organizations = useMemo(() => { - if (organizationsDict && organizationsList) { - return organizationsList.map((x) => organizationsDict[x]); - } - - return []; - }, [organizationsDict, organizationsList]); - useEffect(() => { (async () => { try { @@ -212,39 +195,33 @@ const AuthenticationProvider: React.FC = ({ children }) => { })(); }, []); - const loginWithRedirect = useCallback( - async (o: RedirectLoginOptions) => { - try { - const client = await getAuth0Client(); - await client.loginWithRedirect(o); - } catch (error) { - return handleError(error); - } - }, - [handleError] - ); - - const getAccessTokenSilently = useCallback( - async (opts?: GetTokenSilentlyOptions) => { - try { - const result = await getTokenSilently(opts); + const loginWithRedirect = async (o: RedirectLoginOptions) => { + try { + const client = await getAuth0Client(); + await client.loginWithRedirect(o); + } catch (error) { + return handleError(error); + } + }; - return result; - } catch (error: any) { - if (error?.error === 'login_required' || error?.error === 'consent_required') { - return loginWithRedirect({ - authorizationParams: { - organization: organization || undefined, - invitation: invitation || undefined, - }, - }); - } + const getAccessTokenSilently = async (opts?: GetTokenSilentlyOptions) => { + try { + const result = await getTokenSilently(opts); - handleError(error); + return result; + } catch (error: any) { + if (error?.error === 'login_required' || error?.error === 'consent_required') { + return loginWithRedirect({ + authorizationParams: { + organization: organization || undefined, + invitation: invitation || undefined, + }, + }); } - }, - [handleError, invitation, loginWithRedirect, organization] - ); + + handleError(error); + } + }; useEffect(() => { const searchParams = new URL(window.location.href).searchParams; @@ -253,7 +230,7 @@ const AuthenticationProvider: React.FC = ({ children }) => { if (error === 'access_denied') { const org = organizations[0]; - setSelectedOrganization(org.org_id); + setSelectedOrganization(org); loginWithRedirect({ authorizationParams: { organization: org?.org_id || undefined, @@ -271,22 +248,6 @@ const AuthenticationProvider: React.FC = ({ children }) => { } }, [auth0Client, isLoading, isAuthenticated]); - const switchOrganization = useCallback( - async function (orgID) { - const client = await getAuth0Client(); - await client.logout({ openUrl: false }); - await client.loginWithRedirect({ - authorizationParams: { - organization: orgID, - }, - }); - setSelectedOrganization(orgID); - }, - [setSelectedOrganization] - ); - - const { data: orfiumProducts = null } = useOrfiumProducts(selectedOrganization?.org_id); - return ( { loginWithRedirect, logout: logoutAuth, getAccessTokenSilently: (o?: GetTokenSilentlyOptions) => getAccessTokenSilently(o), - orfiumProducts, - organizations, - selectedOrganization, - switchOrganization, user, }} > diff --git a/src/authentication/types.ts b/src/authentication/types.ts index edc77c64..267c6bd0 100644 --- a/src/authentication/types.ts +++ b/src/authentication/types.ts @@ -1,10 +1,8 @@ -import type { +import { Auth0ClientOptions, GetTokenSilentlyOptions, RedirectLoginOptions, } from '@auth0/auth0-spa-js'; -import type { Product } from '../hooks/useOrfiumProducts/types'; -import { Organization } from '../store/organizations'; export type DecodedTokenResponse = { iss?: string; @@ -53,11 +51,7 @@ export type AuthenticationContextProps = { token: string; decodedToken: DecodedTokenResponse | Record; } | void>; - orfiumProducts: Product[] | null; user: User | undefined; - organizations: Organization[]; - selectedOrganization: Organization | null; - switchOrganization: (organisation: Organization['org_id']) => void; }; export type AuthenticationProviderProps = { overrides?: Auth0ClientOptions }; diff --git a/src/hooks/useOrfiumProducts/index.ts b/src/hooks/useOrfiumProducts/index.ts deleted file mode 100644 index 9709c40a..00000000 --- a/src/hooks/useOrfiumProducts/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { useQuery } from 'react-query'; -import { orfiumIdBaseInstance } from '../../request'; -import { Product } from './types'; - -export function useOrfiumProducts(orgId: string | undefined) { - return useQuery( - ['products', orgId], - () => { - return orfiumIdBaseInstance - .createRequest({ - method: 'get', - url: '/products/', - }) - .request(); - }, - { - enabled: !!orgId, - } - ); -} diff --git a/src/hooks/useOrfiumProducts/types.ts b/src/hooks/useOrfiumProducts/types.ts deleted file mode 100644 index 6879cc7f..00000000 --- a/src/hooks/useOrfiumProducts/types.ts +++ /dev/null @@ -1,11 +0,0 @@ -export type ClientMetadata = { - product_code: string; -}; - -export type Product = { - name: string; - organization_usage: string; - client_metadata: ClientMetadata; - logo_url: string; - login_url: string; -}; diff --git a/src/index.ts b/src/index.ts index 6cba7270..9e80afb8 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { default as useOrganizationModule, Organization } from './store/organizations'; +import { default as useOrganizationModule, Organization } from './store/useOrganization'; /* * Eliminate any other information from the useOrganization zustand state coming out @@ -11,8 +11,8 @@ const useOrganization = () => selectedOrganization, })); -export * from './authentication'; export * from './request'; export * from './routing'; +export * from './authentication'; export type { Organization }; export { useOrganization }; diff --git a/src/request/createAPIInstance.ts b/src/request/createAPIInstance.ts index 4728a5e1..456fde66 100644 --- a/src/request/createAPIInstance.ts +++ b/src/request/createAPIInstance.ts @@ -1,7 +1,7 @@ import axios, { AxiosInstance, CancelTokenSource } from 'axios'; import { getTokenSilently, logoutAuth } from '../authentication/context'; -import useRequestToken from '../store/requestToken'; +import useRequestToken from '../store/useRequestToken'; import { deleteToken, request, RequestProps, setToken, tokenFormat } from './request'; export { default as MockRequest } from './mock'; diff --git a/src/store/organizations.ts b/src/store/organizations.ts deleted file mode 100644 index b466793a..00000000 --- a/src/store/organizations.ts +++ /dev/null @@ -1,71 +0,0 @@ -import create from 'zustand'; -import { persist } from 'zustand/middleware'; - -export type Organization = { - org_id: string; - display_name: string; - name: string; - can_administrate: boolean; - metadata: { - type: string; - product_codes: string; - }; - branding: { - logo_url: string; - }; -}; - -type Store = { - // list of organizations that fetched and stored - organizations: Record | null; - organizationsList: Organization['org_id'][] | null; - // the selected organization for the current session - selectedOrganization: Organization | null; - setOrganizations: (organizations: Organization[]) => void; - setSelectedOrganization: (orgID: Organization['org_id']) => void; - reset: () => void; -}; - -const initialState = { - organizations: null, - organizationsList: null, - selectedOrganization: null, -}; -const useOrganization = create( - persist( - (set, get) => ({ - ...initialState, - setOrganizations: (organizations: Organization[]) => - set(() => { - return organizations.reduce( - (acc, org) => { - acc.organizations[org.org_id] = org; - acc.organizationsList.push(org.org_id); - - return acc; - }, - { organizations: {}, organizationsList: [] } as { - organizations: NonNullable; - organizationsList: NonNullable; - } - ); - }), - setSelectedOrganization: (organization: Organization['org_id']) => { - const orgs = get().organizations; - if (orgs === null) { - set({ selectedOrganization: null }); - } else { - set({ selectedOrganization: orgs[organization] }); - } - }, - reset: () => { - set({ ...initialState }); - }, - }), - { - name: 'selectedOrganization', - } - ) -); - -export default useOrganization; diff --git a/src/store/useOrganization.ts b/src/store/useOrganization.ts new file mode 100644 index 00000000..7da2fdb0 --- /dev/null +++ b/src/store/useOrganization.ts @@ -0,0 +1,50 @@ +import create from 'zustand'; +import { persist } from 'zustand/middleware'; + +export type Organization = { + org_id: string; + display_name: string; + name: string; + can_administrate: boolean; + metadata: { + type: string; + product_codes: string; + }; + branding: { + logo_url: string; + }; +}; + +type Store = { + // list of organizations that fetched and stored + organizations: Organization[]; + // the selected organization for the current session + selectedOrganization: Organization | undefined; + setOrganizations: (organizations: Organization[]) => void; + setSelectedOrganization: (organizations: Organization) => void; + reset: () => void; +}; + +const initialState = { + organizations: [], + selectedOrganization: undefined, +}; +const useOrganization = create( + persist( + (set, __get) => ({ + ...initialState, + setOrganizations: (organizations: Organization[]) => set(() => ({ organizations })), + setSelectedOrganization: (organization: Organization) => { + set({ selectedOrganization: organization }); + }, + reset: () => { + set({ ...initialState }); + }, + }), + { + name: 'selectedOrganization', + } + ) +); + +export default useOrganization; diff --git a/src/store/requestToken.ts b/src/store/useRequestToken.ts similarity index 100% rename from src/store/requestToken.ts rename to src/store/useRequestToken.ts