core-api: migrate ApiContext to use version bridge
Co-authored-by: Juan Lulkin <jmaiz@spotify.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -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<VersionedValue<{ 1: ApiHolder }>>
|
||||
>('api-context');
|
||||
|
||||
function useMockApiV1<T>(apiRef: ApiRef<T>): T {
|
||||
const impl = useContext(ApiContext)?.atVersion(1)?.get(apiRef);
|
||||
if (!impl) {
|
||||
throw new Error('no impl');
|
||||
}
|
||||
return impl;
|
||||
}
|
||||
|
||||
type Api = () => string;
|
||||
const apiRef = createApiRef<Api>({ id: 'x', description: '' });
|
||||
const registry = ApiRegistry.from([[apiRef, () => 'hello']]);
|
||||
|
||||
const MyHookConsumerV1 = () => {
|
||||
const api = useMockApiV1(apiRef);
|
||||
return <p>hook message: {api()}</p>;
|
||||
};
|
||||
|
||||
it('should provide apis', () => {
|
||||
const renderedHook = render(
|
||||
<ApiProvider apis={registry}>
|
||||
<MyHookConsumerV1 />
|
||||
</ApiProvider>,
|
||||
);
|
||||
renderedHook.getByText('hook message: hello');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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<ApiHolder | undefined>(undefined);
|
||||
type ApiContextType = VersionedValue<{ 1: ApiHolder }> | undefined;
|
||||
const ApiContext = createContext<ApiContextType>(undefined);
|
||||
|
||||
setGlobalSingleton('api-context', ApiContext);
|
||||
|
||||
export const ApiProvider = ({
|
||||
apis,
|
||||
children,
|
||||
}: PropsWithChildren<ApiProviderProps>) => {
|
||||
const parentHolder = useContext(Context);
|
||||
const parentHolder = useContext(ApiContext)?.atVersion(1);
|
||||
const holder = parentHolder ? new ApiAggregator(apis, parentHolder) : apis;
|
||||
|
||||
return <Context.Provider value={holder} children={children} />;
|
||||
return (
|
||||
<ApiContext.Provider
|
||||
value={createVersionedValueMap({ 1: holder })}
|
||||
children={children}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
ApiProvider.propTypes = {
|
||||
@@ -53,12 +67,19 @@ ApiProvider.propTypes = {
|
||||
};
|
||||
|
||||
export function useApiHolder(): ApiHolder {
|
||||
const apiHolder = useContext(Context);
|
||||
const versionedHolder = useContext(
|
||||
getGlobalSingleton<Context<ApiContextType>>('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<T>(apis: TypesToApiRefs<T>) {
|
||||
WrappedComponent: React.ComponentType<P>,
|
||||
) {
|
||||
const Hoc = (props: PropsWithChildren<Omit<P, keyof T>>) => {
|
||||
const apiHolder = useContext(Context);
|
||||
|
||||
if (!apiHolder) {
|
||||
throw new Error(missingHolderMessage);
|
||||
}
|
||||
const apiHolder = useApiHolder();
|
||||
|
||||
const impls = {} as T;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user