diff --git a/packages/core-api/src/apis/system/ApiProvider.test.tsx b/packages/core-api/src/apis/system/ApiProvider.test.tsx index 71269697c2..4c0b3f5683 100644 --- a/packages/core-api/src/apis/system/ApiProvider.test.tsx +++ b/packages/core-api/src/apis/system/ApiProvider.test.tsx @@ -14,12 +14,15 @@ * limitations under the License. */ -import React from 'react'; +import React, { Context, useContext } from 'react'; import { ApiProvider, useApi, withApis } from './ApiProvider'; import { createApiRef } from './ApiRef'; import { ApiRegistry } from './ApiRegistry'; import { render } from '@testing-library/react'; import { withLogCollector } from '@backstage/test-utils-core'; +import { getGlobalSingleton } from '../../lib/globalObject'; +import { ApiHolder, ApiRef } from './types'; +import { VersionedValue } from '../../lib/versionedValues'; describe('ApiProvider', () => { type Api = () => string; @@ -175,3 +178,35 @@ describe('ApiProvider', () => { ]); }); }); + +describe('v1 consumer', () => { + const ApiContext = getGlobalSingleton< + Context> + >('api-context'); + + function useMockApiV1(apiRef: ApiRef): T { + const impl = useContext(ApiContext)?.atVersion(1)?.get(apiRef); + if (!impl) { + throw new Error('no impl'); + } + return impl; + } + + type Api = () => string; + const apiRef = createApiRef({ id: 'x', description: '' }); + const registry = ApiRegistry.from([[apiRef, () => 'hello']]); + + const MyHookConsumerV1 = () => { + const api = useMockApiV1(apiRef); + return

hook message: {api()}

; + }; + + it('should provide apis', () => { + const renderedHook = render( + + + , + ); + renderedHook.getByText('hook message: hello'); + }); +}); diff --git a/packages/core-api/src/apis/system/ApiProvider.tsx b/packages/core-api/src/apis/system/ApiProvider.tsx index d41730cb59..6740746ce2 100644 --- a/packages/core-api/src/apis/system/ApiProvider.tsx +++ b/packages/core-api/src/apis/system/ApiProvider.tsx @@ -19,10 +19,16 @@ import React, { useContext, ReactNode, PropsWithChildren, + Context, } from 'react'; import PropTypes from 'prop-types'; import { ApiRef, ApiHolder, TypesToApiRefs } from './types'; import { ApiAggregator } from './ApiAggregator'; +import { getGlobalSingleton, setGlobalSingleton } from '../../lib/globalObject'; +import { + VersionedValue, + createVersionedValueMap, +} from '../../lib/versionedValues'; const missingHolderMessage = 'No ApiProvider available in react context. ' + @@ -35,16 +41,24 @@ type ApiProviderProps = { children: ReactNode; }; -const Context = createContext(undefined); +type ApiContextType = VersionedValue<{ 1: ApiHolder }> | undefined; +const ApiContext = createContext(undefined); + +setGlobalSingleton('api-context', ApiContext); export const ApiProvider = ({ apis, children, }: PropsWithChildren) => { - const parentHolder = useContext(Context); + const parentHolder = useContext(ApiContext)?.atVersion(1); const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis; - return ; + return ( + + ); }; ApiProvider.propTypes = { @@ -53,12 +67,19 @@ ApiProvider.propTypes = { }; export function useApiHolder(): ApiHolder { - const apiHolder = useContext(Context); + const versionedHolder = useContext( + getGlobalSingleton>('api-context'), + ); - if (!apiHolder) { + if (!versionedHolder) { throw new Error(missingHolderMessage); } + const apiHolder = versionedHolder.atVersion(1); + if (!apiHolder) { + throw new Error('ApiContext v1 not available'); + } + return apiHolder; } @@ -77,11 +98,7 @@ export function withApis(apis: TypesToApiRefs) { WrappedComponent: React.ComponentType

, ) { const Hoc = (props: PropsWithChildren>) => { - const apiHolder = useContext(Context); - - if (!apiHolder) { - throw new Error(missingHolderMessage); - } + const apiHolder = useApiHolder(); const impls = {} as T;