diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 414ca2afe1..d9380a3245 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -31,6 +31,7 @@ "dependencies": { "@backstage/config": "^0.1.9", "@backstage/theme": "^0.2.9", + "@backstage/version-bridge": "^0.1.0", "@material-ui/core": "^4.12.2", "@types/react": "*", "history": "^5.0.0", diff --git a/packages/core-plugin-api/src/apis/system/useApi.test.tsx b/packages/core-plugin-api/src/apis/system/useApi.test.tsx index b23e44b815..6f473d8f59 100644 --- a/packages/core-plugin-api/src/apis/system/useApi.test.tsx +++ b/packages/core-plugin-api/src/apis/system/useApi.test.tsx @@ -15,7 +15,7 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { createVersionedContextForTesting } from '../../lib/versionedValues'; +import { createVersionedContextForTesting } from '@backstage/version-bridge'; import { createApiRef } from './ApiRef'; import { useApi } from './useApi'; diff --git a/packages/core-plugin-api/src/apis/system/useApi.tsx b/packages/core-plugin-api/src/apis/system/useApi.tsx index eae6b98654..10fe1df4a7 100644 --- a/packages/core-plugin-api/src/apis/system/useApi.tsx +++ b/packages/core-plugin-api/src/apis/system/useApi.tsx @@ -16,7 +16,7 @@ import React, { PropsWithChildren } from 'react'; import { ApiRef, ApiHolder, TypesToApiRefs } from './types'; -import { useVersionedContext } from '../../lib/versionedValues'; +import { useVersionedContext } from '@backstage/version-bridge'; export function useApiHolder(): ApiHolder { const versionedHolder = useVersionedContext<{ 1: ApiHolder }>('api-context'); diff --git a/packages/core-plugin-api/src/app/useApp.test.tsx b/packages/core-plugin-api/src/app/useApp.test.tsx index 12b315ae87..f2e330e847 100644 --- a/packages/core-plugin-api/src/app/useApp.test.tsx +++ b/packages/core-plugin-api/src/app/useApp.test.tsx @@ -15,7 +15,7 @@ */ import { renderHook } from '@testing-library/react-hooks'; -import { createVersionedContextForTesting } from '../lib/versionedValues'; +import { createVersionedContextForTesting } from '@backstage/version-bridge'; import { useApp } from './useApp'; describe('v1 consumer', () => { diff --git a/packages/core-plugin-api/src/app/useApp.tsx b/packages/core-plugin-api/src/app/useApp.tsx index aa323db4e2..d4615eb532 100644 --- a/packages/core-plugin-api/src/app/useApp.tsx +++ b/packages/core-plugin-api/src/app/useApp.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { useVersionedContext } from '../lib/versionedValues'; +import { useVersionedContext } from '@backstage/version-bridge'; import { AppContext as AppContextV1 } from './types'; export const useApp = (): AppContextV1 => { diff --git a/packages/core-plugin-api/src/extensions/componentData.tsx b/packages/core-plugin-api/src/extensions/componentData.tsx index d28d17d8ca..97a0b74e5b 100644 --- a/packages/core-plugin-api/src/extensions/componentData.tsx +++ b/packages/core-plugin-api/src/extensions/componentData.tsx @@ -15,7 +15,7 @@ */ import { ComponentType, ReactNode } from 'react'; -import { getOrCreateGlobalSingleton } from '../lib/globalObject'; +import { getOrCreateGlobalSingleton } from '@backstage/version-bridge'; type DataContainer = { map: Map; diff --git a/packages/core-plugin-api/src/lib/globalObject.test.ts b/packages/core-plugin-api/src/lib/globalObject.test.ts deleted file mode 100644 index 13634f1f08..0000000000 --- a/packages/core-plugin-api/src/lib/globalObject.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { getGlobalSingleton, getOrCreateGlobalSingleton } from './globalObject'; - -const anyGlobal = global as any; - -describe('getGlobalSingleton', () => { - beforeEach(() => { - delete anyGlobal['__@backstage/my-thing__']; - }); - - it('should return an existing value', () => { - const myThing = {}; - const myOtherThing = {}; - - anyGlobal['__@backstage/my-thing__'] = myThing; - expect(getGlobalSingleton('my-thing')).toBe(myThing); - expect(getGlobalSingleton('my-thing')).toBe(myThing); - anyGlobal['__@backstage/my-thing__'] = myOtherThing; - expect(getGlobalSingleton('my-thing')).toBe(myOtherThing); - }); - - it('should throw if the value is not set', () => { - expect(() => getGlobalSingleton('my-thing')).toThrow( - 'Global my-thing is not set', - ); - }); -}); - -describe('getOrCreateGlobalSingleton', () => { - beforeEach(() => { - delete anyGlobal['__@backstage/my-thing__']; - }); - - it('should return an existing value', () => { - const myThing = {}; - anyGlobal['__@backstage/my-thing__'] = myThing; - - expect(getOrCreateGlobalSingleton('my-thing', () => ({}))).toBe(myThing); - expect(getOrCreateGlobalSingleton('my-thing', () => ({}))).toBe(myThing); - }); - - it('should should create a new value', () => { - const myNewThing = {}; - - expect(anyGlobal['__@backstage/my-thing__']).toBe(undefined); - expect(getOrCreateGlobalSingleton('my-thing', () => myNewThing)).toBe( - myNewThing, - ); - expect(anyGlobal['__@backstage/my-thing__']).toBe(myNewThing); - expect(getOrCreateGlobalSingleton('my-thing', () => ({}))).toBe(myNewThing); - }); -}); diff --git a/packages/core-plugin-api/src/lib/globalObject.ts b/packages/core-plugin-api/src/lib/globalObject.ts deleted file mode 100644 index 7a400148fb..0000000000 --- a/packages/core-plugin-api/src/lib/globalObject.ts +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// https://github.com/zloirock/core-js/issues/86#issuecomment-115759028 -function getGlobalObject() { - if (typeof window !== 'undefined' && window.Math === Math) { - return window; - } - if (typeof self !== 'undefined' && self.Math === Math) { - return self; - } - // eslint-disable-next-line no-new-func - return Function('return this')(); -} - -const globalObject = getGlobalObject(); - -const makeKey = (id: string) => `__@backstage/${id}__`; - -/** - * Used to provide a global singleton value, failing if it is already set. - */ -export function setGlobalSingleton(id: string, value: unknown): void { - const key = makeKey(id); - if (key in globalObject) { - throw new Error(`Global ${id} is already set`); - } - globalObject[key] = value; -} - -/** - * Used to access a global singleton value, failing if it is not already set. - */ -export function getGlobalSingleton(id: string): T { - const key = makeKey(id); - if (!(key in globalObject)) { - throw new Error(`Global ${id} is not set`); - } - - return globalObject[key]; -} - -/** - * Serializes access to a global singleton value, with the first caller creating the value. - */ -export function getOrCreateGlobalSingleton( - id: string, - supplier: () => T, -): T { - const key = makeKey(id); - - let value = globalObject[key]; - if (value) { - return value; - } - - value = supplier(); - globalObject[key] = value; - return value; -} diff --git a/packages/core-plugin-api/src/lib/versionedValues.ts b/packages/core-plugin-api/src/lib/versionedValues.ts deleted file mode 100644 index 7686842ecd..0000000000 --- a/packages/core-plugin-api/src/lib/versionedValues.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createContext, useContext, Context } from 'react'; -import { getGlobalSingleton, setGlobalSingleton } from './globalObject'; - -/** - * The versioned value interface is a container for a set of values that - * can be looked up by version. It is intended to be used as a container - * for values that can be versioned independently of package versions. - */ -export type VersionedValue = { - atVersion( - version: Version, - ): Versions[Version] | undefined; -}; - -/** - * Creates a container for a map of versioned values that implements VersionedValue. - */ -export function createVersionedValueMap< - Versions extends { [version: number]: any }, ->(versions: Versions): VersionedValue { - Object.freeze(versions); - return { - atVersion(version) { - return versions[version]; - }, - }; -} - -export function useVersionedContext< - Versions extends { [version in number]: any }, ->(key: string): VersionedValue { - const versionedValue = useContext( - getGlobalSingleton>>(key), - ); - if (!versionedValue) { - throw new Error(`No provider available for ${key} context`); - } - return versionedValue; -} - -export function createVersionedContextForTesting(key: string) { - return { - set(versions: { [version in number]: unknown }) { - setGlobalSingleton(key, createContext(createVersionedValueMap(versions))); - }, - reset() { - delete (globalThis as any)[`__@backstage/${key}__`]; - }, - }; -} diff --git a/packages/core-plugin-api/src/routing/types.ts b/packages/core-plugin-api/src/routing/types.ts index 5afcc72265..86d469c2cc 100644 --- a/packages/core-plugin-api/src/routing/types.ts +++ b/packages/core-plugin-api/src/routing/types.ts @@ -15,7 +15,7 @@ */ import { OldIconComponent } from '../icons/types'; -import { getOrCreateGlobalSingleton } from '../lib/globalObject'; +import { getOrCreateGlobalSingleton } from '@backstage/version-bridge'; export type AnyParams = { [param in string]: string } | undefined; export type ParamKeys = keyof Params extends never diff --git a/packages/core-plugin-api/src/routing/useRouteRef.test.tsx b/packages/core-plugin-api/src/routing/useRouteRef.test.tsx index ace2d1a58a..62f6d6da42 100644 --- a/packages/core-plugin-api/src/routing/useRouteRef.test.tsx +++ b/packages/core-plugin-api/src/routing/useRouteRef.test.tsx @@ -17,7 +17,7 @@ import { renderHook } from '@testing-library/react-hooks'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { createVersionedContextForTesting } from '../lib/versionedValues'; +import { createVersionedContextForTesting } from '@backstage/version-bridge'; import { useRouteRef } from './useRouteRef'; import { createRouteRef } from './RouteRef'; diff --git a/packages/core-plugin-api/src/routing/useRouteRef.tsx b/packages/core-plugin-api/src/routing/useRouteRef.tsx index ed261489a4..56c5baff8e 100644 --- a/packages/core-plugin-api/src/routing/useRouteRef.tsx +++ b/packages/core-plugin-api/src/routing/useRouteRef.tsx @@ -16,7 +16,7 @@ import { useMemo } from 'react'; import { matchRoutes, useLocation } from 'react-router-dom'; -import { useVersionedContext } from '../lib/versionedValues'; +import { useVersionedContext } from '@backstage/version-bridge'; import { AnyParams, ExternalRouteRef,