{core,frontend}-plugin-api: inverse dependency
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
+2
-2
@@ -37,12 +37,12 @@ import ObservableImpl from 'zen-observable';
|
||||
import {
|
||||
toInternalTranslationResource,
|
||||
InternalTranslationResourceLoader,
|
||||
} from '../../../../../core-plugin-api/src/translation/TranslationResource';
|
||||
} from '../../../../../frontend-plugin-api/src/translation/TranslationResource';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import {
|
||||
toInternalTranslationRef,
|
||||
InternalTranslationRef,
|
||||
} from '../../../../../core-plugin-api/src/translation/TranslationRef';
|
||||
} from '../../../../../frontend-plugin-api/src/translation/TranslationRef';
|
||||
import { Observable } from '@backstage/types';
|
||||
import { DEFAULT_LANGUAGE } from '../AppLanguageApi/AppLanguageSelector';
|
||||
import { createElement, Fragment, ReactNode, isValidElement } from 'react';
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"dependencies": {
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"@backstage/version-bridge": "workspace:^",
|
||||
"history": "^5.0.0",
|
||||
@@ -59,7 +60,6 @@
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^",
|
||||
"@backstage/core-app-api": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/test-utils": "workspace:^",
|
||||
"@testing-library/dom": "^10.0.0",
|
||||
"@testing-library/jest-dom": "^6.0.0",
|
||||
|
||||
@@ -14,5 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './translation';
|
||||
export * from './apis/alpha';
|
||||
// Translation exports
|
||||
export {
|
||||
type TranslationMessages,
|
||||
type TranslationMessagesOptions,
|
||||
createTranslationMessages,
|
||||
type TranslationResource,
|
||||
type TranslationResourceOptions,
|
||||
createTranslationResource,
|
||||
type TranslationRef,
|
||||
type TranslationRefOptions,
|
||||
createTranslationRef,
|
||||
useTranslationRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
// API definition exports
|
||||
export {
|
||||
appLanguageApiRef,
|
||||
type AppLanguageApi,
|
||||
translationApiRef,
|
||||
type TranslationApi,
|
||||
type TranslationFunction,
|
||||
type TranslationSnapshot,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -16,9 +16,12 @@
|
||||
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useAnalytics } from './useAnalytics';
|
||||
import { useApi } from '../apis';
|
||||
import { useApi } from '@backstage/frontend-plugin-api';
|
||||
|
||||
jest.mock('../apis');
|
||||
jest.mock('@backstage/frontend-plugin-api', () => ({
|
||||
...jest.requireActual('@backstage/frontend-plugin-api'),
|
||||
useApi: jest.fn(),
|
||||
}));
|
||||
|
||||
const mocked = (f: Function) => f as jest.Mock;
|
||||
|
||||
|
||||
@@ -15,12 +15,8 @@
|
||||
*/
|
||||
|
||||
import { useAnalyticsContext } from './AnalyticsContext';
|
||||
import {
|
||||
analyticsApiRef,
|
||||
AnalyticsTracker,
|
||||
AnalyticsApi,
|
||||
useApi,
|
||||
} from '../apis';
|
||||
import { analyticsApiRef, AnalyticsTracker, AnalyticsApi } from '../apis';
|
||||
import { useApi } from '@backstage/frontend-plugin-api';
|
||||
import { useRef } from 'react';
|
||||
import { Tracker } from './Tracker';
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
export * from './definitions/alpha';
|
||||
@@ -14,43 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef, ApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* Message handled by the {@link AlertApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AlertMessage = {
|
||||
message: string;
|
||||
// Severity will default to success since that is what material ui defaults the value to.
|
||||
severity?: 'success' | 'info' | 'warning' | 'error';
|
||||
display?: 'permanent' | 'transient';
|
||||
};
|
||||
|
||||
/**
|
||||
* The alert API is used to report alerts to the app, and display them to the user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AlertApi = {
|
||||
/**
|
||||
* Post an alert for handling by the application.
|
||||
*/
|
||||
post(alert: AlertMessage): void;
|
||||
|
||||
/**
|
||||
* Observe alerts posted by other parts of the application.
|
||||
*/
|
||||
alert$(): Observable<AlertMessage>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link AlertApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const alertApiRef: ApiRef<AlertApi> = createApiRef({
|
||||
id: 'core.alert',
|
||||
});
|
||||
export {
|
||||
type AlertApi,
|
||||
type AlertMessage,
|
||||
alertApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -122,7 +122,7 @@ export type AnalyticsApi = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link AnalyticsApi}.
|
||||
* The `ApiRef` of {@link AnalyticsApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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 { ApiRef, createApiRef } from '@backstage/core-plugin-api';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/** @alpha */
|
||||
export type AppLanguageApi = {
|
||||
getAvailableLanguages(): { languages: string[] };
|
||||
|
||||
setLanguage(language?: string): void;
|
||||
|
||||
getLanguage(): { language: string };
|
||||
|
||||
language$(): Observable<{ language: string }>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
export const appLanguageApiRef: ApiRef<AppLanguageApi> = createApiRef({
|
||||
id: 'core.applanguage',
|
||||
});
|
||||
@@ -14,74 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ReactNode } from 'react';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* Describes a theme provided by the app.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AppTheme = {
|
||||
/**
|
||||
* ID used to remember theme selections.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Title of the theme
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Theme variant
|
||||
*/
|
||||
variant: 'light' | 'dark';
|
||||
|
||||
/**
|
||||
* An Icon for the theme mode setting.
|
||||
*/
|
||||
icon?: React.ReactElement;
|
||||
|
||||
Provider(props: { children: ReactNode }): JSX.Element | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* The AppThemeApi gives access to the current app theme, and allows switching
|
||||
* to other options that have been registered as a part of the App.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AppThemeApi = {
|
||||
/**
|
||||
* Get a list of available themes.
|
||||
*/
|
||||
getInstalledThemes(): AppTheme[];
|
||||
|
||||
/**
|
||||
* Observe the currently selected theme. A value of undefined means no specific theme has been selected.
|
||||
*/
|
||||
activeThemeId$(): Observable<string | undefined>;
|
||||
|
||||
/**
|
||||
* Get the current theme ID. Returns undefined if no specific theme is selected.
|
||||
*/
|
||||
getActiveThemeId(): string | undefined;
|
||||
|
||||
/**
|
||||
* Set a specific theme to use in the app, overriding the default theme selection.
|
||||
*
|
||||
* Clear the selection by passing in undefined.
|
||||
*/
|
||||
setActiveThemeId(themeId?: string): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link AppThemeApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const appThemeApiRef: ApiRef<AppThemeApi> = createApiRef({
|
||||
id: 'core.apptheme',
|
||||
});
|
||||
export {
|
||||
type AppTheme,
|
||||
type AppThemeApi,
|
||||
appThemeApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -13,22 +13,5 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
/**
|
||||
* The Config API is used to provide a mechanism to access the
|
||||
* runtime configuration of the system.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ConfigApi = Config;
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link ConfigApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const configApiRef: ApiRef<ConfigApi> = createApiRef({
|
||||
id: 'core.config',
|
||||
});
|
||||
export { type ConfigApi, configApiRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -13,43 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* The discovery API is used to provide a mechanism for plugins to
|
||||
* discover the endpoint to use to talk to their backend counterpart.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The purpose of the discovery API is to allow for many different deployment
|
||||
* setups and routing methods through a central configuration, instead
|
||||
* of letting each individual plugin manage that configuration.
|
||||
*
|
||||
* Implementations of the discovery API can be a simple as a URL pattern
|
||||
* using the pluginId, but could also have overrides for individual plugins,
|
||||
* or query a separate discovery service.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DiscoveryApi = {
|
||||
/**
|
||||
* Returns the HTTP base backend URL for a given plugin, without a trailing slash.
|
||||
*
|
||||
* This method must always be called just before making a request, as opposed to
|
||||
* fetching the URL when constructing an API client. That is to ensure that more
|
||||
* flexible routing patterns can be supported.
|
||||
*
|
||||
* For example, asking for the URL for `auth` may return something
|
||||
* like `https://backstage.example.com/api/auth`
|
||||
*/
|
||||
getBaseUrl(pluginId: string): Promise<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link DiscoveryApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const discoveryApiRef: ApiRef<DiscoveryApi> = createApiRef({
|
||||
id: 'core.discovery',
|
||||
});
|
||||
export {
|
||||
type DiscoveryApi,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,78 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* Mirrors the JavaScript Error class, for the purpose of
|
||||
* providing documentation and optional fields.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ErrorApiError = {
|
||||
name: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides additional information about an error that was posted to the application.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ErrorApiErrorContext = {
|
||||
/**
|
||||
* If set to true, this error should not be displayed to the user.
|
||||
*
|
||||
* Hidden errors are typically not displayed in the UI, but the ErrorApi
|
||||
* implementation may still report them to error tracking services
|
||||
* or other utilities that care about all errors.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
hidden?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The error API is used to report errors to the app, and display them to the user.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Plugins can use this API as a method of displaying errors to the user, but also
|
||||
* to report errors for collection by error reporting services.
|
||||
*
|
||||
* If an error can be displayed inline, e.g. as feedback in a form, that should be
|
||||
* preferred over relying on this API to display the error. The main use of this API
|
||||
* for displaying errors should be for asynchronous errors, such as a failing background process.
|
||||
*
|
||||
* Even if an error is displayed inline, it should still be reported through this API
|
||||
* if it would be useful to collect or log it for debugging purposes, but with
|
||||
* the hidden flag set. For example, an error arising from form field validation
|
||||
* should probably not be reported, while a failed REST call would be useful to report.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ErrorApi = {
|
||||
/**
|
||||
* Post an error for handling by the application.
|
||||
*/
|
||||
post(error: ErrorApiError, context?: ErrorApiErrorContext): void;
|
||||
|
||||
/**
|
||||
* Observe errors posted by other parts of the application.
|
||||
*/
|
||||
error$(): Observable<{
|
||||
error: ErrorApiError;
|
||||
context?: ErrorApiErrorContext;
|
||||
}>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link ErrorApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const errorApiRef: ApiRef<ErrorApi> = createApiRef({
|
||||
id: 'core.error',
|
||||
});
|
||||
export {
|
||||
type ErrorApiError,
|
||||
type ErrorApiErrorContext,
|
||||
type ErrorApi,
|
||||
errorApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -13,114 +13,11 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* We want to maintain the same information as an enum, so we disable the redeclaration warning */
|
||||
/* eslint-disable @typescript-eslint/no-redeclare */
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* Feature flag descriptor.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlag = {
|
||||
name: string;
|
||||
pluginId: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum representing the state of a feature flag (inactive/active).
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const FeatureFlagState = {
|
||||
/**
|
||||
* Feature flag inactive (disabled).
|
||||
*/
|
||||
None: 0,
|
||||
/**
|
||||
* Feature flag active (enabled).
|
||||
*/
|
||||
Active: 1,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlagState =
|
||||
(typeof FeatureFlagState)[keyof typeof FeatureFlagState];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export namespace FeatureFlagState {
|
||||
export type None = typeof FeatureFlagState.None;
|
||||
export type Active = typeof FeatureFlagState.Active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options to use when saving feature flags.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlagsSaveOptions = {
|
||||
/**
|
||||
* The new feature flag states to save.
|
||||
*/
|
||||
states: Record<string, FeatureFlagState>;
|
||||
|
||||
/**
|
||||
* Whether the saves states should be merged into the existing ones, or replace them.
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
merge?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The feature flags API is used to toggle functionality to users across plugins and Backstage.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Plugins can use this API to register feature flags that they have available
|
||||
* for users to enable/disable, and this API will centralize the current user's
|
||||
* state of which feature flags they would like to enable.
|
||||
*
|
||||
* This is ideal for Backstage plugins, as well as your own App, to trial incomplete
|
||||
* or unstable upcoming features. Although there will be a common interface for users
|
||||
* to enable and disable feature flags, this API acts as another way to enable/disable.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface FeatureFlagsApi {
|
||||
/**
|
||||
* Registers a new feature flag. Once a feature flag has been registered it
|
||||
* can be toggled by users, and read back to enable or disable features.
|
||||
*/
|
||||
registerFlag(flag: FeatureFlag): void;
|
||||
|
||||
/**
|
||||
* Get a list of all registered flags.
|
||||
*/
|
||||
getRegisteredFlags(): FeatureFlag[];
|
||||
|
||||
/**
|
||||
* Whether the feature flag with the given name is currently activated for the user.
|
||||
*/
|
||||
isActive(name: string): boolean;
|
||||
|
||||
/**
|
||||
* Save the user's choice of feature flag states.
|
||||
*/
|
||||
save(options: FeatureFlagsSaveOptions): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link FeatureFlagsApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const featureFlagsApiRef: ApiRef<FeatureFlagsApi> = createApiRef({
|
||||
id: 'core.featureflags',
|
||||
});
|
||||
export {
|
||||
type FeatureFlag,
|
||||
type FeatureFlagsApi,
|
||||
type FeatureFlagsSaveOptions,
|
||||
FeatureFlagState,
|
||||
featureFlagsApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,38 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* A wrapper for the fetch API, that has additional behaviors such as the
|
||||
* ability to automatically inject auth information where necessary.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FetchApi = {
|
||||
/**
|
||||
* The `fetch` implementation.
|
||||
*/
|
||||
fetch: typeof fetch;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link FetchApi}.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is a wrapper for the fetch API, that has additional behaviors such as
|
||||
* the ability to automatically inject auth information where necessary.
|
||||
*
|
||||
* Note that the default behavior of this API (unless overridden by your org),
|
||||
* is to require that the user is already signed in so that it has auth
|
||||
* information to inject. Therefore, using the default implementation of this
|
||||
* utility API e.g. on the `SignInPage` or similar, would cause issues. In
|
||||
* special circumstances like those, you can use the regular system `fetch`
|
||||
* instead.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const fetchApiRef: ApiRef<FetchApi> = createApiRef({
|
||||
id: 'core.fetch',
|
||||
});
|
||||
export { type FetchApi, fetchApiRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -13,44 +13,8 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { BackstageUserIdentity, ProfileInfo } from './auth';
|
||||
|
||||
/**
|
||||
* The Identity API used to identify and get information about the signed in user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type IdentityApi = {
|
||||
/**
|
||||
* The profile of the signed in user.
|
||||
*/
|
||||
getProfileInfo(): Promise<ProfileInfo>;
|
||||
|
||||
/**
|
||||
* User identity information within Backstage.
|
||||
*/
|
||||
getBackstageIdentity(): Promise<BackstageUserIdentity>;
|
||||
|
||||
/**
|
||||
* Provides credentials in the form of a token which proves the identity of the signed in user.
|
||||
*
|
||||
* The token will be undefined if the signed in user does not have a verified
|
||||
* identity, such as a demo user or mocked user for e2e tests.
|
||||
*/
|
||||
getCredentials(): Promise<{ token?: string }>;
|
||||
|
||||
/**
|
||||
* Sign out the current user
|
||||
*/
|
||||
signOut(): Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link IdentityApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const identityApiRef: ApiRef<IdentityApi> = createApiRef({
|
||||
id: 'core.identity',
|
||||
});
|
||||
export {
|
||||
type IdentityApi,
|
||||
identityApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,118 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Observable } from '@backstage/types';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { AuthProviderInfo } from './auth';
|
||||
|
||||
/**
|
||||
* Describes how to handle auth requests. Both how to show them to the user, and what to do when
|
||||
* the user accesses the auth request.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthRequesterOptions<TOAuthResponse> = {
|
||||
/**
|
||||
* Information about the auth provider, which will be forwarded to auth requests.
|
||||
*/
|
||||
provider: AuthProviderInfo;
|
||||
|
||||
/**
|
||||
* Implementation of the auth flow, which will be called synchronously when
|
||||
* trigger() is called on an auth requests.
|
||||
*/
|
||||
onAuthRequest(scopes: Set<string>): Promise<TOAuthResponse>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function used to trigger new auth requests for a set of scopes.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The returned promise will resolve to the same value returned by the onAuthRequest in the
|
||||
* {@link OAuthRequesterOptions}. Or rejected, if the request is rejected.
|
||||
*
|
||||
* This function can be called multiple times before the promise resolves. All calls
|
||||
* will be merged into one request, and the scopes forwarded to the onAuthRequest will be the
|
||||
* union of all requested scopes.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthRequester<TAuthResponse> = (
|
||||
scopes: Set<string>,
|
||||
) => Promise<TAuthResponse>;
|
||||
|
||||
/**
|
||||
* An pending auth request for a single auth provider. The request will remain in this pending
|
||||
* state until either reject() or trigger() is called.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Any new requests for the same provider are merged into the existing pending request, meaning
|
||||
* there will only ever be a single pending request for a given provider.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PendingOAuthRequest = {
|
||||
/**
|
||||
* Information about the auth provider, as given in the AuthRequesterOptions
|
||||
*/
|
||||
provider: AuthProviderInfo;
|
||||
|
||||
/**
|
||||
* Rejects the request, causing all pending AuthRequester calls to fail with "RejectedError".
|
||||
*/
|
||||
reject(): void;
|
||||
|
||||
/**
|
||||
* Trigger the auth request to continue the auth flow, by for example showing a popup.
|
||||
*
|
||||
* Synchronously calls onAuthRequest with all scope currently in the request.
|
||||
*/
|
||||
trigger(): Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides helpers for implemented OAuth login flows within Backstage.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthRequestApi = {
|
||||
/**
|
||||
* A utility for showing login popups or similar things, and merging together multiple requests for
|
||||
* different scopes into one request that includes all scopes.
|
||||
*
|
||||
* The passed in options provide information about the login provider, and how to handle auth requests.
|
||||
*
|
||||
* The returned AuthRequester function is used to request login with new scopes. These requests
|
||||
* are merged together and forwarded to the auth handler, as soon as a consumer of auth requests
|
||||
* triggers an auth flow.
|
||||
*
|
||||
* See AuthRequesterOptions, AuthRequester, and handleAuthRequests for more info.
|
||||
*/
|
||||
createAuthRequester<OAuthResponse>(
|
||||
options: OAuthRequesterOptions<OAuthResponse>,
|
||||
): OAuthRequester<OAuthResponse>;
|
||||
|
||||
/**
|
||||
* Observers pending auth requests. The returned observable will emit all
|
||||
* current active auth request, at most one for each created auth requester.
|
||||
*
|
||||
* Each request has its own info about the login provider, forwarded from the auth requester options.
|
||||
*
|
||||
* Depending on user interaction, the request should either be rejected, or used to trigger the auth handler.
|
||||
* If the request is rejected, all pending AuthRequester calls will fail with a "RejectedError".
|
||||
* If a auth is triggered, and the auth handler resolves successfully, then all currently pending
|
||||
* AuthRequester calls will resolve to the value returned by the onAuthRequest call.
|
||||
*/
|
||||
authRequest$(): Observable<PendingOAuthRequest[]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link OAuthRequestApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const oauthRequestApiRef: ApiRef<OAuthRequestApi> = createApiRef({
|
||||
id: 'core.oauthrequest',
|
||||
});
|
||||
export {
|
||||
type OAuthRequesterOptions,
|
||||
type OAuthRequester,
|
||||
type PendingOAuthRequest,
|
||||
type OAuthRequestApi,
|
||||
oauthRequestApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,97 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { JsonValue, Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* A snapshot in time of the current known value of a storage key.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StorageValueSnapshot<TValue extends JsonValue> =
|
||||
| {
|
||||
key: string;
|
||||
presence: 'unknown' | 'absent';
|
||||
value?: undefined;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
presence: 'present';
|
||||
value: TValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides a key-value persistence API.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface StorageApi {
|
||||
/**
|
||||
* Create a bucket to store data in.
|
||||
*
|
||||
* @param name - Namespace for the storage to be stored under,
|
||||
* will inherit previous namespaces too
|
||||
*/
|
||||
forBucket(name: string): StorageApi;
|
||||
|
||||
/**
|
||||
* Remove persistent data.
|
||||
*
|
||||
* @param key - Unique key associated with the data.
|
||||
*/
|
||||
remove(key: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Save persistent data, and emit messages to anyone that is using
|
||||
* {@link StorageApi.observe$} for this key.
|
||||
*
|
||||
* @param key - Unique key associated with the data.
|
||||
* @param data - The data to be stored under the key.
|
||||
*/
|
||||
set<T extends JsonValue>(key: string, data: T): Promise<void>;
|
||||
|
||||
/**
|
||||
* Observe the value over time for a particular key in the current bucket.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The observable will only emit values when the value changes in the underlying
|
||||
* storage, although multiple values with the same shape may be emitted in a row.
|
||||
*
|
||||
* If a {@link StorageApi.snapshot} of a key is retrieved and the presence is
|
||||
* `'unknown'`, then you are guaranteed to receive a snapshot with a known
|
||||
* presence, as long as you observe the key within the same tick.
|
||||
*
|
||||
* Since the emitted values are shared across all subscribers, it is important
|
||||
* not to mutate the returned values. The values may be frozen as a precaution.
|
||||
*
|
||||
* @param key - Unique key associated with the data
|
||||
*/
|
||||
observe$<T extends JsonValue>(
|
||||
key: string,
|
||||
): Observable<StorageValueSnapshot<T>>;
|
||||
|
||||
/**
|
||||
* Returns an immediate snapshot value for the given key, if possible.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Combine with {@link StorageApi.observe$} to get notified of value changes.
|
||||
*
|
||||
* Note that this method is synchronous, and some underlying storages may be
|
||||
* unable to retrieve a value using this method - the result may or may not
|
||||
* consistently have a presence of 'unknown'. Use {@link StorageApi.observe$}
|
||||
* to be sure to receive an actual value eventually.
|
||||
*/
|
||||
snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link StorageApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const storageApiRef: ApiRef<StorageApi> = createApiRef({
|
||||
id: 'core.storage',
|
||||
});
|
||||
export {
|
||||
type StorageValueSnapshot,
|
||||
type StorageApi,
|
||||
storageApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
export {
|
||||
translationApiRef,
|
||||
type TranslationApi,
|
||||
type TranslationFunction,
|
||||
type TranslationSnapshot,
|
||||
} from './TranslationApi';
|
||||
export { appLanguageApiRef, type AppLanguageApi } from './AppLanguageApi';
|
||||
@@ -16,493 +16,28 @@
|
||||
/* We want to maintain the same information as an enum, so we disable the redeclaration warning */
|
||||
/* eslint-disable @typescript-eslint/no-redeclare */
|
||||
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { IconComponent } from '../../icons/types';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* This file contains declarations for common interfaces of auth-related APIs.
|
||||
* The declarations should be used to signal which type of authentication and
|
||||
* authorization methods each separate auth provider supports.
|
||||
*
|
||||
* For example, a Google OAuth provider that supports OAuth 2 and OpenID Connect,
|
||||
* would be declared as follows:
|
||||
*
|
||||
* const googleAuthApiRef = createApiRef<OAuthApi & OpenIDConnectApi>({ ... })
|
||||
*/
|
||||
|
||||
/**
|
||||
* Information about the auth provider.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This information is used both to connect the correct auth provider in the backend, as
|
||||
* well as displaying the provider to the user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuthProviderInfo = {
|
||||
/**
|
||||
* The ID of the auth provider. This should match with ID of the provider in the `@backstage/auth-backend`.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Title for the auth provider, for example "GitHub"
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Icon for the auth provider.
|
||||
*/
|
||||
icon: IconComponent;
|
||||
|
||||
/**
|
||||
* Optional user friendly messaage to display for the auth provider.
|
||||
*/
|
||||
message?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* An array of scopes, or a scope string formatted according to the
|
||||
* auth provider, which is typically a space separated list.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* See the documentation for each auth provider for the list of scopes
|
||||
* supported by each provider.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthScope = string | string[];
|
||||
|
||||
/**
|
||||
* Configuration of an authentication request.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuthRequestOptions = {
|
||||
/**
|
||||
* If this is set to true, the user will not be prompted to log in,
|
||||
* and an empty response will be returned if there is no existing session.
|
||||
*
|
||||
* This can be used to perform a check whether the user is logged in, or if you don't
|
||||
* want to force a user to be logged in, but provide functionality if they already are.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
optional?: boolean;
|
||||
|
||||
/**
|
||||
* If this is set to true, the request will bypass the regular oauth login modal
|
||||
* and open the login popup directly.
|
||||
*
|
||||
* The method must be called synchronously from a user action for this to work in all browsers.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
instantPopup?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to OAuth 2 credentials. It lets you request access tokens,
|
||||
* which can be used to act on behalf of the user when talking to APIs.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthApi = {
|
||||
/**
|
||||
* Requests an OAuth 2 Access Token, optionally with a set of scopes. The access token allows
|
||||
* you to make requests on behalf of the user, and the copes may grant you broader access, depending
|
||||
* on the auth provider.
|
||||
*
|
||||
* Each auth provider has separate handling of scope, so you need to look at the documentation
|
||||
* for each one to know what scope you need to request.
|
||||
*
|
||||
* This method is cheap and should be called each time an access token is used. Do not for example
|
||||
* store the access token in React component state, as that could cause the token to expire. Instead
|
||||
* fetch a new access token for each request.
|
||||
*
|
||||
* Be sure to include all required scopes when requesting an access token. When testing your implementation
|
||||
* it is best to log out the Backstage session and then visit your plugin page directly, as
|
||||
* you might already have some required scopes in your existing session. Not requesting the correct
|
||||
* scopes can lead to 403 or other authorization errors, which can be tricky to debug.
|
||||
*
|
||||
* If the user has not yet granted access to the provider and the set of requested scopes, the user
|
||||
* will be prompted to log in. The returned promise will not resolve until the user has
|
||||
* successfully logged in. The returned promise can be rejected, but only if the user rejects the login request.
|
||||
*/
|
||||
getAccessToken(
|
||||
scope?: OAuthScope,
|
||||
options?: AuthRequestOptions,
|
||||
): Promise<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to OpenID Connect credentials. It lets you request ID tokens,
|
||||
* which can be passed to backend services to prove the user's identity.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OpenIdConnectApi = {
|
||||
/**
|
||||
* Requests an OpenID Connect ID Token.
|
||||
*
|
||||
* This method is cheap and should be called each time an ID token is used. Do not for example
|
||||
* store the id token in React component state, as that could cause the token to expire. Instead
|
||||
* fetch a new id token for each request.
|
||||
*
|
||||
* If the user has not yet logged in to Google inside Backstage, the user will be prompted
|
||||
* to log in. The returned promise will not resolve until the user has successfully logged in.
|
||||
* The returned promise can be rejected, but only if the user rejects the login request.
|
||||
*/
|
||||
getIdToken(options?: AuthRequestOptions): Promise<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to profile information of the user from an auth provider.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ProfileInfoApi = {
|
||||
/**
|
||||
* Get profile information for the user as supplied by this auth provider.
|
||||
*
|
||||
* If the optional flag is not set, a session is guaranteed to be returned, while if
|
||||
* the optional flag is set, the session may be undefined. See {@link AuthRequestOptions} for more details.
|
||||
*/
|
||||
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to the user's identity within Backstage.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* An auth provider that implements this interface can be used to sign-in to backstage. It is
|
||||
* not intended to be used directly from a plugin, but instead serves as a connection between
|
||||
* this authentication method and the app's {@link IdentityApi}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type BackstageIdentityApi = {
|
||||
/**
|
||||
* Get the user's identity within Backstage. This should normally not be called directly,
|
||||
* use the {@link IdentityApi} instead.
|
||||
*
|
||||
* If the optional flag is not set, a session is guaranteed to be returned, while if
|
||||
* the optional flag is set, the session may be undefined. See {@link AuthRequestOptions} for more details.
|
||||
*/
|
||||
getBackstageIdentity(
|
||||
options?: AuthRequestOptions,
|
||||
): Promise<BackstageIdentityResponse | undefined>;
|
||||
};
|
||||
|
||||
/**
|
||||
* User identity information within Backstage.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type BackstageUserIdentity = {
|
||||
/**
|
||||
* The type of identity that this structure represents. In the frontend app
|
||||
* this will currently always be 'user'.
|
||||
*/
|
||||
type: 'user';
|
||||
|
||||
/**
|
||||
* The entityRef of the user in the catalog.
|
||||
* For example User:default/sandra
|
||||
*/
|
||||
userEntityRef: string;
|
||||
|
||||
/**
|
||||
* The user and group entities that the user claims ownership through
|
||||
*/
|
||||
ownershipEntityRefs: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Token and Identity response, with the users claims in the Identity.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type BackstageIdentityResponse = {
|
||||
/**
|
||||
* The token used to authenticate the user within Backstage.
|
||||
*/
|
||||
token: string;
|
||||
|
||||
/**
|
||||
* The time at which the token expires. If not set, it can be assumed that the token does not expire.
|
||||
*/
|
||||
expiresAt?: Date;
|
||||
|
||||
/**
|
||||
* Identity information derived from the token.
|
||||
*/
|
||||
identity: BackstageUserIdentity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Profile information of the user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ProfileInfo = {
|
||||
/**
|
||||
* Email ID.
|
||||
*/
|
||||
email?: string;
|
||||
|
||||
/**
|
||||
* Display name that can be presented to the user.
|
||||
*/
|
||||
displayName?: string;
|
||||
|
||||
/**
|
||||
* URL to an avatar image of the user.
|
||||
*/
|
||||
picture?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Session state values passed to subscribers of the SessionApi.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SessionState = {
|
||||
/**
|
||||
* User signed in.
|
||||
*/
|
||||
SignedIn: 'SignedIn',
|
||||
/**
|
||||
* User not signed in.
|
||||
*/
|
||||
SignedOut: 'SignedOut',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type SessionState = (typeof SessionState)[keyof typeof SessionState];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export namespace SessionState {
|
||||
export type SignedIn = typeof SessionState.SignedIn;
|
||||
export type SignedOut = typeof SessionState.SignedOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SessionApi provides basic controls for any auth provider that is tied to a persistent session.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type SessionApi = {
|
||||
/**
|
||||
* Sign in with a minimum set of permissions.
|
||||
*/
|
||||
signIn(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sign out from the current session. This will reload the page.
|
||||
*/
|
||||
signOut(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Observe the current state of the auth session. Emits the current state on subscription.
|
||||
*/
|
||||
sessionState$(): Observable<SessionState>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides authentication towards Google APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developers.google.com/identity/protocols/googlescopes} for a full list of supported scopes.
|
||||
*
|
||||
* Note that the ID token payload is only guaranteed to contain the user's numerical Google ID,
|
||||
* email and expiration information. Do not rely on any other fields, as they might not be present.
|
||||
*/
|
||||
export const googleAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.google',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards GitHub APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const githubAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.github',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Okta APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const oktaAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.okta',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards GitLab APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const gitlabAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.gitlab',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Microsoft APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* For more info and a full list of supported scopes, see:
|
||||
* - {@link https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent}
|
||||
* - {@link https://docs.microsoft.com/en-us/graph/permissions-reference}
|
||||
*/
|
||||
export const microsoftAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.microsoft',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards OneLogin APIs.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const oneloginAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.onelogin',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Bitbucket APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const bitbucketAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.bitbucket',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Bitbucket Server APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://confluence.atlassian.com/bitbucketserver/bitbucket-oauth-2-0-provider-api-1108483661.html#BitbucketOAuth2.0providerAPI-scopes}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const bitbucketServerAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.bitbucket-server',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Atlassian APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developer.atlassian.com/cloud/jira/platform/scopes-for-connect-and-oauth-2-3LO-apps/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const atlassianAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.atlassian',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards VMware Cloud APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* For more info about VMware Cloud identity and access management:
|
||||
* - {@link https://docs.vmware.com/en/VMware-Cloud-services/services/Using-VMware-Cloud-Services/GUID-53D39337-D93A-4B84-BD18-DDF43C21479A.html}
|
||||
*/
|
||||
export const vmwareCloudAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.vmware-cloud',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards OpenShift APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/authentication_and_authorization/configuring-oauth-clients}
|
||||
* on how to configure the OAuth clients and
|
||||
* {@link https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html-single/authentication_and_authorization/index#tokens-scoping-about_configuring-internal-oauth}
|
||||
* for available scopes.
|
||||
*/
|
||||
export const openshiftAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.openshift',
|
||||
});
|
||||
export {
|
||||
type AuthProviderInfo,
|
||||
type OAuthScope,
|
||||
type AuthRequestOptions,
|
||||
type OAuthApi,
|
||||
type OpenIdConnectApi,
|
||||
type ProfileInfoApi,
|
||||
type BackstageIdentityApi,
|
||||
type BackstageUserIdentity,
|
||||
type BackstageIdentityResponse,
|
||||
type ProfileInfo,
|
||||
SessionState,
|
||||
type SessionApi,
|
||||
googleAuthApiRef,
|
||||
githubAuthApiRef,
|
||||
oktaAuthApiRef,
|
||||
gitlabAuthApiRef,
|
||||
microsoftAuthApiRef,
|
||||
oneloginAuthApiRef,
|
||||
bitbucketAuthApiRef,
|
||||
bitbucketServerAuthApiRef,
|
||||
atlassianAuthApiRef,
|
||||
vmwareCloudAuthApiRef,
|
||||
openshiftAuthApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,51 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { ApiRef } from './types';
|
||||
|
||||
/**
|
||||
* API reference configuration - holds an ID of the referenced API.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiRefConfig = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
class ApiRefImpl<T> implements ApiRef<T> {
|
||||
constructor(private readonly config: ApiRefConfig) {
|
||||
const valid = config.id
|
||||
.split('.')
|
||||
.flatMap(part => part.split('-'))
|
||||
.every(part => part.match(/^[a-z][a-z0-9]*$/));
|
||||
if (!valid) {
|
||||
throw new Error(
|
||||
`API id must only contain period separated lowercase alphanum tokens with dashes, got '${config.id}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.config.id;
|
||||
}
|
||||
|
||||
// Utility for getting type of an api, using `typeof apiRef.T`
|
||||
get T(): T {
|
||||
throw new Error(`tried to read ApiRef.T of ${this}`);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `apiRef{${this.config.id}}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reference to an API.
|
||||
*
|
||||
* @param config - The descriptor of the API to reference.
|
||||
* @returns An API reference.
|
||||
* @public
|
||||
*/
|
||||
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T> {
|
||||
return new ApiRefImpl<T>(config);
|
||||
}
|
||||
export { createApiRef } from '@backstage/frontend-plugin-api';
|
||||
export type { ApiRefConfig } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,61 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, ApiFactory, TypesToApiRefs } from './types';
|
||||
|
||||
/**
|
||||
* Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed
|
||||
* to another function.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This function doesn't actually do anything, it's only used to infer types.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function createApiFactory<
|
||||
Api,
|
||||
Impl extends Api,
|
||||
Deps extends { [name in string]: unknown },
|
||||
>(factory: ApiFactory<Api, Impl, Deps>): ApiFactory<Api, Impl, Deps>;
|
||||
/**
|
||||
* Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed
|
||||
* to another function.
|
||||
*
|
||||
* @param api - Ref of the API that will be produced by the factory.
|
||||
* @param instance - Implementation of the API to use.
|
||||
* @public
|
||||
*/
|
||||
export function createApiFactory<Api, Impl extends Api>(
|
||||
api: ApiRef<Api>,
|
||||
instance: Impl,
|
||||
): ApiFactory<Api, Impl, {}>;
|
||||
/**
|
||||
* Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed
|
||||
* to another function.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Creates factory from {@link ApiRef} or returns the factory itself if provided.
|
||||
*
|
||||
* @param factory - Existing factory or {@link ApiRef}.
|
||||
* @param instance - The instance to be returned by the factory.
|
||||
* @public
|
||||
*/
|
||||
export function createApiFactory<
|
||||
Api,
|
||||
Impl extends Api,
|
||||
Deps extends { [name in string]: unknown },
|
||||
>(
|
||||
factory: ApiFactory<Api, Impl, Deps> | ApiRef<Api>,
|
||||
instance?: Impl,
|
||||
): ApiFactory<Api, Impl, Deps> {
|
||||
if ('id' in factory) {
|
||||
return {
|
||||
api: factory,
|
||||
deps: {} as TypesToApiRefs<Deps>,
|
||||
factory: () => instance!,
|
||||
};
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
export { createApiFactory } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,61 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* API reference.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiRef<T> = {
|
||||
id: string;
|
||||
T: T;
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch-all {@link ApiRef} type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AnyApiRef = ApiRef<unknown>;
|
||||
|
||||
/**
|
||||
* Wraps a type with API properties into a type holding their respective {@link ApiRef}s.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TypesToApiRefs<T> = { [key in keyof T]: ApiRef<T[key]> };
|
||||
|
||||
/**
|
||||
* Provides lookup of APIs through their {@link ApiRef}s.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiHolder = {
|
||||
get<T>(api: ApiRef<T>): T | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes type returning API implementations.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiFactory<
|
||||
Api,
|
||||
Impl extends Api,
|
||||
Deps extends { [name in string]: unknown },
|
||||
> = {
|
||||
api: ApiRef<Api>;
|
||||
deps: TypesToApiRefs<Deps>;
|
||||
factory(deps: Deps): Impl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch-all {@link ApiFactory} type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AnyApiFactory = ApiFactory<
|
||||
unknown,
|
||||
unknown,
|
||||
{ [key in string]: unknown }
|
||||
>;
|
||||
export type {
|
||||
ApiRef,
|
||||
AnyApiRef,
|
||||
TypesToApiRefs,
|
||||
ApiHolder,
|
||||
ApiFactory,
|
||||
AnyApiFactory,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,81 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentType, PropsWithChildren } from 'react';
|
||||
import { ApiRef, ApiHolder, TypesToApiRefs } from './types';
|
||||
import { useVersionedContext } from '@backstage/version-bridge';
|
||||
import { NotImplementedError } from '@backstage/errors';
|
||||
|
||||
/**
|
||||
* React hook for retrieving {@link ApiHolder}, an API catalog.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useApiHolder(): ApiHolder {
|
||||
const versionedHolder = useVersionedContext<{ 1: ApiHolder }>('api-context');
|
||||
if (!versionedHolder) {
|
||||
throw new NotImplementedError('API context is not available');
|
||||
}
|
||||
|
||||
const apiHolder = versionedHolder.atVersion(1);
|
||||
if (!apiHolder) {
|
||||
throw new NotImplementedError('ApiContext v1 not available');
|
||||
}
|
||||
return apiHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook for retrieving APIs.
|
||||
*
|
||||
* @param apiRef - Reference of the API to use.
|
||||
* @public
|
||||
*/
|
||||
export function useApi<T>(apiRef: ApiRef<T>): T {
|
||||
const apiHolder = useApiHolder();
|
||||
|
||||
const api = apiHolder.get(apiRef);
|
||||
if (!api) {
|
||||
throw new NotImplementedError(`No implementation available for ${apiRef}`);
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for giving component an API context.
|
||||
*
|
||||
* @param apis - APIs for the context.
|
||||
* @public
|
||||
*/
|
||||
export function withApis<T extends {}>(apis: TypesToApiRefs<T>) {
|
||||
return function withApisWrapper<TProps extends T>(
|
||||
WrappedComponent: ComponentType<TProps>,
|
||||
) {
|
||||
const Hoc = (props: PropsWithChildren<Omit<TProps, keyof T>>) => {
|
||||
const apiHolder = useApiHolder();
|
||||
|
||||
const impls = {} as T;
|
||||
|
||||
for (const key in apis) {
|
||||
if (apis.hasOwnProperty(key)) {
|
||||
const ref = apis[key];
|
||||
|
||||
const api = apiHolder.get(ref);
|
||||
if (!api) {
|
||||
throw new NotImplementedError(
|
||||
`No implementation available for ${ref}`,
|
||||
);
|
||||
}
|
||||
impls[key] = api;
|
||||
}
|
||||
}
|
||||
|
||||
return <WrappedComponent {...(props as TProps)} {...impls} />;
|
||||
};
|
||||
const displayName =
|
||||
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||
|
||||
Hoc.displayName = `withApis(${displayName})`;
|
||||
|
||||
return Hoc;
|
||||
};
|
||||
}
|
||||
export { useApiHolder, useApi, withApis } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -14,24 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
|
||||
/**
|
||||
* IconComponent is the common icon type used throughout Backstage when
|
||||
* working with and rendering generic icons, including the app system icons.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The type is based on SvgIcon from Material UI, but we do not want the plugin-api
|
||||
* package to have a dependency on Material UI, nor do we want the props to be as broad
|
||||
* as the SvgIconProps interface.
|
||||
*
|
||||
* If you have the need to forward additional props from SvgIconProps, you can
|
||||
* open an issue or submit a PR to the main Backstage repo. When doing so please
|
||||
* also describe your use-case and reasoning of the addition.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type IconComponent = ComponentType<{
|
||||
fontSize?: 'medium' | 'large' | 'small' | 'inherit';
|
||||
}>;
|
||||
export type { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
export {
|
||||
type TranslationMessages,
|
||||
type TranslationMessagesOptions,
|
||||
createTranslationMessages,
|
||||
} from './TranslationMessages';
|
||||
export {
|
||||
type TranslationResource,
|
||||
type TranslationResourceOptions,
|
||||
createTranslationResource,
|
||||
} from './TranslationResource';
|
||||
export {
|
||||
type TranslationRef,
|
||||
type TranslationRefOptions,
|
||||
createTranslationRef,
|
||||
} from './TranslationRef';
|
||||
export { useTranslationRef } from './useTranslationRef';
|
||||
@@ -5,9 +5,7 @@
|
||||
"role": "web-library"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -16,8 +14,19 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"package.json": [
|
||||
"package.json"
|
||||
]
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
@@ -31,8 +40,9 @@
|
||||
"test": "backstage-cli package test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "workspace:^",
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"@backstage/version-bridge": "workspace:^",
|
||||
"@material-ui/core": "^4.12.4",
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import { useAnalytics } from './useAnalytics';
|
||||
import { analyticsApiRef } from '@backstage/core-plugin-api';
|
||||
import { analyticsApiRef } from '../apis/definitions/AnalyticsApi';
|
||||
import { TestApiProvider } from '@backstage/test-utils';
|
||||
|
||||
describe('useAnalytics', () => {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { useApi } from '../apis/system';
|
||||
import { useAnalyticsContext } from './AnalyticsContext';
|
||||
import { analyticsApiRef, AnalyticsTracker, AnalyticsApi } from '../apis';
|
||||
import { useRef } from 'react';
|
||||
|
||||
@@ -14,8 +14,43 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
type AlertApi,
|
||||
type AlertMessage,
|
||||
alertApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { createApiRef, ApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* Message handled by the {@link AlertApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AlertMessage = {
|
||||
message: string;
|
||||
// Severity will default to success since that is what material ui defaults the value to.
|
||||
severity?: 'success' | 'info' | 'warning' | 'error';
|
||||
display?: 'permanent' | 'transient';
|
||||
};
|
||||
|
||||
/**
|
||||
* The alert API is used to report alerts to the app, and display them to the user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AlertApi = {
|
||||
/**
|
||||
* Post an alert for handling by the application.
|
||||
*/
|
||||
post(alert: AlertMessage): void;
|
||||
|
||||
/**
|
||||
* Observe alerts posted by other parts of the application.
|
||||
*/
|
||||
alert$(): Observable<AlertMessage>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link AlertApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const alertApiRef: ApiRef<AlertApi> = createApiRef({
|
||||
id: 'core.alert',
|
||||
});
|
||||
|
||||
@@ -14,9 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { AnalyticsContextValue } from '../../analytics/types';
|
||||
import type { AnalyticsImplementationBlueprint } from '../../blueprints/';
|
||||
|
||||
/**
|
||||
* Represents an event worth tracking in an analytics system that could inform
|
||||
|
||||
@@ -14,7 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
type AppLanguageApi,
|
||||
appLanguageApiRef,
|
||||
} from '@backstage/core-plugin-api/alpha';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/** @public */
|
||||
export type AppLanguageApi = {
|
||||
getAvailableLanguages(): { languages: string[] };
|
||||
|
||||
setLanguage(language?: string): void;
|
||||
|
||||
getLanguage(): { language: string };
|
||||
|
||||
language$(): Observable<{ language: string }>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export const appLanguageApiRef: ApiRef<AppLanguageApi> = createApiRef({
|
||||
id: 'core.applanguage',
|
||||
});
|
||||
|
||||
@@ -14,8 +14,74 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
type AppTheme,
|
||||
type AppThemeApi,
|
||||
appThemeApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ReactNode } from 'react';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* Describes a theme provided by the app.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AppTheme = {
|
||||
/**
|
||||
* ID used to remember theme selections.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Title of the theme
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Theme variant
|
||||
*/
|
||||
variant: 'light' | 'dark';
|
||||
|
||||
/**
|
||||
* An Icon for the theme mode setting.
|
||||
*/
|
||||
icon?: React.ReactElement;
|
||||
|
||||
Provider(props: { children: ReactNode }): JSX.Element | null;
|
||||
};
|
||||
|
||||
/**
|
||||
* The AppThemeApi gives access to the current app theme, and allows switching
|
||||
* to other options that have been registered as a part of the App.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AppThemeApi = {
|
||||
/**
|
||||
* Get a list of available themes.
|
||||
*/
|
||||
getInstalledThemes(): AppTheme[];
|
||||
|
||||
/**
|
||||
* Observe the currently selected theme. A value of undefined means no specific theme has been selected.
|
||||
*/
|
||||
activeThemeId$(): Observable<string | undefined>;
|
||||
|
||||
/**
|
||||
* Get the current theme ID. Returns undefined if no specific theme is selected.
|
||||
*/
|
||||
getActiveThemeId(): string | undefined;
|
||||
|
||||
/**
|
||||
* Set a specific theme to use in the app, overriding the default theme selection.
|
||||
*
|
||||
* Clear the selection by passing in undefined.
|
||||
*/
|
||||
setActiveThemeId(themeId?: string): void;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link AppThemeApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const appThemeApiRef: ApiRef<AppThemeApi> = createApiRef({
|
||||
id: 'core.apptheme',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { createApiRef } from '../system';
|
||||
import { FrontendPlugin, Extension, ExtensionDataRef } from '../../wiring';
|
||||
import { ExtensionAttachTo } from '../../wiring/resolveExtensionDefinition';
|
||||
|
||||
|
||||
@@ -13,5 +13,22 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Config } from '@backstage/config';
|
||||
|
||||
export { type ConfigApi, configApiRef } from '@backstage/core-plugin-api';
|
||||
/**
|
||||
* The Config API is used to provide a mechanism to access the
|
||||
* runtime configuration of the system.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ConfigApi = Config;
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link ConfigApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const configApiRef: ApiRef<ConfigApi> = createApiRef({
|
||||
id: 'core.config',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* A handle for an open dialog that can be used to interact with it.
|
||||
|
||||
@@ -13,5 +13,43 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
|
||||
export { type DiscoveryApi, discoveryApiRef } from '@backstage/core-plugin-api';
|
||||
/**
|
||||
* The discovery API is used to provide a mechanism for plugins to
|
||||
* discover the endpoint to use to talk to their backend counterpart.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The purpose of the discovery API is to allow for many different deployment
|
||||
* setups and routing methods through a central configuration, instead
|
||||
* of letting each individual plugin manage that configuration.
|
||||
*
|
||||
* Implementations of the discovery API can be a simple as a URL pattern
|
||||
* using the pluginId, but could also have overrides for individual plugins,
|
||||
* or query a separate discovery service.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type DiscoveryApi = {
|
||||
/**
|
||||
* Returns the HTTP base backend URL for a given plugin, without a trailing slash.
|
||||
*
|
||||
* This method must always be called just before making a request, as opposed to
|
||||
* fetching the URL when constructing an API client. That is to ensure that more
|
||||
* flexible routing patterns can be supported.
|
||||
*
|
||||
* For example, asking for the URL for `auth` may return something
|
||||
* like `https://backstage.example.com/api/auth`
|
||||
*/
|
||||
getBaseUrl(pluginId: string): Promise<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link DiscoveryApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const discoveryApiRef: ApiRef<DiscoveryApi> = createApiRef({
|
||||
id: 'core.discovery',
|
||||
});
|
||||
|
||||
@@ -14,9 +14,78 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
type ErrorApiError,
|
||||
type ErrorApiErrorContext,
|
||||
type ErrorApi,
|
||||
errorApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* Mirrors the JavaScript Error class, for the purpose of
|
||||
* providing documentation and optional fields.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ErrorApiError = {
|
||||
name: string;
|
||||
message: string;
|
||||
stack?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides additional information about an error that was posted to the application.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ErrorApiErrorContext = {
|
||||
/**
|
||||
* If set to true, this error should not be displayed to the user.
|
||||
*
|
||||
* Hidden errors are typically not displayed in the UI, but the ErrorApi
|
||||
* implementation may still report them to error tracking services
|
||||
* or other utilities that care about all errors.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
hidden?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The error API is used to report errors to the app, and display them to the user.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Plugins can use this API as a method of displaying errors to the user, but also
|
||||
* to report errors for collection by error reporting services.
|
||||
*
|
||||
* If an error can be displayed inline, e.g. as feedback in a form, that should be
|
||||
* preferred over relying on this API to display the error. The main use of this API
|
||||
* for displaying errors should be for asynchronous errors, such as a failing background process.
|
||||
*
|
||||
* Even if an error is displayed inline, it should still be reported through this API
|
||||
* if it would be useful to collect or log it for debugging purposes, but with
|
||||
* the hidden flag set. For example, an error arising from form field validation
|
||||
* should probably not be reported, while a failed REST call would be useful to report.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ErrorApi = {
|
||||
/**
|
||||
* Post an error for handling by the application.
|
||||
*/
|
||||
post(error: ErrorApiError, context?: ErrorApiErrorContext): void;
|
||||
|
||||
/**
|
||||
* Observe errors posted by other parts of the application.
|
||||
*/
|
||||
error$(): Observable<{
|
||||
error: ErrorApiError;
|
||||
context?: ErrorApiErrorContext;
|
||||
}>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link ErrorApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const errorApiRef: ApiRef<ErrorApi> = createApiRef({
|
||||
id: 'core.error',
|
||||
});
|
||||
|
||||
@@ -13,11 +13,114 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* We want to maintain the same information as an enum, so we disable the redeclaration warning */
|
||||
/* eslint-disable @typescript-eslint/no-redeclare */
|
||||
|
||||
export {
|
||||
type FeatureFlag,
|
||||
type FeatureFlagState,
|
||||
type FeatureFlagsSaveOptions,
|
||||
type FeatureFlagsApi,
|
||||
featureFlagsApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* Feature flag descriptor.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlag = {
|
||||
name: string;
|
||||
pluginId: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Enum representing the state of a feature flag (inactive/active).
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const FeatureFlagState = {
|
||||
/**
|
||||
* Feature flag inactive (disabled).
|
||||
*/
|
||||
None: 0,
|
||||
/**
|
||||
* Feature flag active (enabled).
|
||||
*/
|
||||
Active: 1,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlagState =
|
||||
(typeof FeatureFlagState)[keyof typeof FeatureFlagState];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export namespace FeatureFlagState {
|
||||
export type None = typeof FeatureFlagState.None;
|
||||
export type Active = typeof FeatureFlagState.Active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Options to use when saving feature flags.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlagsSaveOptions = {
|
||||
/**
|
||||
* The new feature flag states to save.
|
||||
*/
|
||||
states: Record<string, FeatureFlagState>;
|
||||
|
||||
/**
|
||||
* Whether the saves states should be merged into the existing ones, or replace them.
|
||||
*
|
||||
* Defaults to false.
|
||||
*/
|
||||
merge?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The feature flags API is used to toggle functionality to users across plugins and Backstage.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Plugins can use this API to register feature flags that they have available
|
||||
* for users to enable/disable, and this API will centralize the current user's
|
||||
* state of which feature flags they would like to enable.
|
||||
*
|
||||
* This is ideal for Backstage plugins, as well as your own App, to trial incomplete
|
||||
* or unstable upcoming features. Although there will be a common interface for users
|
||||
* to enable and disable feature flags, this API acts as another way to enable/disable.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface FeatureFlagsApi {
|
||||
/**
|
||||
* Registers a new feature flag. Once a feature flag has been registered it
|
||||
* can be toggled by users, and read back to enable or disable features.
|
||||
*/
|
||||
registerFlag(flag: FeatureFlag): void;
|
||||
|
||||
/**
|
||||
* Get a list of all registered flags.
|
||||
*/
|
||||
getRegisteredFlags(): FeatureFlag[];
|
||||
|
||||
/**
|
||||
* Whether the feature flag with the given name is currently activated for the user.
|
||||
*/
|
||||
isActive(name: string): boolean;
|
||||
|
||||
/**
|
||||
* Save the user's choice of feature flag states.
|
||||
*/
|
||||
save(options: FeatureFlagsSaveOptions): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link FeatureFlagsApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const featureFlagsApiRef: ApiRef<FeatureFlagsApi> = createApiRef({
|
||||
id: 'core.featureflags',
|
||||
});
|
||||
|
||||
@@ -14,4 +14,38 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { type FetchApi, fetchApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* A wrapper for the fetch API, that has additional behaviors such as the
|
||||
* ability to automatically inject auth information where necessary.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FetchApi = {
|
||||
/**
|
||||
* The `fetch` implementation.
|
||||
*/
|
||||
fetch: typeof fetch;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link FetchApi}.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This is a wrapper for the fetch API, that has additional behaviors such as
|
||||
* the ability to automatically inject auth information where necessary.
|
||||
*
|
||||
* Note that the default behavior of this API (unless overridden by your org),
|
||||
* is to require that the user is already signed in so that it has auth
|
||||
* information to inject. Therefore, using the default implementation of this
|
||||
* utility API e.g. on the `SignInPage` or similar, would cause issues. In
|
||||
* special circumstances like those, you can use the regular system `fetch`
|
||||
* instead.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const fetchApiRef: ApiRef<FetchApi> = createApiRef({
|
||||
id: 'core.fetch',
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { createApiRef } from '../system';
|
||||
import { IconComponent } from '../../icons';
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,5 +13,44 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { BackstageUserIdentity, ProfileInfo } from './auth';
|
||||
|
||||
export { type IdentityApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
/**
|
||||
* The Identity API used to identify and get information about the signed in user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type IdentityApi = {
|
||||
/**
|
||||
* The profile of the signed in user.
|
||||
*/
|
||||
getProfileInfo(): Promise<ProfileInfo>;
|
||||
|
||||
/**
|
||||
* User identity information within Backstage.
|
||||
*/
|
||||
getBackstageIdentity(): Promise<BackstageUserIdentity>;
|
||||
|
||||
/**
|
||||
* Provides credentials in the form of a token which proves the identity of the signed in user.
|
||||
*
|
||||
* The token will be undefined if the signed in user does not have a verified
|
||||
* identity, such as a demo user or mocked user for e2e tests.
|
||||
*/
|
||||
getCredentials(): Promise<{ token?: string }>;
|
||||
|
||||
/**
|
||||
* Sign out the current user
|
||||
*/
|
||||
signOut(): Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link IdentityApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const identityApiRef: ApiRef<IdentityApi> = createApiRef({
|
||||
id: 'core.identity',
|
||||
});
|
||||
|
||||
@@ -14,10 +14,118 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
type OAuthRequesterOptions,
|
||||
type OAuthRequester,
|
||||
type PendingOAuthRequest,
|
||||
type OAuthRequestApi,
|
||||
oauthRequestApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { Observable } from '@backstage/types';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { AuthProviderInfo } from './auth';
|
||||
|
||||
/**
|
||||
* Describes how to handle auth requests. Both how to show them to the user, and what to do when
|
||||
* the user accesses the auth request.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthRequesterOptions<TOAuthResponse> = {
|
||||
/**
|
||||
* Information about the auth provider, which will be forwarded to auth requests.
|
||||
*/
|
||||
provider: AuthProviderInfo;
|
||||
|
||||
/**
|
||||
* Implementation of the auth flow, which will be called synchronously when
|
||||
* trigger() is called on an auth requests.
|
||||
*/
|
||||
onAuthRequest(scopes: Set<string>): Promise<TOAuthResponse>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Function used to trigger new auth requests for a set of scopes.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The returned promise will resolve to the same value returned by the onAuthRequest in the
|
||||
* {@link OAuthRequesterOptions}. Or rejected, if the request is rejected.
|
||||
*
|
||||
* This function can be called multiple times before the promise resolves. All calls
|
||||
* will be merged into one request, and the scopes forwarded to the onAuthRequest will be the
|
||||
* union of all requested scopes.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthRequester<TAuthResponse> = (
|
||||
scopes: Set<string>,
|
||||
) => Promise<TAuthResponse>;
|
||||
|
||||
/**
|
||||
* An pending auth request for a single auth provider. The request will remain in this pending
|
||||
* state until either reject() or trigger() is called.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Any new requests for the same provider are merged into the existing pending request, meaning
|
||||
* there will only ever be a single pending request for a given provider.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PendingOAuthRequest = {
|
||||
/**
|
||||
* Information about the auth provider, as given in the AuthRequesterOptions
|
||||
*/
|
||||
provider: AuthProviderInfo;
|
||||
|
||||
/**
|
||||
* Rejects the request, causing all pending AuthRequester calls to fail with "RejectedError".
|
||||
*/
|
||||
reject(): void;
|
||||
|
||||
/**
|
||||
* Trigger the auth request to continue the auth flow, by for example showing a popup.
|
||||
*
|
||||
* Synchronously calls onAuthRequest with all scope currently in the request.
|
||||
*/
|
||||
trigger(): Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides helpers for implemented OAuth login flows within Backstage.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthRequestApi = {
|
||||
/**
|
||||
* A utility for showing login popups or similar things, and merging together multiple requests for
|
||||
* different scopes into one request that includes all scopes.
|
||||
*
|
||||
* The passed in options provide information about the login provider, and how to handle auth requests.
|
||||
*
|
||||
* The returned AuthRequester function is used to request login with new scopes. These requests
|
||||
* are merged together and forwarded to the auth handler, as soon as a consumer of auth requests
|
||||
* triggers an auth flow.
|
||||
*
|
||||
* See AuthRequesterOptions, AuthRequester, and handleAuthRequests for more info.
|
||||
*/
|
||||
createAuthRequester<OAuthResponse>(
|
||||
options: OAuthRequesterOptions<OAuthResponse>,
|
||||
): OAuthRequester<OAuthResponse>;
|
||||
|
||||
/**
|
||||
* Observers pending auth requests. The returned observable will emit all
|
||||
* current active auth request, at most one for each created auth requester.
|
||||
*
|
||||
* Each request has its own info about the login provider, forwarded from the auth requester options.
|
||||
*
|
||||
* Depending on user interaction, the request should either be rejected, or used to trigger the auth handler.
|
||||
* If the request is rejected, all pending AuthRequester calls will fail with a "RejectedError".
|
||||
* If a auth is triggered, and the auth handler resolves successfully, then all currently pending
|
||||
* AuthRequester calls will resolve to the value returned by the onAuthRequest call.
|
||||
*/
|
||||
authRequest$(): Observable<PendingOAuthRequest[]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link OAuthRequestApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const oauthRequestApiRef: ApiRef<OAuthRequestApi> = createApiRef({
|
||||
id: 'core.oauthrequest',
|
||||
});
|
||||
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
SubRouteRef,
|
||||
ExternalRouteRef,
|
||||
} from '../../routing';
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* TS magic for handling route parameters.
|
||||
|
||||
@@ -14,8 +14,97 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
type StorageValueSnapshot,
|
||||
type StorageApi,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { JsonValue, Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* A snapshot in time of the current known value of a storage key.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type StorageValueSnapshot<TValue extends JsonValue> =
|
||||
| {
|
||||
key: string;
|
||||
presence: 'unknown' | 'absent';
|
||||
value?: undefined;
|
||||
}
|
||||
| {
|
||||
key: string;
|
||||
presence: 'present';
|
||||
value: TValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides a key-value persistence API.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface StorageApi {
|
||||
/**
|
||||
* Create a bucket to store data in.
|
||||
*
|
||||
* @param name - Namespace for the storage to be stored under,
|
||||
* will inherit previous namespaces too
|
||||
*/
|
||||
forBucket(name: string): StorageApi;
|
||||
|
||||
/**
|
||||
* Remove persistent data.
|
||||
*
|
||||
* @param key - Unique key associated with the data.
|
||||
*/
|
||||
remove(key: string): Promise<void>;
|
||||
|
||||
/**
|
||||
* Save persistent data, and emit messages to anyone that is using
|
||||
* {@link StorageApi.observe$} for this key.
|
||||
*
|
||||
* @param key - Unique key associated with the data.
|
||||
* @param data - The data to be stored under the key.
|
||||
*/
|
||||
set<T extends JsonValue>(key: string, data: T): Promise<void>;
|
||||
|
||||
/**
|
||||
* Observe the value over time for a particular key in the current bucket.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* The observable will only emit values when the value changes in the underlying
|
||||
* storage, although multiple values with the same shape may be emitted in a row.
|
||||
*
|
||||
* If a {@link StorageApi.snapshot} of a key is retrieved and the presence is
|
||||
* `'unknown'`, then you are guaranteed to receive a snapshot with a known
|
||||
* presence, as long as you observe the key within the same tick.
|
||||
*
|
||||
* Since the emitted values are shared across all subscribers, it is important
|
||||
* not to mutate the returned values. The values may be frozen as a precaution.
|
||||
*
|
||||
* @param key - Unique key associated with the data
|
||||
*/
|
||||
observe$<T extends JsonValue>(
|
||||
key: string,
|
||||
): Observable<StorageValueSnapshot<T>>;
|
||||
|
||||
/**
|
||||
* Returns an immediate snapshot value for the given key, if possible.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Combine with {@link StorageApi.observe$} to get notified of value changes.
|
||||
*
|
||||
* Note that this method is synchronous, and some underlying storages may be
|
||||
* unable to retrieve a value using this method - the result may or may not
|
||||
* consistently have a presence of 'unknown'. Use {@link StorageApi.observe$}
|
||||
* to be sure to receive an actual value eventually.
|
||||
*/
|
||||
snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link ApiRef} of {@link StorageApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const storageApiRef: ApiRef<StorageApi> = createApiRef({
|
||||
id: 'core.storage',
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { SwappableComponentRef } from '../../components';
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { createApiRef } from '../system';
|
||||
|
||||
/**
|
||||
* API for looking up components based on component refs.
|
||||
|
||||
+6
-6
@@ -14,9 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ApiRef, createApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { Expand, ExpandRecursive, Observable } from '@backstage/types';
|
||||
import { TranslationRef } from '../../translation/TranslationRef';
|
||||
import { TranslationRef } from '../../translation';
|
||||
import { JSX } from 'react';
|
||||
|
||||
/**
|
||||
@@ -305,7 +305,7 @@ type TranslationFunctionOptions<
|
||||
>
|
||||
>;
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export type TranslationFunction<TMessages extends { [key in string]: string }> =
|
||||
CollapsedMessages<TMessages> extends infer IMessages extends {
|
||||
[key in string]: string;
|
||||
@@ -340,11 +340,11 @@ export type TranslationFunction<TMessages extends { [key in string]: string }> =
|
||||
}
|
||||
: never;
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export type TranslationSnapshot<TMessages extends { [key in string]: string }> =
|
||||
{ ready: false } | { ready: true; t: TranslationFunction<TMessages> };
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export type TranslationApi = {
|
||||
getTranslation<TMessages extends { [key in string]: string }>(
|
||||
translationRef: TranslationRef<string, TMessages>,
|
||||
@@ -356,7 +356,7 @@ export type TranslationApi = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* @public
|
||||
*/
|
||||
export const translationApiRef: ApiRef<TranslationApi> = createApiRef({
|
||||
id: 'core.translation',
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
export { appLanguageApiRef, type AppLanguageApi } from './AppLanguageApi';
|
||||
@@ -13,29 +13,496 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/* We want to maintain the same information as an enum, so we disable the redeclaration warning */
|
||||
/* eslint-disable @typescript-eslint/no-redeclare */
|
||||
|
||||
export {
|
||||
type BackstageIdentityApi,
|
||||
type BackstageIdentityResponse,
|
||||
type BackstageUserIdentity,
|
||||
type AuthProviderInfo,
|
||||
type AuthRequestOptions,
|
||||
type OAuthScope,
|
||||
type OAuthApi,
|
||||
type OpenIdConnectApi,
|
||||
type ProfileInfoApi,
|
||||
type ProfileInfo,
|
||||
type SessionApi,
|
||||
SessionState,
|
||||
atlassianAuthApiRef,
|
||||
bitbucketAuthApiRef,
|
||||
bitbucketServerAuthApiRef,
|
||||
githubAuthApiRef,
|
||||
gitlabAuthApiRef,
|
||||
googleAuthApiRef,
|
||||
oktaAuthApiRef,
|
||||
microsoftAuthApiRef,
|
||||
oneloginAuthApiRef,
|
||||
vmwareCloudAuthApiRef,
|
||||
openshiftAuthApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ApiRef, createApiRef } from '../system';
|
||||
import { IconComponent } from '../../icons/types';
|
||||
import { Observable } from '@backstage/types';
|
||||
|
||||
/**
|
||||
* This file contains declarations for common interfaces of auth-related APIs.
|
||||
* The declarations should be used to signal which type of authentication and
|
||||
* authorization methods each separate auth provider supports.
|
||||
*
|
||||
* For example, a Google OAuth provider that supports OAuth 2 and OpenID Connect,
|
||||
* would be declared as follows:
|
||||
*
|
||||
* const googleAuthApiRef = createApiRef<OAuthApi & OpenIDConnectApi>({ ... })
|
||||
*/
|
||||
|
||||
/**
|
||||
* Information about the auth provider.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This information is used both to connect the correct auth provider in the backend, as
|
||||
* well as displaying the provider to the user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuthProviderInfo = {
|
||||
/**
|
||||
* The ID of the auth provider. This should match with ID of the provider in the `@backstage/auth-backend`.
|
||||
*/
|
||||
id: string;
|
||||
|
||||
/**
|
||||
* Title for the auth provider, for example "GitHub"
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Icon for the auth provider.
|
||||
*/
|
||||
icon: IconComponent;
|
||||
|
||||
/**
|
||||
* Optional user friendly messaage to display for the auth provider.
|
||||
*/
|
||||
message?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* An array of scopes, or a scope string formatted according to the
|
||||
* auth provider, which is typically a space separated list.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* See the documentation for each auth provider for the list of scopes
|
||||
* supported by each provider.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthScope = string | string[];
|
||||
|
||||
/**
|
||||
* Configuration of an authentication request.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuthRequestOptions = {
|
||||
/**
|
||||
* If this is set to true, the user will not be prompted to log in,
|
||||
* and an empty response will be returned if there is no existing session.
|
||||
*
|
||||
* This can be used to perform a check whether the user is logged in, or if you don't
|
||||
* want to force a user to be logged in, but provide functionality if they already are.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
optional?: boolean;
|
||||
|
||||
/**
|
||||
* If this is set to true, the request will bypass the regular oauth login modal
|
||||
* and open the login popup directly.
|
||||
*
|
||||
* The method must be called synchronously from a user action for this to work in all browsers.
|
||||
*
|
||||
* @defaultValue false
|
||||
*/
|
||||
instantPopup?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to OAuth 2 credentials. It lets you request access tokens,
|
||||
* which can be used to act on behalf of the user when talking to APIs.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OAuthApi = {
|
||||
/**
|
||||
* Requests an OAuth 2 Access Token, optionally with a set of scopes. The access token allows
|
||||
* you to make requests on behalf of the user, and the copes may grant you broader access, depending
|
||||
* on the auth provider.
|
||||
*
|
||||
* Each auth provider has separate handling of scope, so you need to look at the documentation
|
||||
* for each one to know what scope you need to request.
|
||||
*
|
||||
* This method is cheap and should be called each time an access token is used. Do not for example
|
||||
* store the access token in React component state, as that could cause the token to expire. Instead
|
||||
* fetch a new access token for each request.
|
||||
*
|
||||
* Be sure to include all required scopes when requesting an access token. When testing your implementation
|
||||
* it is best to log out the Backstage session and then visit your plugin page directly, as
|
||||
* you might already have some required scopes in your existing session. Not requesting the correct
|
||||
* scopes can lead to 403 or other authorization errors, which can be tricky to debug.
|
||||
*
|
||||
* If the user has not yet granted access to the provider and the set of requested scopes, the user
|
||||
* will be prompted to log in. The returned promise will not resolve until the user has
|
||||
* successfully logged in. The returned promise can be rejected, but only if the user rejects the login request.
|
||||
*/
|
||||
getAccessToken(
|
||||
scope?: OAuthScope,
|
||||
options?: AuthRequestOptions,
|
||||
): Promise<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to OpenID Connect credentials. It lets you request ID tokens,
|
||||
* which can be passed to backend services to prove the user's identity.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type OpenIdConnectApi = {
|
||||
/**
|
||||
* Requests an OpenID Connect ID Token.
|
||||
*
|
||||
* This method is cheap and should be called each time an ID token is used. Do not for example
|
||||
* store the id token in React component state, as that could cause the token to expire. Instead
|
||||
* fetch a new id token for each request.
|
||||
*
|
||||
* If the user has not yet logged in to Google inside Backstage, the user will be prompted
|
||||
* to log in. The returned promise will not resolve until the user has successfully logged in.
|
||||
* The returned promise can be rejected, but only if the user rejects the login request.
|
||||
*/
|
||||
getIdToken(options?: AuthRequestOptions): Promise<string>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to profile information of the user from an auth provider.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ProfileInfoApi = {
|
||||
/**
|
||||
* Get profile information for the user as supplied by this auth provider.
|
||||
*
|
||||
* If the optional flag is not set, a session is guaranteed to be returned, while if
|
||||
* the optional flag is set, the session may be undefined. See {@link AuthRequestOptions} for more details.
|
||||
*/
|
||||
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
|
||||
};
|
||||
|
||||
/**
|
||||
* This API provides access to the user's identity within Backstage.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* An auth provider that implements this interface can be used to sign-in to backstage. It is
|
||||
* not intended to be used directly from a plugin, but instead serves as a connection between
|
||||
* this authentication method and the app's {@link IdentityApi}
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type BackstageIdentityApi = {
|
||||
/**
|
||||
* Get the user's identity within Backstage. This should normally not be called directly,
|
||||
* use the {@link IdentityApi} instead.
|
||||
*
|
||||
* If the optional flag is not set, a session is guaranteed to be returned, while if
|
||||
* the optional flag is set, the session may be undefined. See {@link AuthRequestOptions} for more details.
|
||||
*/
|
||||
getBackstageIdentity(
|
||||
options?: AuthRequestOptions,
|
||||
): Promise<BackstageIdentityResponse | undefined>;
|
||||
};
|
||||
|
||||
/**
|
||||
* User identity information within Backstage.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type BackstageUserIdentity = {
|
||||
/**
|
||||
* The type of identity that this structure represents. In the frontend app
|
||||
* this will currently always be 'user'.
|
||||
*/
|
||||
type: 'user';
|
||||
|
||||
/**
|
||||
* The entityRef of the user in the catalog.
|
||||
* For example User:default/sandra
|
||||
*/
|
||||
userEntityRef: string;
|
||||
|
||||
/**
|
||||
* The user and group entities that the user claims ownership through
|
||||
*/
|
||||
ownershipEntityRefs: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Token and Identity response, with the users claims in the Identity.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type BackstageIdentityResponse = {
|
||||
/**
|
||||
* The token used to authenticate the user within Backstage.
|
||||
*/
|
||||
token: string;
|
||||
|
||||
/**
|
||||
* The time at which the token expires. If not set, it can be assumed that the token does not expire.
|
||||
*/
|
||||
expiresAt?: Date;
|
||||
|
||||
/**
|
||||
* Identity information derived from the token.
|
||||
*/
|
||||
identity: BackstageUserIdentity;
|
||||
};
|
||||
|
||||
/**
|
||||
* Profile information of the user.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ProfileInfo = {
|
||||
/**
|
||||
* Email ID.
|
||||
*/
|
||||
email?: string;
|
||||
|
||||
/**
|
||||
* Display name that can be presented to the user.
|
||||
*/
|
||||
displayName?: string;
|
||||
|
||||
/**
|
||||
* URL to an avatar image of the user.
|
||||
*/
|
||||
picture?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Session state values passed to subscribers of the SessionApi.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const SessionState = {
|
||||
/**
|
||||
* User signed in.
|
||||
*/
|
||||
SignedIn: 'SignedIn',
|
||||
/**
|
||||
* User not signed in.
|
||||
*/
|
||||
SignedOut: 'SignedOut',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export type SessionState = (typeof SessionState)[keyof typeof SessionState];
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export namespace SessionState {
|
||||
export type SignedIn = typeof SessionState.SignedIn;
|
||||
export type SignedOut = typeof SessionState.SignedOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* The SessionApi provides basic controls for any auth provider that is tied to a persistent session.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type SessionApi = {
|
||||
/**
|
||||
* Sign in with a minimum set of permissions.
|
||||
*/
|
||||
signIn(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Sign out from the current session. This will reload the page.
|
||||
*/
|
||||
signOut(): Promise<void>;
|
||||
|
||||
/**
|
||||
* Observe the current state of the auth session. Emits the current state on subscription.
|
||||
*/
|
||||
sessionState$(): Observable<SessionState>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Provides authentication towards Google APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developers.google.com/identity/protocols/googlescopes} for a full list of supported scopes.
|
||||
*
|
||||
* Note that the ID token payload is only guaranteed to contain the user's numerical Google ID,
|
||||
* email and expiration information. Do not rely on any other fields, as they might not be present.
|
||||
*/
|
||||
export const googleAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.google',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards GitHub APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const githubAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.github',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Okta APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developer.okta.com/docs/guides/implement-oauth-for-okta/scopes/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const oktaAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.okta',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards GitLab APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#limiting-scopes-of-a-personal-access-token}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const gitlabAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.gitlab',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Microsoft APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* For more info and a full list of supported scopes, see:
|
||||
* - {@link https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent}
|
||||
* - {@link https://docs.microsoft.com/en-us/graph/permissions-reference}
|
||||
*/
|
||||
export const microsoftAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.microsoft',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards OneLogin APIs.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const oneloginAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.onelogin',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Bitbucket APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const bitbucketAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.bitbucket',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Bitbucket Server APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://confluence.atlassian.com/bitbucketserver/bitbucket-oauth-2-0-provider-api-1108483661.html#BitbucketOAuth2.0providerAPI-scopes}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const bitbucketServerAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.bitbucket-server',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards Atlassian APIs.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://developer.atlassian.com/cloud/jira/platform/scopes-for-connect-and-oauth-2-3LO-apps/}
|
||||
* for a full list of supported scopes.
|
||||
*/
|
||||
export const atlassianAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.atlassian',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards VMware Cloud APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* For more info about VMware Cloud identity and access management:
|
||||
* - {@link https://docs.vmware.com/en/VMware-Cloud-services/services/Using-VMware-Cloud-Services/GUID-53D39337-D93A-4B84-BD18-DDF43C21479A.html}
|
||||
*/
|
||||
export const vmwareCloudAuthApiRef: ApiRef<
|
||||
OAuthApi &
|
||||
OpenIdConnectApi &
|
||||
ProfileInfoApi &
|
||||
BackstageIdentityApi &
|
||||
SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.vmware-cloud',
|
||||
});
|
||||
|
||||
/**
|
||||
* Provides authentication towards OpenShift APIs and identities.
|
||||
*
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* See {@link https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html/authentication_and_authorization/configuring-oauth-clients}
|
||||
* on how to configure the OAuth clients and
|
||||
* {@link https://docs.redhat.com/en/documentation/openshift_container_platform/latest/html-single/authentication_and_authorization/index#tokens-scoping-about_configuring-internal-oauth}
|
||||
* for available scopes.
|
||||
*/
|
||||
export const openshiftAuthApiRef: ApiRef<
|
||||
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
|
||||
> = createApiRef({
|
||||
id: 'core.auth.openshift',
|
||||
});
|
||||
|
||||
@@ -33,6 +33,7 @@ export {
|
||||
export * from './auth';
|
||||
|
||||
export * from './AlertApi';
|
||||
export * from './AppLanguageApi';
|
||||
export * from './AppThemeApi';
|
||||
export * from './SwappableComponentsApi';
|
||||
export * from './ConfigApi';
|
||||
@@ -47,3 +48,4 @@ export * from './OAuthRequestApi';
|
||||
export * from './RouteResolutionApi';
|
||||
export * from './StorageApi';
|
||||
export * from './AnalyticsApi';
|
||||
export * from './TranslationApi';
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2020 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 { createApiRef } from './ApiRef';
|
||||
|
||||
describe('ApiRef', () => {
|
||||
it('should be created', () => {
|
||||
const ref = createApiRef({ id: 'abc' });
|
||||
expect(ref.id).toBe('abc');
|
||||
expect(String(ref)).toBe('apiRef{abc}');
|
||||
expect(() => ref.T).toThrow('tried to read ApiRef.T of apiRef{abc}');
|
||||
});
|
||||
|
||||
it('should reject invalid ids', () => {
|
||||
for (const id of ['a', 'abc', 'ab-c', 'a.b.c', 'a-b.c', 'abc.a-b-c.abc3']) {
|
||||
expect(createApiRef({ id }).id).toBe(id);
|
||||
}
|
||||
|
||||
for (const id of [
|
||||
'123',
|
||||
'ab-3',
|
||||
'ab_c',
|
||||
'.',
|
||||
'2ac',
|
||||
'ab.3a',
|
||||
'.abc',
|
||||
'abc.',
|
||||
'ab..s',
|
||||
'',
|
||||
'_',
|
||||
]) {
|
||||
expect(() => createApiRef({ id }).id).toThrow(
|
||||
`API id must only contain period separated lowercase alphanum tokens with dashes, got '${id}'`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -14,8 +14,51 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
type ApiRef,
|
||||
type ApiRefConfig,
|
||||
createApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import type { ApiRef } from './types';
|
||||
|
||||
/**
|
||||
* API reference configuration - holds an ID of the referenced API.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiRefConfig = {
|
||||
id: string;
|
||||
};
|
||||
|
||||
class ApiRefImpl<T> implements ApiRef<T> {
|
||||
constructor(private readonly config: ApiRefConfig) {
|
||||
const valid = config.id
|
||||
.split('.')
|
||||
.flatMap(part => part.split('-'))
|
||||
.every(part => part.match(/^[a-z][a-z0-9]*$/));
|
||||
if (!valid) {
|
||||
throw new Error(
|
||||
`API id must only contain period separated lowercase alphanum tokens with dashes, got '${config.id}'`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get id(): string {
|
||||
return this.config.id;
|
||||
}
|
||||
|
||||
// Utility for getting type of an api, using `typeof apiRef.T`
|
||||
get T(): T {
|
||||
throw new Error(`tried to read ApiRef.T of ${this}`);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `apiRef{${this.config.id}}`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a reference to an API.
|
||||
*
|
||||
* @param config - The descriptor of the API to reference.
|
||||
* @returns An API reference.
|
||||
* @public
|
||||
*/
|
||||
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T> {
|
||||
return new ApiRefImpl<T>(config);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2020 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.
|
||||
@@ -14,4 +14,61 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { createApiFactory } from '@backstage/core-plugin-api';
|
||||
import { ApiRef, ApiFactory, TypesToApiRefs } from './types';
|
||||
|
||||
/**
|
||||
* Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed
|
||||
* to another function.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This function doesn't actually do anything, it's only used to infer types.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function createApiFactory<
|
||||
Api,
|
||||
Impl extends Api,
|
||||
Deps extends { [name in string]: unknown },
|
||||
>(factory: ApiFactory<Api, Impl, Deps>): ApiFactory<Api, Impl, Deps>;
|
||||
/**
|
||||
* Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed
|
||||
* to another function.
|
||||
*
|
||||
* @param api - Ref of the API that will be produced by the factory.
|
||||
* @param instance - Implementation of the API to use.
|
||||
* @public
|
||||
*/
|
||||
export function createApiFactory<Api, Impl extends Api>(
|
||||
api: ApiRef<Api>,
|
||||
instance: Impl,
|
||||
): ApiFactory<Api, Impl, {}>;
|
||||
/**
|
||||
* Used to infer types for a standalone {@link ApiFactory} that isn't immediately passed
|
||||
* to another function.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* Creates factory from {@link ApiRef} or returns the factory itself if provided.
|
||||
*
|
||||
* @param factory - Existing factory or {@link ApiRef}.
|
||||
* @param instance - The instance to be returned by the factory.
|
||||
* @public
|
||||
*/
|
||||
export function createApiFactory<
|
||||
Api,
|
||||
Impl extends Api,
|
||||
Deps extends { [name in string]: unknown },
|
||||
>(
|
||||
factory: ApiFactory<Api, Impl, Deps> | ApiRef<Api>,
|
||||
instance?: Impl,
|
||||
): ApiFactory<Api, Impl, Deps> {
|
||||
if ('id' in factory) {
|
||||
return {
|
||||
api: factory,
|
||||
deps: {} as TypesToApiRefs<Deps>,
|
||||
factory: () => instance!,
|
||||
};
|
||||
}
|
||||
return factory;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2020 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2020 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.
|
||||
@@ -14,11 +14,61 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type {
|
||||
ApiRef,
|
||||
AnyApiRef,
|
||||
TypesToApiRefs,
|
||||
ApiHolder,
|
||||
ApiFactory,
|
||||
AnyApiFactory,
|
||||
} from '@backstage/core-plugin-api';
|
||||
/**
|
||||
* API reference.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiRef<T> = {
|
||||
id: string;
|
||||
T: T;
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch-all {@link ApiRef} type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AnyApiRef = ApiRef<unknown>;
|
||||
|
||||
/**
|
||||
* Wraps a type with API properties into a type holding their respective {@link ApiRef}s.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type TypesToApiRefs<T> = { [key in keyof T]: ApiRef<T[key]> };
|
||||
|
||||
/**
|
||||
* Provides lookup of APIs through their {@link ApiRef}s.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiHolder = {
|
||||
get<T>(api: ApiRef<T>): T | undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes type returning API implementations.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type ApiFactory<
|
||||
Api,
|
||||
Impl extends Api,
|
||||
Deps extends { [name in string]: unknown },
|
||||
> = {
|
||||
api: ApiRef<Api>;
|
||||
deps: TypesToApiRefs<Deps>;
|
||||
factory(deps: Deps): Impl;
|
||||
};
|
||||
|
||||
/**
|
||||
* Catch-all {@link ApiFactory} type.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AnyApiFactory = ApiFactory<
|
||||
unknown,
|
||||
unknown,
|
||||
{ [key in string]: unknown }
|
||||
>;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2020 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 { renderHook } from '@testing-library/react';
|
||||
import { createVersionedContextForTesting } from '@backstage/version-bridge';
|
||||
import { createApiRef } from './ApiRef';
|
||||
import { useApi } from './useApi';
|
||||
|
||||
describe('useApi', () => {
|
||||
const context = createVersionedContextForTesting('api-context');
|
||||
|
||||
afterEach(() => {
|
||||
context.reset();
|
||||
});
|
||||
|
||||
it('should resolve routes', () => {
|
||||
const get = jest.fn(() => 'my-api-impl');
|
||||
context.set({ 1: { get } });
|
||||
|
||||
const apiRef = createApiRef<string>({ id: 'x' });
|
||||
const renderedHook = renderHook(() => useApi(apiRef));
|
||||
|
||||
const value = renderedHook.result.current;
|
||||
expect(value).toBe('my-api-impl');
|
||||
expect(get).toHaveBeenCalledWith(apiRef);
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2020 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.
|
||||
@@ -14,4 +14,81 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { useApiHolder, useApi, withApis } from '@backstage/core-plugin-api';
|
||||
import { ComponentType, PropsWithChildren } from 'react';
|
||||
import { ApiRef, ApiHolder, TypesToApiRefs } from './types';
|
||||
import { useVersionedContext } from '@backstage/version-bridge';
|
||||
import { NotImplementedError } from '@backstage/errors';
|
||||
|
||||
/**
|
||||
* React hook for retrieving {@link ApiHolder}, an API catalog.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function useApiHolder(): ApiHolder {
|
||||
const versionedHolder = useVersionedContext<{ 1: ApiHolder }>('api-context');
|
||||
if (!versionedHolder) {
|
||||
throw new NotImplementedError('API context is not available');
|
||||
}
|
||||
|
||||
const apiHolder = versionedHolder.atVersion(1);
|
||||
if (!apiHolder) {
|
||||
throw new NotImplementedError('ApiContext v1 not available');
|
||||
}
|
||||
return apiHolder;
|
||||
}
|
||||
|
||||
/**
|
||||
* React hook for retrieving APIs.
|
||||
*
|
||||
* @param apiRef - Reference of the API to use.
|
||||
* @public
|
||||
*/
|
||||
export function useApi<T>(apiRef: ApiRef<T>): T {
|
||||
const apiHolder = useApiHolder();
|
||||
|
||||
const api = apiHolder.get(apiRef);
|
||||
if (!api) {
|
||||
throw new NotImplementedError(`No implementation available for ${apiRef}`);
|
||||
}
|
||||
return api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for giving component an API context.
|
||||
*
|
||||
* @param apis - APIs for the context.
|
||||
* @public
|
||||
*/
|
||||
export function withApis<T extends {}>(apis: TypesToApiRefs<T>) {
|
||||
return function withApisWrapper<TProps extends T>(
|
||||
WrappedComponent: ComponentType<TProps>,
|
||||
) {
|
||||
const Hoc = (props: PropsWithChildren<Omit<TProps, keyof T>>) => {
|
||||
const apiHolder = useApiHolder();
|
||||
|
||||
const impls = {} as T;
|
||||
|
||||
for (const key in apis) {
|
||||
if (apis.hasOwnProperty(key)) {
|
||||
const ref = apis[key];
|
||||
|
||||
const api = apiHolder.get(ref);
|
||||
if (!api) {
|
||||
throw new NotImplementedError(
|
||||
`No implementation available for ${ref}`,
|
||||
);
|
||||
}
|
||||
impls[key] = api;
|
||||
}
|
||||
}
|
||||
|
||||
return <WrappedComponent {...(props as TProps)} {...impls} />;
|
||||
};
|
||||
const displayName =
|
||||
WrappedComponent.displayName || WrappedComponent.name || 'Component';
|
||||
|
||||
Hoc.displayName = `withApis(${displayName})`;
|
||||
|
||||
return Hoc;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { createExtensionInput } from '../wiring';
|
||||
import { ApiBlueprint } from './ApiBlueprint';
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { createApiRef } from '../apis/system';
|
||||
|
||||
describe('ApiBlueprint', () => {
|
||||
it('should create an extension with sensible defaults', () => {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { IconComponent } from '../icons/types';
|
||||
import { RouteRef } from '../routing';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { AppTheme } from '@backstage/core-plugin-api';
|
||||
import { AppTheme } from '../apis/definitions/AppThemeApi';
|
||||
import { ThemeBlueprint } from './ThemeBlueprint';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AppTheme } from '@backstage/core-plugin-api';
|
||||
import { AppTheme } from '../apis/definitions/AppThemeApi';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
|
||||
const themeDataRef = createExtensionDataRef<AppTheme>().with({
|
||||
|
||||
@@ -16,14 +16,11 @@
|
||||
|
||||
import { useEffect } from 'react';
|
||||
import { act, screen, waitFor } from '@testing-library/react';
|
||||
import {
|
||||
mockApis,
|
||||
TestApiProvider,
|
||||
withLogCollector,
|
||||
} from '@backstage/test-utils';
|
||||
import { TestApiProvider, withLogCollector } from '@backstage/test-utils';
|
||||
import { ExtensionBoundary } from './ExtensionBoundary';
|
||||
import { coreExtensionData, createExtension } from '../wiring';
|
||||
import { analyticsApiRef, useAnalytics } from '@backstage/core-plugin-api';
|
||||
import { analyticsApiRef } from '../apis/definitions/AnalyticsApi';
|
||||
import { useAnalytics } from '../analytics';
|
||||
import { createRouteRef } from '../routing';
|
||||
import {
|
||||
createExtensionTester,
|
||||
@@ -93,7 +90,7 @@ describe('ExtensionBoundary', () => {
|
||||
it('should wrap children with analytics context', async () => {
|
||||
const action = 'render';
|
||||
const subject = 'analytics';
|
||||
const analyticsApiMock = mockApis.analytics();
|
||||
const analyticsApiMock = { captureEvent: jest.fn() };
|
||||
|
||||
const AnalyticsComponent = () => {
|
||||
const analytics = useAnalytics();
|
||||
@@ -134,7 +131,7 @@ describe('ExtensionBoundary', () => {
|
||||
});
|
||||
return null;
|
||||
};
|
||||
const analyticsApiMock = mockApis.analytics();
|
||||
const analyticsApiMock = { captureEvent: jest.fn() };
|
||||
|
||||
await act(async () => {
|
||||
renderInTestApp(
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
useEffect,
|
||||
lazy as reactLazy,
|
||||
} from 'react';
|
||||
import { AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api';
|
||||
import { AnalyticsContext, useAnalytics } from '../analytics';
|
||||
import { ErrorBoundary } from './ErrorBoundary';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { routableExtensionRenderedEvent } from '../../../core-plugin-api/src/analytics/Tracker';
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('ExtensionSuspense', () => {
|
||||
),
|
||||
);
|
||||
|
||||
expect(screen.getByTestId('progress')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('core-progress')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render the lazy loaded children component', async () => {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
import { ReactNode, Suspense } from 'react';
|
||||
import { useApp } from '@backstage/core-plugin-api';
|
||||
import { Progress } from './DefaultSwappableComponents';
|
||||
|
||||
/** @public */
|
||||
export interface ExtensionSuspenseProps {
|
||||
@@ -26,8 +26,5 @@ export interface ExtensionSuspenseProps {
|
||||
export function ExtensionSuspense(props: ExtensionSuspenseProps) {
|
||||
const { children } = props;
|
||||
|
||||
const app = useApp();
|
||||
const { Progress } = app.getComponents();
|
||||
|
||||
return <Suspense fallback={<Progress />}>{children}</Suspense>;
|
||||
}
|
||||
|
||||
+4
-4
@@ -14,12 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { TranslationRef } from './TranslationRef';
|
||||
|
||||
/**
|
||||
* Represents a collection of messages to be provided for a given translation ref.
|
||||
*
|
||||
* @alpha
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* This collection of messages can either be used directly as an override for the
|
||||
@@ -43,7 +43,7 @@ export interface TranslationMessages<
|
||||
/**
|
||||
* Options for {@link createTranslationMessages}.
|
||||
*
|
||||
* @alpha
|
||||
* @public
|
||||
*/
|
||||
export interface TranslationMessagesOptions<
|
||||
TId extends string,
|
||||
@@ -62,7 +62,7 @@ export interface TranslationMessagesOptions<
|
||||
/**
|
||||
* Creates a collection of messages for a given translation ref.
|
||||
*
|
||||
* @alpha
|
||||
* @public
|
||||
*/
|
||||
export function createTranslationMessages<
|
||||
TId extends string,
|
||||
+3
-3
@@ -19,7 +19,7 @@ import {
|
||||
TranslationResource,
|
||||
} from './TranslationResource';
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export interface TranslationRef<
|
||||
TId extends string = string,
|
||||
TMessages extends { [key in string]: string } = { [key in string]: string },
|
||||
@@ -83,7 +83,7 @@ export interface InternalTranslationRef<
|
||||
getDefaultResource(): TranslationResource | undefined;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export interface TranslationRefOptions<
|
||||
TId extends string,
|
||||
TNestedMessages extends AnyNestedMessages,
|
||||
@@ -164,7 +164,7 @@ class TranslationRefImpl<
|
||||
}
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export function createTranslationRef<
|
||||
TId extends string,
|
||||
const TNestedMessages extends AnyNestedMessages,
|
||||
+5
-7
@@ -14,12 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
TranslationMessages,
|
||||
TranslationRef,
|
||||
} from '@backstage/core-plugin-api/alpha';
|
||||
import { TranslationMessages } from './TranslationMessages';
|
||||
import { TranslationRef } from './TranslationRef';
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export interface TranslationResource<TId extends string = string> {
|
||||
$$type: '@backstage/TranslationResource';
|
||||
id: TId;
|
||||
@@ -55,7 +53,7 @@ export function toInternalTranslationResource<TId extends string>(
|
||||
return r;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export interface TranslationResourceOptions<
|
||||
TId extends string,
|
||||
TMessages extends { [key in string]: string },
|
||||
@@ -72,7 +70,7 @@ export interface TranslationResourceOptions<
|
||||
translations: TTranslations;
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export function createTranslationResource<
|
||||
TId extends string,
|
||||
TMessages extends { [key in string]: string },
|
||||
@@ -17,12 +17,16 @@
|
||||
export {
|
||||
type TranslationMessages,
|
||||
type TranslationMessagesOptions,
|
||||
createTranslationMessages,
|
||||
} from './TranslationMessages';
|
||||
export {
|
||||
type TranslationResource,
|
||||
type TranslationResourceOptions,
|
||||
createTranslationResource,
|
||||
} from './TranslationResource';
|
||||
export {
|
||||
type TranslationRef,
|
||||
type TranslationRefOptions,
|
||||
createTranslationMessages,
|
||||
createTranslationResource,
|
||||
createTranslationRef,
|
||||
useTranslationRef,
|
||||
} from '@backstage/core-plugin-api/alpha';
|
||||
} from './TranslationRef';
|
||||
export { useTranslationRef } from './useTranslationRef';
|
||||
|
||||
+2
-2
@@ -27,11 +27,11 @@ import { useTranslationRef } from './useTranslationRef';
|
||||
import { I18nextTranslationApi } from '../../../core-app-api/src/apis/implementations/TranslationApi';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { AppLanguageSelector } from '../../..//core-app-api/src/apis/implementations/AppLanguageApi';
|
||||
import { createTranslationResource } from './TranslationResource';
|
||||
import {
|
||||
createTranslationResource,
|
||||
TranslationApi,
|
||||
translationApiRef,
|
||||
} from '../alpha';
|
||||
} from '../apis/definitions/TranslationApi';
|
||||
import { ErrorApi, errorApiRef } from '../apis';
|
||||
|
||||
const plainRef = createTranslationRef({
|
||||
+2
-2
@@ -20,13 +20,13 @@ import {
|
||||
translationApiRef,
|
||||
TranslationFunction,
|
||||
TranslationSnapshot,
|
||||
} from '../apis/alpha';
|
||||
} from '../apis/definitions/TranslationApi';
|
||||
import { TranslationRef } from './TranslationRef';
|
||||
|
||||
// Make sure we don't fill the logs with loading errors for the same ref
|
||||
const loggedRefs = new WeakSet<TranslationRef<string, {}>>();
|
||||
|
||||
/** @alpha */
|
||||
/** @public */
|
||||
export const useTranslationRef = <
|
||||
TMessages extends { [key in string]: string },
|
||||
>(
|
||||
@@ -25,7 +25,7 @@ import ObservableImpl from 'zen-observable';
|
||||
import { Observable } from '@backstage/types';
|
||||
// Internal import to avoid code duplication, this will lead to duplication in build output
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { toInternalTranslationRef } from '../../../../../core-plugin-api/src/translation/TranslationRef';
|
||||
import { toInternalTranslationRef } from '../../../../../frontend-plugin-api/src/translation/TranslationRef';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { JsxInterpolator } from '../../../../../core-app-api/src/apis/implementations/TranslationApi/I18nextTranslationApi';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user