Merge pull request #31849 from backstage/rugvip/reverse

reverse relationship between core-plugin-api and frontend-plugin-api
This commit is contained in:
Patrik Oldsberg
2025-11-24 14:46:01 +01:00
committed by GitHub
103 changed files with 2548 additions and 2506 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core-app-api': patch
'@backstage/test-utils': patch
---
Internal update of translation imports.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/frontend-plugin-api': patch
'@backstage/core-plugin-api': patch
---
Reversed the relationship between the old `@backstage/core-plugin-api` and the new `@backstage/frontend-plugin-api`. Previously, the a lot of API definitions and utilities where defined in the old and re-exported from the old, but this change flips that around so that they now reside in the new package and are re-exported from the old. The external API of both packages remain the same, but this is a step towards being able to add further compatibility with the new frontend system built into the old.
+35 -35
View File
@@ -18,23 +18,23 @@ plugins to communicate during their entire life cycle.
## Consuming APIs
Each Utility API is tied to an [`ApiRef`](../reference/core-plugin-api.apiref.md)
Each Utility API is tied to an [`ApiRef`](../reference/frontend-plugin-api.apiref.md)
instance, which is a global singleton object without any additional state or
functionality, its only purpose is to reference Utility APIs.
[`ApiRef`](../reference/core-plugin-api.apiref.md)s are created using
[`createApiRef`](../reference/core-plugin-api.createapiref.md), which is exported
[`ApiRef`](../reference/frontend-plugin-api.apiref.md)s are created using
[`createApiRef`](../reference/frontend-plugin-api.createapiref.md), which is exported
by [`@backstage/core-plugin-api`](../reference/core-plugin-api.md). There are also
many predefined Utility APIs in
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md), and they're all
exported with a name of the pattern `*ApiRef`, for example
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md).
[`errorApiRef`](../reference/frontend-plugin-api.errorapiref.md).
To access one of the Utility APIs inside a React component, use the
[`useApi`](../reference/core-plugin-api.useapi.md) hook exported by
[`useApi`](../reference/frontend-plugin-api.useapi.md) hook exported by
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md), or the
[`withApis`](../reference/core-plugin-api.withapis.md) HOC if you prefer class
[`withApis`](../reference/frontend-plugin-api.withapis.md) HOC if you prefer class
components. For example, the
[`ErrorApi`](../reference/core-plugin-api.errorapi.md) can be accessed like this:
[`ErrorApi`](../reference/frontend-plugin-api.errorapi.md) can be accessed like this:
```tsx
import { useApi, errorApiRef } from '@backstage/core-plugin-api';
@@ -52,9 +52,9 @@ export const MyComponent = () => {
```
Note that there is no explicit type given for
[`ErrorApi`](../reference/core-plugin-api.errorapi.md). This is because the
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md) has the type
embedded, and [`useApi`](../reference/core-plugin-api.useapi.md) is able to infer
[`ErrorApi`](../reference/frontend-plugin-api.errorapi.md). This is because the
[`errorApiRef`](../reference/frontend-plugin-api.errorapiref.md) has the type
embedded, and [`useApi`](../reference/frontend-plugin-api.useapi.md) is able to infer
the type.
Also note that consuming Utility APIs is not limited to plugins; it can be done
@@ -67,15 +67,15 @@ requirement is that they are beneath the `AppProvider` in the react tree.
### API Factories
APIs are registered in the form of
[`ApiFactory`](../reference/core-plugin-api.apifactory.md) instances, which encapsulate
[`ApiFactory`](../reference/frontend-plugin-api.apifactory.md) instances, which encapsulate
the process of instantiating an API. It is a collection of three things: the
[`ApiRef`](../reference/core-plugin-api.apiref.md) of the API to instantiate, a
[`ApiRef`](../reference/frontend-plugin-api.apiref.md) of the API to instantiate, a
list of all required dependencies, and a factory function that returns a new API
instance.
For example, this is the default
[`ApiFactory`](../reference/core-plugin-api.apifactory.md) for the
[`ErrorApi`](../reference/core-plugin-api.errorapi.md):
[`ApiFactory`](../reference/frontend-plugin-api.apifactory.md) for the
[`ErrorApi`](../reference/frontend-plugin-api.errorapi.md):
```ts
createApiFactory({
@@ -89,25 +89,25 @@ createApiFactory({
});
```
In this example, the [`errorApiRef`](../reference/core-plugin-api.errorapiref.md)
In this example, the [`errorApiRef`](../reference/frontend-plugin-api.errorapiref.md)
is our API, which encapsulates the
[`ErrorApi`](../reference/core-plugin-api.errorapi.md) type. The
[`alertApiRef`](../reference/core-plugin-api.alertapiref.md) is our single
[`ErrorApi`](../reference/frontend-plugin-api.errorapi.md) type. The
[`alertApiRef`](../reference/frontend-plugin-api.alertapiref.md) is our single
dependency, which we give the name `alertApi`, and is then passed on to the
factory function, which returns an implementation of the
[`ErrorApi`](../reference/core-plugin-api.errorapi.md).
[`ErrorApi`](../reference/frontend-plugin-api.errorapi.md).
The [`createApiFactory`](../reference/core-plugin-api.createapifactory.md)
The [`createApiFactory`](../reference/frontend-plugin-api.createapifactory.md)
function is a thin wrapper that enables TypeScript type inference. You may
notice that there are no type annotations in the above example, and that is
because we're able to infer all types from the
[`ApiRef`](../reference/core-plugin-api.apiref.md)s. TypeScript will make sure
[`ApiRef`](../reference/frontend-plugin-api.apiref.md)s. TypeScript will make sure
that the return value of the `factory` function matches the type embedded in
`api`'s [`ApiRef`](../reference/core-plugin-api.apiref.md), in this case the
[`ErrorApi`](../reference/core-plugin-api.errorapi.md). It will also match the
`api`'s [`ApiRef`](../reference/frontend-plugin-api.apiref.md), in this case the
[`ErrorApi`](../reference/frontend-plugin-api.errorapi.md). It will also match the
types between the `deps` and the parameters of the `factory` function, again
using the type embedded within the
[`ApiRef`](../reference/core-plugin-api.apiref.md)s.
[`ApiRef`](../reference/frontend-plugin-api.apiref.md)s.
## Registering API Factories
@@ -120,8 +120,8 @@ app, and the app itself.
Starting with the Backstage core library, it provides implementations for all of
the core APIs. The core APIs are the ones exported by
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md), such as the
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md) and
[`configApiRef`](../reference/core-plugin-api.configapiref.md).
[`errorApiRef`](../reference/frontend-plugin-api.errorapiref.md) and
[`configApiRef`](../reference/frontend-plugin-api.configapiref.md).
The core APIs are loaded for any app created with
[`createApp`](../reference/app-defaults.createapp.md) from
@@ -133,7 +133,7 @@ there is no step that needs to be taken to include these APIs in an app.
In addition to the core APIs, plugins can define and export their own APIs.
While doing so, they should usually also provide default implementations of their
own APIs; for example, the `catalog` plugin exports `catalogApiRef` and also
supplies a default [`ApiFactory`](../reference/core-plugin-api.apifactory.md) of
supplies a default [`ApiFactory`](../reference/frontend-plugin-api.apifactory.md) of
that API using the `CatalogClient`. There is one restriction to plugin-provided
API Factories: plugins may not supply factories for core APIs; trying to do so
will cause the app to refuse to start.
@@ -227,16 +227,16 @@ const app = createApp({
```
Note that the above line will cause an error if `IgnoreErrorApi` does not fully
implement the [`ErrorApi`](../reference/core-plugin-api.errorapi.md), as it is
implement the [`ErrorApi`](../reference/frontend-plugin-api.errorapi.md), as it is
checked by the type embedded in the
[`errorApiRef`](../reference/core-plugin-api.errorapiref.md) at compile time.
[`errorApiRef`](../reference/frontend-plugin-api.errorapiref.md) at compile time.
## Defining custom Utility APIs
Plugins are free to define their own Utility APIs. Simply define the TypeScript
interface for the API and create an
[`ApiRef`](../reference/core-plugin-api.apiref.md) using
[`createApiRef`](../reference/core-plugin-api.createapiref.md) exported from
[`ApiRef`](../reference/frontend-plugin-api.apiref.md) using
[`createApiRef`](../reference/frontend-plugin-api.createapiref.md) exported from
[`@backstage/core-plugin-api`](../reference/core-plugin-api.md). Also, be sure to
provide at least one implementation of the API and to declare a default factory
for the API in [`createPlugin`](../reference/core-plugin-api.createplugin.md).
@@ -244,16 +244,16 @@ for the API in [`createPlugin`](../reference/core-plugin-api.createplugin.md).
Custom Utility APIs can be either public or private, which is up to the plugin to choose. Private APIs do not expose an external API surface, and it's therefore possible to make breaking changes to the API without affecting other users of the plugin. If an API is made public, however, it opens up for other plugins to make use of the API, and it also makes it possible for users for your plugin to override the API in the app. It is, however, important to maintain backward compatibility of public APIs, as you may otherwise break apps that are using your plugin.
To make an API public, simply export the
[`ApiRef`](../reference/core-plugin-api.apiref.md) of the API, and any associated
[`ApiRef`](../reference/frontend-plugin-api.apiref.md) of the API, and any associated
types. To make an API private, just avoid exporting the
[`ApiRef`](../reference/core-plugin-api.apiref.md), but still be sure to supply a
[`ApiRef`](../reference/frontend-plugin-api.apiref.md), but still be sure to supply a
default factory to [`createPlugin`](../reference/core-plugin-api.createplugin.md).
Private APIs are useful for plugins that want to depend on other APIs outside of
React components, but not have to expose an entire API surface to maintain. When
using private APIs, it is fine to use the `typeof` of an implementing class as
the type parameter passed to
[`createApiRef`](../reference/core-plugin-api.createapiref.md), while public APIs
[`createApiRef`](../reference/frontend-plugin-api.createapiref.md), while public APIs
should always define a separate TypeScript interface type.
Plugins may depend on APIs from other plugins, both in React components and as
@@ -262,13 +262,13 @@ dependencies between plugins.
## Architecture
The [`ApiRef`](../reference/core-plugin-api.apiref.md) instances mentioned above
The [`ApiRef`](../reference/frontend-plugin-api.apiref.md) instances mentioned above
provide a point of indirection between consumers and producers of Utility APIs.
It allows for plugins and components to depend on APIs in a type-safe way,
without having a direct reference to a concrete implementation of the APIs. The
Apps are also given a lot of flexibility in what implementations to provide. As
long as they adhere to the contract established by an
[`ApiRef`](../reference/core-plugin-api.apiref.md), they are free to choose any
[`ApiRef`](../reference/frontend-plugin-api.apiref.md), they are free to choose any
implementation they want.
The figure below shows the relationship between
+1 -1
View File
@@ -115,7 +115,7 @@ example `getString`. These will throw an error if there is no value available.
## Accessing ConfigApi in Frontend Plugins
The [ConfigApi](../reference/core-plugin-api.configapi.md) in the frontend is a
The [ConfigApi](../reference/frontend-plugin-api.configapi.md) in the frontend is a
[UtilityApi](../api/utility-apis.md). It's accessible as usual via the
`configApiRef` exported from `@backstage/core-plugin-api`:
@@ -94,15 +94,15 @@ export const AwesomeUsersTable = () => {
This section describes the steps to wrap your API client in a [Utility API](../api/utility-apis.md), which are:
- use [`createApiRef`](../reference/core-plugin-api.createapiref.md) to create a
new [`ApiRef`](../reference/core-plugin-api.apiref.md)
- register an [`ApiFactory`](../reference/core-plugin-api.apifactory.md) with
- use [`createApiRef`](../reference/frontend-plugin-api.createapiref.md) to create a
new [`ApiRef`](../reference/frontend-plugin-api.apiref.md)
- register an [`ApiFactory`](../reference/frontend-plugin-api.apifactory.md) with
your plugin using
[`createApiFactory`](../reference/core-plugin-api.createapifactory.md). This
[`createApiFactory`](../reference/frontend-plugin-api.createapifactory.md). This
will wrap your API implementation, associate your `ApiRef` with your
implementation and tell backstage how to instantiate it
- finally, you can use your API in your components by calling
[`useApi`](../reference/core-plugin-api.useapi.md)
[`useApi`](../reference/frontend-plugin-api.useapi.md)
### Defining the API client interface
@@ -187,8 +187,8 @@ export class MyAwesomeApiClient implements MyAwesomeApi {
```
> Check out the docs for more information on the
> [DiscoveryApi](../reference/core-plugin-api.discoveryapi.md) or the
> [FetchApi](../reference/core-plugin-api.fetchapi.md)
> [DiscoveryApi](../reference/frontend-plugin-api.discoveryapi.md) or the
> [FetchApi](../reference/frontend-plugin-api.fetchapi.md)
### Bundling your ApiRef with your plugin
@@ -233,7 +233,7 @@ export const myCustomPlugin = createPlugin({
### Using the API in your components
Now you should be able to access your API using the backstage hook
[`useApi`](../reference/core-plugin-api.useapi.md) from within your plugin code.
[`useApi`](../reference/frontend-plugin-api.useapi.md) from within your plugin code.
```ts title="plugins/my-awesome-plugin/src/components/AwesomeUsersTable.tsx"
import { useApi } from '@backstage/core-plugin-api';
@@ -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';
+1 -1
View File
@@ -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",
+32 -231
View File
@@ -3,253 +3,54 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { ApiRef } from '@backstage/core-plugin-api';
import { Expand } from '@backstage/types';
import { ExpandRecursive } from '@backstage/types';
import { JSX as JSX_2 } from 'react';
import { Observable } from '@backstage/types';
import { TranslationMessages as TranslationMessages_2 } from '@backstage/core-plugin-api/alpha';
import { TranslationRef as TranslationRef_2 } from '@backstage/core-plugin-api/alpha';
import { AppLanguageApi } from '@backstage/frontend-plugin-api';
import { appLanguageApiRef } from '@backstage/frontend-plugin-api';
import { createTranslationMessages } from '@backstage/frontend-plugin-api';
import { createTranslationRef } from '@backstage/frontend-plugin-api';
import { createTranslationResource } from '@backstage/frontend-plugin-api';
import { TranslationApi } from '@backstage/frontend-plugin-api';
import { translationApiRef } from '@backstage/frontend-plugin-api';
import { TranslationFunction } from '@backstage/frontend-plugin-api';
import { TranslationMessages } from '@backstage/frontend-plugin-api';
import { TranslationMessagesOptions } from '@backstage/frontend-plugin-api';
import { TranslationRef } from '@backstage/frontend-plugin-api';
import { TranslationRefOptions } from '@backstage/frontend-plugin-api';
import { TranslationResource } from '@backstage/frontend-plugin-api';
import { TranslationResourceOptions } from '@backstage/frontend-plugin-api';
import { TranslationSnapshot } from '@backstage/frontend-plugin-api';
import { useTranslationRef } from '@backstage/frontend-plugin-api';
// @alpha (undocumented)
export type AppLanguageApi = {
getAvailableLanguages(): {
languages: string[];
};
setLanguage(language?: string): void;
getLanguage(): {
language: string;
};
language$(): Observable<{
language: string;
}>;
};
export { AppLanguageApi };
// @alpha (undocumented)
export const appLanguageApiRef: ApiRef<AppLanguageApi>;
export { appLanguageApiRef };
// @alpha
export function createTranslationMessages<
TId extends string,
TMessages extends {
[key in string]: string;
},
TFull extends boolean,
>(
options: TranslationMessagesOptions<TId, TMessages, TFull>,
): TranslationMessages<TId, TMessages, TFull>;
export { createTranslationMessages };
// @alpha (undocumented)
export function createTranslationRef<
TId extends string,
const TNestedMessages extends AnyNestedMessages,
TTranslations extends {
[language in string]: () => Promise<{
default: {
[key in keyof FlattenedMessages<TNestedMessages>]: string | null;
};
}>;
},
>(
config: TranslationRefOptions<TId, TNestedMessages, TTranslations>,
): TranslationRef<TId, FlattenedMessages<TNestedMessages>>;
export { createTranslationRef };
// @alpha (undocumented)
export function createTranslationResource<
TId extends string,
TMessages extends {
[key in string]: string;
},
TTranslations extends {
[language in string]: () => Promise<{
default:
| TranslationMessages_2<TId>
| {
[key in keyof TMessages]: string | null;
};
}>;
},
>(
options: TranslationResourceOptions<TId, TMessages, TTranslations>,
): TranslationResource<TId>;
export { createTranslationResource };
// @alpha (undocumented)
export type TranslationApi = {
getTranslation<
TMessages extends {
[key in string]: string;
},
>(
translationRef: TranslationRef<string, TMessages>,
): TranslationSnapshot<TMessages>;
translation$<
TMessages extends {
[key in string]: string;
},
>(
translationRef: TranslationRef<string, TMessages>,
): Observable<TranslationSnapshot<TMessages>>;
};
export { TranslationApi };
// @alpha (undocumented)
export const translationApiRef: ApiRef<TranslationApi>;
export { translationApiRef };
// @alpha (undocumented)
export type TranslationFunction<
TMessages extends {
[key in string]: string;
},
> = CollapsedMessages<TMessages> extends infer IMessages extends {
[key in string]: string;
}
? {
<TKey extends keyof IMessages>(
key: TKey,
...[args]: TranslationFunctionOptions<
NestedMessageKeys<TKey, IMessages>,
PluralKeys<TMessages>,
IMessages,
string
>
): IMessages[TKey];
<TKey extends keyof IMessages>(
key: TKey,
...[args]: TranslationFunctionOptions<
NestedMessageKeys<TKey, IMessages>,
PluralKeys<TMessages>,
IMessages,
string | JSX_2.Element
>
): JSX_2.Element;
}
: never;
export { TranslationFunction };
// @alpha
export interface TranslationMessages<
TId extends string = string,
TMessages extends {
[key in string]: string;
} = {
[key in string]: string;
},
TFull extends boolean = boolean,
> {
// (undocumented)
$$type: '@backstage/TranslationMessages';
full: TFull;
id: TId;
messages: TMessages;
}
export { TranslationMessages };
// @alpha
export interface TranslationMessagesOptions<
TId extends string,
TMessages extends {
[key in string]: string;
},
TFull extends boolean,
> {
// (undocumented)
full?: TFull;
// (undocumented)
messages: false extends TFull
? {
[key in keyof TMessages]?: string | null;
}
: {
[key in keyof TMessages]: string | null;
};
// (undocumented)
ref: TranslationRef_2<TId, TMessages>;
}
export { TranslationMessagesOptions };
// @alpha (undocumented)
export interface TranslationRef<
TId extends string = string,
TMessages extends {
[key in string]: string;
} = {
[key in string]: string;
},
> {
// (undocumented)
$$type: '@backstage/TranslationRef';
// (undocumented)
id: TId;
// (undocumented)
T: TMessages;
}
export { TranslationRef };
// @alpha (undocumented)
export interface TranslationRefOptions<
TId extends string,
TNestedMessages extends AnyNestedMessages,
TTranslations extends {
[language in string]: () => Promise<{
default: {
[key in keyof FlattenedMessages<TNestedMessages>]: string | null;
};
}>;
},
> {
// (undocumented)
id: TId;
// (undocumented)
messages: TNestedMessages;
// (undocumented)
translations?: TTranslations;
}
export { TranslationRefOptions };
// @alpha (undocumented)
export interface TranslationResource<TId extends string = string> {
// (undocumented)
$$type: '@backstage/TranslationResource';
// (undocumented)
id: TId;
}
export { TranslationResource };
// @alpha (undocumented)
export interface TranslationResourceOptions<
TId extends string,
TMessages extends {
[key in string]: string;
},
TTranslations extends {
[language in string]: () => Promise<{
default:
| TranslationMessages_2<TId>
| {
[key in keyof TMessages]: string | null;
};
}>;
},
> {
// (undocumented)
ref: TranslationRef_2<TId, TMessages>;
// (undocumented)
translations: TTranslations;
}
export { TranslationResourceOptions };
// @alpha (undocumented)
export type TranslationSnapshot<
TMessages extends {
[key in string]: string;
},
> =
| {
ready: false;
}
| {
ready: true;
t: TranslationFunction<TMessages>;
};
export { TranslationSnapshot };
// @alpha (undocumented)
export const useTranslationRef: <TMessages extends { [key in string]: string }>(
translationRef: TranslationRef<string, TMessages>,
) => {
t: TranslationFunction<TMessages>;
};
export { useTranslationRef };
// (No @packageDocumentation comment for this package)
```
+134 -380
View File
@@ -3,33 +3,87 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AlertApi } from '@backstage/frontend-plugin-api';
import { alertApiRef } from '@backstage/frontend-plugin-api';
import { AlertMessage } from '@backstage/frontend-plugin-api';
import { AnyApiFactory } from '@backstage/frontend-plugin-api';
import { AnyApiRef } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ApiHolder } from '@backstage/frontend-plugin-api';
import { ApiRef } from '@backstage/frontend-plugin-api';
import { ApiRefConfig } from '@backstage/frontend-plugin-api';
import { AppTheme } from '@backstage/frontend-plugin-api';
import { AppThemeApi } from '@backstage/frontend-plugin-api';
import { appThemeApiRef } from '@backstage/frontend-plugin-api';
import { atlassianAuthApiRef } from '@backstage/frontend-plugin-api';
import { AuthProviderInfo } from '@backstage/frontend-plugin-api';
import { AuthRequestOptions } from '@backstage/frontend-plugin-api';
import { BackstageIdentityApi } from '@backstage/frontend-plugin-api';
import { BackstageIdentityResponse } from '@backstage/frontend-plugin-api';
import { BackstagePlugin as BackstagePlugin_2 } from '@backstage/core-plugin-api';
import { BackstageUserIdentity } from '@backstage/frontend-plugin-api';
import { bitbucketAuthApiRef } from '@backstage/frontend-plugin-api';
import { bitbucketServerAuthApiRef } from '@backstage/frontend-plugin-api';
import { ComponentType } from 'react';
import { Config } from '@backstage/config';
import { ConfigApi } from '@backstage/frontend-plugin-api';
import { configApiRef } from '@backstage/frontend-plugin-api';
import { createApiFactory } from '@backstage/frontend-plugin-api';
import { createApiRef } from '@backstage/frontend-plugin-api';
import { DiscoveryApi } from '@backstage/frontend-plugin-api';
import { discoveryApiRef } from '@backstage/frontend-plugin-api';
import { ErrorApi } from '@backstage/frontend-plugin-api';
import { ErrorApiError } from '@backstage/frontend-plugin-api';
import { ErrorApiErrorContext } from '@backstage/frontend-plugin-api';
import { errorApiRef } from '@backstage/frontend-plugin-api';
import { FeatureFlag } from '@backstage/frontend-plugin-api';
import { FeatureFlagsApi } from '@backstage/frontend-plugin-api';
import { featureFlagsApiRef } from '@backstage/frontend-plugin-api';
import { FeatureFlagsSaveOptions } from '@backstage/frontend-plugin-api';
import { FeatureFlagState } from '@backstage/frontend-plugin-api';
import { FetchApi } from '@backstage/frontend-plugin-api';
import { fetchApiRef } from '@backstage/frontend-plugin-api';
import { githubAuthApiRef } from '@backstage/frontend-plugin-api';
import { gitlabAuthApiRef } from '@backstage/frontend-plugin-api';
import { googleAuthApiRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { IconComponent as IconComponent_2 } from '@backstage/core-plugin-api';
import { IdentityApi } from '@backstage/frontend-plugin-api';
import { IdentityApi as IdentityApi_2 } from '@backstage/core-plugin-api';
import { JsonValue } from '@backstage/types';
import { identityApiRef } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import { Observable } from '@backstage/types';
import { microsoftAuthApiRef } from '@backstage/frontend-plugin-api';
import { OAuthApi } from '@backstage/frontend-plugin-api';
import { OAuthRequestApi } from '@backstage/frontend-plugin-api';
import { oauthRequestApiRef } from '@backstage/frontend-plugin-api';
import { OAuthRequester } from '@backstage/frontend-plugin-api';
import { OAuthRequesterOptions } from '@backstage/frontend-plugin-api';
import { OAuthScope } from '@backstage/frontend-plugin-api';
import { oktaAuthApiRef } from '@backstage/frontend-plugin-api';
import { oneloginAuthApiRef } from '@backstage/frontend-plugin-api';
import { OpenIdConnectApi } from '@backstage/frontend-plugin-api';
import { openshiftAuthApiRef } from '@backstage/frontend-plugin-api';
import { PendingOAuthRequest } from '@backstage/frontend-plugin-api';
import { ProfileInfo } from '@backstage/frontend-plugin-api';
import { ProfileInfoApi } from '@backstage/frontend-plugin-api';
import { PropsWithChildren } from 'react';
import { ReactElement } from 'react';
import { ReactNode } from 'react';
import { SessionApi } from '@backstage/frontend-plugin-api';
import { SessionState } from '@backstage/frontend-plugin-api';
import { StorageApi } from '@backstage/frontend-plugin-api';
import { storageApiRef } from '@backstage/frontend-plugin-api';
import { StorageValueSnapshot } from '@backstage/frontend-plugin-api';
import { TypesToApiRefs } from '@backstage/frontend-plugin-api';
import { useApi } from '@backstage/frontend-plugin-api';
import { useApiHolder } from '@backstage/frontend-plugin-api';
import { vmwareCloudAuthApiRef } from '@backstage/frontend-plugin-api';
import { withApis } from '@backstage/frontend-plugin-api';
// @public
export type AlertApi = {
post(alert: AlertMessage): void;
alert$(): Observable<AlertMessage>;
};
export { AlertApi };
// @public
export const alertApiRef: ApiRef<AlertApi>;
export { alertApiRef };
// @public
export type AlertMessage = {
message: string;
severity?: 'success' | 'info' | 'warning' | 'error';
display?: 'permanent' | 'transient';
};
export { AlertMessage };
// @public
export type AnalyticsApi = {
@@ -76,17 +130,9 @@ export type AnalyticsTracker = {
) => void;
};
// @public
export type AnyApiFactory = ApiFactory<
unknown,
unknown,
{
[key in string]: unknown;
}
>;
export { AnyApiFactory };
// @public
export type AnyApiRef = ApiRef<unknown>;
export { AnyApiRef };
// @public
export type AnyExternalRoutes = {
@@ -108,34 +154,13 @@ export type AnyRoutes = {
[name: string]: RouteRef | SubRouteRef;
};
// @public
export type ApiFactory<
Api,
Impl extends Api,
Deps extends {
[name in string]: unknown;
},
> = {
api: ApiRef<Api>;
deps: TypesToApiRefs<Deps>;
factory(deps: Deps): Impl;
};
export { ApiFactory };
// @public
export type ApiHolder = {
get<T>(api: ApiRef<T>): T | undefined;
};
export { ApiHolder };
// @public
export type ApiRef<T> = {
id: string;
T: T;
};
export { ApiRef };
// @public
export type ApiRefConfig = {
id: string;
};
export { ApiRefConfig };
// @public
export type AppComponents = {
@@ -160,30 +185,13 @@ export type AppContext = {
getComponents(): AppComponents;
};
// @public
export type AppTheme = {
id: string;
title: string;
variant: 'light' | 'dark';
icon?: React.ReactElement;
Provider(props: { children: ReactNode }): JSX.Element | null;
};
export { AppTheme };
// @public
export type AppThemeApi = {
getInstalledThemes(): AppTheme[];
activeThemeId$(): Observable<string | undefined>;
getActiveThemeId(): string | undefined;
setActiveThemeId(themeId?: string): void;
};
export { AppThemeApi };
// @public
export const appThemeApiRef: ApiRef<AppThemeApi>;
export { appThemeApiRef };
// @public
export const atlassianAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { atlassianAuthApiRef };
// @public
export function attachComponentData<P>(
@@ -192,33 +200,13 @@ export function attachComponentData<P>(
data: unknown,
): void;
// @public
export type AuthProviderInfo = {
id: string;
title: string;
icon: IconComponent;
message?: string;
};
export { AuthProviderInfo };
// @public
export type AuthRequestOptions = {
optional?: boolean;
instantPopup?: boolean;
};
export { AuthRequestOptions };
// @public
export type BackstageIdentityApi = {
getBackstageIdentity(
options?: AuthRequestOptions,
): Promise<BackstageIdentityResponse | undefined>;
};
export { BackstageIdentityApi };
// @public
export type BackstageIdentityResponse = {
token: string;
expiresAt?: Date;
identity: BackstageUserIdentity;
};
export { BackstageIdentityResponse };
// @public
export type BackstagePlugin<
@@ -234,22 +222,11 @@ export type BackstagePlugin<
externalRoutes: ExternalRoutes;
};
// @public
export type BackstageUserIdentity = {
type: 'user';
userEntityRef: string;
ownershipEntityRefs: string[];
};
export { BackstageUserIdentity };
// @public
export const bitbucketAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { bitbucketAuthApiRef };
// @public
export const bitbucketServerAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { bitbucketServerAuthApiRef };
// @public
export type BootErrorPageProps = PropsWithChildren<{
@@ -273,29 +250,13 @@ export type ComponentLoader<T> =
sync: T;
};
// @public
export type ConfigApi = Config;
export { ConfigApi };
// @public
export const configApiRef: ApiRef<ConfigApi>;
export { configApiRef };
// @public
export function createApiFactory<
Api,
Impl extends Api,
Deps extends {
[name in string]: unknown;
},
>(factory: ApiFactory<Api, Impl, Deps>): ApiFactory<Api, Impl, Deps>;
export { createApiFactory };
// @public
export function createApiFactory<Api, Impl extends Api>(
api: ApiRef<Api>,
instance: Impl,
): ApiFactory<Api, Impl, {}>;
// @public
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T>;
export { createApiRef };
// @public
export function createComponentExtension<
@@ -363,13 +324,9 @@ export function createSubRouteRef<
parent: RouteRef<ParentParams>;
}): MakeSubRouteRef<PathParams<Path>, ParentParams>;
// @public
export type DiscoveryApi = {
getBaseUrl(pluginId: string): Promise<string>;
};
export { DiscoveryApi };
// @public
export const discoveryApiRef: ApiRef<DiscoveryApi>;
export { discoveryApiRef };
// @public
export interface ElementCollection {
@@ -385,29 +342,13 @@ export interface ElementCollection {
}): ElementCollection;
}
// @public
export type ErrorApi = {
post(error: ErrorApiError, context?: ErrorApiErrorContext): void;
error$(): Observable<{
error: ErrorApiError;
context?: ErrorApiErrorContext;
}>;
};
export { ErrorApi };
// @public
export type ErrorApiError = {
name: string;
message: string;
stack?: string;
};
export { ErrorApiError };
// @public
export type ErrorApiErrorContext = {
hidden?: boolean;
};
export { ErrorApiErrorContext };
// @public
export const errorApiRef: ApiRef<ErrorApi>;
export { errorApiRef };
// @public
export type ErrorBoundaryFallbackProps = PropsWithChildren<{
@@ -433,60 +374,24 @@ export type ExternalRouteRef<
readonly T: Params;
};
// @public
export type FeatureFlag = {
name: string;
pluginId: string;
description?: string;
};
export { FeatureFlag };
// @public
export interface FeatureFlagsApi {
getRegisteredFlags(): FeatureFlag[];
isActive(name: string): boolean;
registerFlag(flag: FeatureFlag): void;
save(options: FeatureFlagsSaveOptions): void;
}
export { FeatureFlagsApi };
// @public
export const featureFlagsApiRef: ApiRef<FeatureFlagsApi>;
export { featureFlagsApiRef };
// @public
export type FeatureFlagsHooks = {
register(name: string): void;
};
// @public
export type FeatureFlagsSaveOptions = {
states: Record<string, FeatureFlagState>;
merge?: boolean;
};
export { FeatureFlagsSaveOptions };
// @public
export const FeatureFlagState: {
readonly None: 0;
readonly Active: 1;
};
export { FeatureFlagState };
// @public (undocumented)
export type FeatureFlagState =
(typeof FeatureFlagState)[keyof typeof FeatureFlagState];
export { FetchApi };
// @public (undocumented)
export namespace FeatureFlagState {
// (undocumented)
export type Active = typeof FeatureFlagState.Active;
// (undocumented)
export type None = typeof FeatureFlagState.None;
}
// @public
export type FetchApi = {
fetch: typeof fetch;
};
// @public
export const fetchApiRef: ApiRef<FetchApi>;
export { fetchApiRef };
// @public
export function getComponentData<T>(
@@ -494,46 +399,17 @@ export function getComponentData<T>(
type: string,
): T | undefined;
// @public
export const githubAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { githubAuthApiRef };
// @public
export const gitlabAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { gitlabAuthApiRef };
// @public
export const googleAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { googleAuthApiRef };
// @public
export type IconComponent = ComponentType<{
fontSize?: 'medium' | 'large' | 'small' | 'inherit';
}>;
export { IconComponent };
// @public
export type IdentityApi = {
getProfileInfo(): Promise<ProfileInfo>;
getBackstageIdentity(): Promise<BackstageUserIdentity>;
getCredentials(): Promise<{
token?: string;
}>;
signOut(): Promise<void>;
};
export { IdentityApi };
// @public
export const identityApiRef: ApiRef<IdentityApi>;
export { identityApiRef };
// @public @deprecated
export type MakeSubRouteRef<
@@ -553,75 +429,27 @@ export type MergeParams<
P2 extends AnyParams,
> = (P1[keyof P1] extends never ? {} : P1) & (P2 extends undefined ? {} : P2);
// @public
export const microsoftAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { microsoftAuthApiRef };
// @public
export type OAuthApi = {
getAccessToken(
scope?: OAuthScope,
options?: AuthRequestOptions,
): Promise<string>;
};
export { OAuthApi };
// @public
export type OAuthRequestApi = {
createAuthRequester<OAuthResponse>(
options: OAuthRequesterOptions<OAuthResponse>,
): OAuthRequester<OAuthResponse>;
authRequest$(): Observable<PendingOAuthRequest[]>;
};
export { OAuthRequestApi };
// @public
export const oauthRequestApiRef: ApiRef<OAuthRequestApi>;
export { oauthRequestApiRef };
// @public
export type OAuthRequester<TAuthResponse> = (
scopes: Set<string>,
) => Promise<TAuthResponse>;
export { OAuthRequester };
// @public
export type OAuthRequesterOptions<TOAuthResponse> = {
provider: AuthProviderInfo;
onAuthRequest(scopes: Set<string>): Promise<TOAuthResponse>;
};
export { OAuthRequesterOptions };
// @public
export type OAuthScope = string | string[];
export { OAuthScope };
// @public
export const oktaAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { oktaAuthApiRef };
// @public
export const oneloginAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { oneloginAuthApiRef };
// @public
export type OpenIdConnectApi = {
getIdToken(options?: AuthRequestOptions): Promise<string>;
};
export { OpenIdConnectApi };
// @public
export const openshiftAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { openshiftAuthApiRef };
// @public @deprecated
export type OptionalParams<
@@ -655,12 +483,7 @@ export type PathParams<S extends string> = {
[name in ParamNames<S>]: string;
};
// @public
export type PendingOAuthRequest = {
provider: AuthProviderInfo;
reject(): void;
trigger(): Promise<void>;
};
export { PendingOAuthRequest };
// @public
export type PluginConfig<
@@ -679,17 +502,9 @@ export type PluginFeatureFlagConfig = {
name: string;
};
// @public
export type ProfileInfo = {
email?: string;
displayName?: string;
picture?: string;
};
export { ProfileInfo };
// @public
export type ProfileInfoApi = {
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
};
export { ProfileInfoApi };
// @public
export type RouteFunc<Params extends AnyParams> = (
@@ -704,61 +519,20 @@ export type RouteRef<Params extends AnyParams = any> = {
readonly T: Params;
};
// @public
export type SessionApi = {
signIn(): Promise<void>;
signOut(): Promise<void>;
sessionState$(): Observable<SessionState>;
};
export { SessionApi };
// @public
export const SessionState: {
readonly SignedIn: 'SignedIn';
readonly SignedOut: 'SignedOut';
};
// @public (undocumented)
export type SessionState = (typeof SessionState)[keyof typeof SessionState];
// @public (undocumented)
export namespace SessionState {
// (undocumented)
export type SignedIn = typeof SessionState.SignedIn;
// (undocumented)
export type SignedOut = typeof SessionState.SignedOut;
}
export { SessionState };
// @public
export type SignInPageProps = PropsWithChildren<{
onSignInSuccess(identityApi: IdentityApi_2): void;
}>;
// @public
export interface StorageApi {
forBucket(name: string): StorageApi;
observe$<T extends JsonValue>(
key: string,
): Observable<StorageValueSnapshot<T>>;
remove(key: string): Promise<void>;
set<T extends JsonValue>(key: string, data: T): Promise<void>;
snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T>;
}
export { StorageApi };
// @public
export const storageApiRef: ApiRef<StorageApi>;
export { storageApiRef };
// @public
export type StorageValueSnapshot<TValue extends JsonValue> =
| {
key: string;
presence: 'unknown' | 'absent';
value?: undefined;
}
| {
key: string;
presence: 'present';
value: TValue;
};
export { StorageValueSnapshot };
// @public
export type SubRouteRef<Params extends AnyParams = any> = {
@@ -770,19 +544,14 @@ export type SubRouteRef<Params extends AnyParams = any> = {
readonly T: Params;
};
// @public
export type TypesToApiRefs<T> = {
[key in keyof T]: ApiRef<T[key]>;
};
export { TypesToApiRefs };
// @public
export function useAnalytics(): AnalyticsTracker;
// @public
export function useApi<T>(apiRef: ApiRef<T>): T;
export { useApi };
// @public
export function useApiHolder(): ApiHolder;
export { useApiHolder };
// @public
export const useApp: () => AppContext;
@@ -809,22 +578,7 @@ export function useRouteRefParams<Params extends AnyParams>(
_routeRef: RouteRef<Params> | SubRouteRef<Params>,
): Params;
// @public
export const vmwareCloudAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { vmwareCloudAuthApiRef };
// @public
export function withApis<T extends {}>(
apis: TypesToApiRefs<T>,
): <TProps extends T>(
WrappedComponent: ComponentType<TProps>,
) => {
(props: PropsWithChildren<Omit<TProps, keyof T>>): JSX_2.Element;
displayName: string;
};
export { withApis };
```
+23 -2
View File
@@ -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';
+1 -21
View File
@@ -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';
+14 -4
View File
@@ -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",
+619 -159
View File
@@ -3,103 +3,40 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { AlertApi } from '@backstage/core-plugin-api';
import { alertApiRef } from '@backstage/core-plugin-api';
import { AlertMessage } from '@backstage/core-plugin-api';
import { AnyApiFactory } from '@backstage/core-plugin-api';
import { AnyApiRef } from '@backstage/core-plugin-api';
import { AnyRouteRefParams as AnyRouteRefParams_2 } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/core-plugin-api';
import { ApiHolder } from '@backstage/core-plugin-api';
import { ApiRef } from '@backstage/core-plugin-api';
import { ApiRefConfig } from '@backstage/core-plugin-api';
import { AppTheme } from '@backstage/core-plugin-api';
import { AppThemeApi } from '@backstage/core-plugin-api';
import { appThemeApiRef } from '@backstage/core-plugin-api';
import { atlassianAuthApiRef } from '@backstage/core-plugin-api';
import { AuthProviderInfo } from '@backstage/core-plugin-api';
import { AuthRequestOptions } from '@backstage/core-plugin-api';
import { BackstageIdentityApi } from '@backstage/core-plugin-api';
import { BackstageIdentityResponse } from '@backstage/core-plugin-api';
import { BackstageUserIdentity } from '@backstage/core-plugin-api';
import { bitbucketAuthApiRef } from '@backstage/core-plugin-api';
import { bitbucketServerAuthApiRef } from '@backstage/core-plugin-api';
import { ComponentType } from 'react';
import { ConfigApi } from '@backstage/core-plugin-api';
import { configApiRef } from '@backstage/core-plugin-api';
import { Config } from '@backstage/config';
import { ConfigurableExtensionDataRef as ConfigurableExtensionDataRef_2 } from '@backstage/frontend-plugin-api';
import { createApiFactory } from '@backstage/core-plugin-api';
import { createApiRef } from '@backstage/core-plugin-api';
import { createTranslationMessages } from '@backstage/core-plugin-api/alpha';
import { createTranslationRef } from '@backstage/core-plugin-api/alpha';
import { createTranslationResource } from '@backstage/core-plugin-api/alpha';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { discoveryApiRef } from '@backstage/core-plugin-api';
import { ErrorApi } from '@backstage/core-plugin-api';
import { ErrorApiError } from '@backstage/core-plugin-api';
import { ErrorApiErrorContext } from '@backstage/core-plugin-api';
import { errorApiRef } from '@backstage/core-plugin-api';
import { Expand } from '@backstage/types';
import { ExpandRecursive } from '@backstage/types';
import { ExtensionBlueprint as ExtensionBlueprint_2 } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef as ExtensionDataRef_2 } from '@backstage/frontend-plugin-api';
import { FeatureFlag } from '@backstage/core-plugin-api';
import { FeatureFlagsApi } from '@backstage/core-plugin-api';
import { featureFlagsApiRef } from '@backstage/core-plugin-api';
import { FeatureFlagsSaveOptions } from '@backstage/core-plugin-api';
import { FeatureFlagState } from '@backstage/core-plugin-api';
import { FetchApi } from '@backstage/core-plugin-api';
import { fetchApiRef } from '@backstage/core-plugin-api';
import { githubAuthApiRef } from '@backstage/core-plugin-api';
import { gitlabAuthApiRef } from '@backstage/core-plugin-api';
import { googleAuthApiRef } from '@backstage/core-plugin-api';
import { IconComponent as IconComponent_2 } from '@backstage/frontend-plugin-api';
import { IconComponent as IconComponent_3 } from '@backstage/core-plugin-api';
import { IdentityApi } from '@backstage/core-plugin-api';
import { identityApiRef } from '@backstage/core-plugin-api';
import { JsonObject } from '@backstage/types';
import { JsonValue } from '@backstage/types';
import { JSX as JSX_2 } from 'react/jsx-runtime';
import { JSX as JSX_3 } from 'react';
import { microsoftAuthApiRef } from '@backstage/core-plugin-api';
import { OAuthApi } from '@backstage/core-plugin-api';
import { OAuthRequestApi } from '@backstage/core-plugin-api';
import { oauthRequestApiRef } from '@backstage/core-plugin-api';
import { OAuthRequester } from '@backstage/core-plugin-api';
import { OAuthRequesterOptions } from '@backstage/core-plugin-api';
import { OAuthScope } from '@backstage/core-plugin-api';
import { oktaAuthApiRef } from '@backstage/core-plugin-api';
import { oneloginAuthApiRef } from '@backstage/core-plugin-api';
import { OpenIdConnectApi } from '@backstage/core-plugin-api';
import { openshiftAuthApiRef } from '@backstage/core-plugin-api';
import { PendingOAuthRequest } from '@backstage/core-plugin-api';
import { ProfileInfo } from '@backstage/core-plugin-api';
import { ProfileInfoApi } from '@backstage/core-plugin-api';
import { Observable } from '@backstage/types';
import { PropsWithChildren } from 'react';
import { ReactNode } from 'react';
import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api';
import { SessionApi } from '@backstage/core-plugin-api';
import { SessionState } from '@backstage/core-plugin-api';
import { SignInPageProps } from '@backstage/core-plugin-api';
import { StorageApi } from '@backstage/core-plugin-api';
import { storageApiRef } from '@backstage/core-plugin-api';
import { StorageValueSnapshot } from '@backstage/core-plugin-api';
import { TranslationMessages } from '@backstage/core-plugin-api/alpha';
import { TranslationMessagesOptions } from '@backstage/core-plugin-api/alpha';
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
import { TranslationRefOptions } from '@backstage/core-plugin-api/alpha';
import { TranslationResource } from '@backstage/core-plugin-api/alpha';
import { TranslationResourceOptions } from '@backstage/core-plugin-api/alpha';
import { TypesToApiRefs } from '@backstage/core-plugin-api';
import { useApi } from '@backstage/core-plugin-api';
import { useApiHolder } from '@backstage/core-plugin-api';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { vmwareCloudAuthApiRef } from '@backstage/core-plugin-api';
import { withApis } from '@backstage/core-plugin-api';
import { z } from 'zod';
export { AlertApi };
// @public
export type AlertApi = {
post(alert: AlertMessage): void;
alert$(): Observable<AlertMessage>;
};
export { alertApiRef };
// @public
export const alertApiRef: ApiRef<AlertApi>;
export { AlertMessage };
// @public
export type AlertMessage = {
message: string;
severity?: 'success' | 'info' | 'warning' | 'error';
display?: 'permanent' | 'transient';
};
// @public
export type AnalyticsApi = {
@@ -187,9 +124,17 @@ export type AnalyticsTracker = {
) => void;
};
export { AnyApiFactory };
// @public
export type AnyApiFactory = ApiFactory<
unknown,
unknown,
{
[key in string]: unknown;
}
>;
export { AnyApiRef };
// @public
export type AnyApiRef = ApiRef<unknown>;
// @public @deprecated (undocumented)
export type AnyExtensionDataRef = ExtensionDataRef;
@@ -224,13 +169,51 @@ export const ApiBlueprint: ExtensionBlueprint<{
};
}>;
export { ApiFactory };
// @public
export type ApiFactory<
Api,
Impl extends Api,
Deps extends {
[name in string]: unknown;
},
> = {
api: ApiRef<Api>;
deps: TypesToApiRefs<Deps>;
factory(deps: Deps): Impl;
};
export { ApiHolder };
// @public
export type ApiHolder = {
get<T>(api: ApiRef<T>): T | undefined;
};
export { ApiRef };
// @public
export type ApiRef<T> = {
id: string;
T: T;
};
export { ApiRefConfig };
// @public
export type ApiRefConfig = {
id: string;
};
// @public (undocumented)
export type AppLanguageApi = {
getAvailableLanguages(): {
languages: string[];
};
setLanguage(language?: string): void;
getLanguage(): {
language: string;
};
language$(): Observable<{
language: string;
}>;
};
// @public (undocumented)
export const appLanguageApiRef: ApiRef<AppLanguageApi>;
// @public
export interface AppNode {
@@ -278,7 +261,7 @@ export const AppRootElementBlueprint: ExtensionBlueprint<{
params: {
element: JSX.Element;
};
output: ExtensionDataRef<JSX_3.Element, 'core.reactElement', {}>;
output: ExtensionDataRef<JSX_3, 'core.reactElement', {}>;
inputs: {};
config: {};
configInput: {};
@@ -309,11 +292,25 @@ export const AppRootWrapperBlueprint: ExtensionBlueprint<{
};
}>;
export { AppTheme };
// @public
export type AppTheme = {
id: string;
title: string;
variant: 'light' | 'dark';
icon?: React.ReactElement;
Provider(props: { children: ReactNode }): JSX.Element | null;
};
export { AppThemeApi };
// @public
export type AppThemeApi = {
getInstalledThemes(): AppTheme[];
activeThemeId$(): Observable<string | undefined>;
getActiveThemeId(): string | undefined;
setActiveThemeId(themeId?: string): void;
};
export { appThemeApiRef };
// @public
export const appThemeApiRef: ApiRef<AppThemeApi>;
// @public
export interface AppTree {
@@ -335,25 +332,61 @@ export interface AppTreeApi {
// @public
export const appTreeApiRef: ApiRef<AppTreeApi>;
export { atlassianAuthApiRef };
// @public
export const atlassianAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { AuthProviderInfo };
// @public
export type AuthProviderInfo = {
id: string;
title: string;
icon: IconComponent;
message?: string;
};
export { AuthRequestOptions };
// @public
export type AuthRequestOptions = {
optional?: boolean;
instantPopup?: boolean;
};
export { BackstageIdentityApi };
// @public
export type BackstageIdentityApi = {
getBackstageIdentity(
options?: AuthRequestOptions,
): Promise<BackstageIdentityResponse | undefined>;
};
export { BackstageIdentityResponse };
// @public
export type BackstageIdentityResponse = {
token: string;
expiresAt?: Date;
identity: BackstageUserIdentity;
};
export { BackstageUserIdentity };
// @public
export type BackstageUserIdentity = {
type: 'user';
userEntityRef: string;
ownershipEntityRefs: string[];
};
export { bitbucketAuthApiRef };
// @public
export const bitbucketAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { bitbucketServerAuthApiRef };
// @public
export const bitbucketServerAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { ConfigApi };
// @public
export type ConfigApi = Config;
export { configApiRef };
// @public
export const configApiRef: ApiRef<ConfigApi>;
// @public (undocumented)
export interface ConfigurableExtensionDataRef<
@@ -391,9 +424,23 @@ export const coreExtensionData: {
>;
};
export { createApiFactory };
// @public
export function createApiFactory<
Api,
Impl extends Api,
Deps extends {
[name in string]: unknown;
},
>(factory: ApiFactory<Api, Impl, Deps>): ApiFactory<Api, Impl, Deps>;
export { createApiRef };
// @public
export function createApiFactory<Api, Impl extends Api>(
api: ApiRef<Api>,
instance: Impl,
): ApiFactory<Api, Impl, {}>;
// @public
export function createApiRef<T>(config: ApiRefConfig): ApiRef<T>;
// @public
export function createExtension<
@@ -769,11 +816,50 @@ export type CreateSwappableComponentOptions<
transformProps?: (props: TExternalComponentProps) => TInnerComponentProps;
};
export { createTranslationMessages };
// @public
export function createTranslationMessages<
TId extends string,
TMessages extends {
[key in string]: string;
},
TFull extends boolean,
>(
options: TranslationMessagesOptions<TId, TMessages, TFull>,
): TranslationMessages<TId, TMessages, TFull>;
export { createTranslationRef };
// @public (undocumented)
export function createTranslationRef<
TId extends string,
const TNestedMessages extends AnyNestedMessages,
TTranslations extends {
[language in string]: () => Promise<{
default: {
[key in keyof FlattenedMessages<TNestedMessages>]: string | null;
};
}>;
},
>(
config: TranslationRefOptions<TId, TNestedMessages, TTranslations>,
): TranslationRef<TId, FlattenedMessages<TNestedMessages>>;
export { createTranslationResource };
// @public (undocumented)
export function createTranslationResource<
TId extends string,
TMessages extends {
[key in string]: string;
},
TTranslations extends {
[language in string]: () => Promise<{
default:
| TranslationMessages<TId>
| {
[key in keyof TMessages]: string | null;
};
}>;
},
>(
options: TranslationResourceOptions<TId, TMessages, TTranslations>,
): TranslationResource<TId>;
// @public
export interface DialogApi {
@@ -807,17 +893,37 @@ export interface DialogApiDialog<TResult = void> {
// @public
export const dialogApiRef: ApiRef<DialogApi>;
export { DiscoveryApi };
// @public
export type DiscoveryApi = {
getBaseUrl(pluginId: string): Promise<string>;
};
export { discoveryApiRef };
// @public
export const discoveryApiRef: ApiRef<DiscoveryApi>;
export { ErrorApi };
// @public
export type ErrorApi = {
post(error: ErrorApiError, context?: ErrorApiErrorContext): void;
error$(): Observable<{
error: ErrorApiError;
context?: ErrorApiErrorContext;
}>;
};
export { ErrorApiError };
// @public
export type ErrorApiError = {
name: string;
message: string;
stack?: string;
};
export { ErrorApiErrorContext };
// @public
export type ErrorApiErrorContext = {
hidden?: boolean;
};
export { errorApiRef };
// @public
export const errorApiRef: ApiRef<ErrorApi>;
// @public (undocumented)
export const ErrorDisplay: {
@@ -1201,24 +1307,60 @@ export interface ExternalRouteRef<
readonly T: TParams;
}
export { FeatureFlag };
// @public
export type FeatureFlag = {
name: string;
pluginId: string;
description?: string;
};
// @public
export type FeatureFlagConfig = {
name: string;
};
export { FeatureFlagsApi };
// @public
export interface FeatureFlagsApi {
getRegisteredFlags(): FeatureFlag[];
isActive(name: string): boolean;
registerFlag(flag: FeatureFlag): void;
save(options: FeatureFlagsSaveOptions): void;
}
export { featureFlagsApiRef };
// @public
export const featureFlagsApiRef: ApiRef<FeatureFlagsApi>;
export { FeatureFlagsSaveOptions };
// @public
export type FeatureFlagsSaveOptions = {
states: Record<string, FeatureFlagState>;
merge?: boolean;
};
export { FeatureFlagState };
// @public
export const FeatureFlagState: {
readonly None: 0;
readonly Active: 1;
};
export { FetchApi };
// @public (undocumented)
export type FeatureFlagState =
(typeof FeatureFlagState)[keyof typeof FeatureFlagState];
export { fetchApiRef };
// @public (undocumented)
export namespace FeatureFlagState {
// (undocumented)
export type Active = typeof FeatureFlagState.Active;
// (undocumented)
export type None = typeof FeatureFlagState.None;
}
// @public
export type FetchApi = {
fetch: typeof fetch;
};
// @public
export const fetchApiRef: ApiRef<FetchApi>;
// @public (undocumented)
export type FrontendFeature = FrontendPlugin | FrontendModule;
@@ -1283,11 +1425,28 @@ export type FrontendPluginInfoOptions = {
manifest?: () => Promise<JsonObject>;
};
export { githubAuthApiRef };
// @public
export const githubAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
export { gitlabAuthApiRef };
// @public
export const gitlabAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { googleAuthApiRef };
// @public
export const googleAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
// @public (undocumented)
export const IconBundleBlueprint: ExtensionBlueprint<{
@@ -1332,11 +1491,27 @@ export interface IconsApi {
// @public
export const iconsApiRef: ApiRef<IconsApi>;
export { IdentityApi };
// @public
export type IdentityApi = {
getProfileInfo(): Promise<ProfileInfo>;
getBackstageIdentity(): Promise<BackstageUserIdentity>;
getCredentials(): Promise<{
token?: string;
}>;
signOut(): Promise<void>;
};
export { identityApiRef };
// @public
export const identityApiRef: ApiRef<IdentityApi>;
export { microsoftAuthApiRef };
// @public
export const microsoftAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
// @public
export const NavContentBlueprint: ExtensionBlueprint_2<{
@@ -1382,13 +1557,13 @@ export const NavItemBlueprint: ExtensionBlueprint<{
kind: 'nav-item';
params: {
title: string;
icon: IconComponent_3;
icon: IconComponent;
routeRef: RouteRef<undefined>;
};
output: ExtensionDataRef<
{
title: string;
icon: IconComponent_3;
icon: IconComponent;
routeRef: RouteRef<undefined>;
},
'core.nav-item.target',
@@ -1401,7 +1576,7 @@ export const NavItemBlueprint: ExtensionBlueprint<{
target: ConfigurableExtensionDataRef<
{
title: string;
icon: IconComponent_3;
icon: IconComponent;
routeRef: RouteRef<undefined>;
},
'core.nav-item.target',
@@ -1421,25 +1596,66 @@ export type NotFoundErrorPageProps = {
children?: ReactNode;
};
export { OAuthApi };
// @public
export type OAuthApi = {
getAccessToken(
scope?: OAuthScope,
options?: AuthRequestOptions,
): Promise<string>;
};
export { OAuthRequestApi };
// @public
export type OAuthRequestApi = {
createAuthRequester<OAuthResponse>(
options: OAuthRequesterOptions<OAuthResponse>,
): OAuthRequester<OAuthResponse>;
authRequest$(): Observable<PendingOAuthRequest[]>;
};
export { oauthRequestApiRef };
// @public
export const oauthRequestApiRef: ApiRef<OAuthRequestApi>;
export { OAuthRequester };
// @public
export type OAuthRequester<TAuthResponse> = (
scopes: Set<string>,
) => Promise<TAuthResponse>;
export { OAuthRequesterOptions };
// @public
export type OAuthRequesterOptions<TOAuthResponse> = {
provider: AuthProviderInfo;
onAuthRequest(scopes: Set<string>): Promise<TOAuthResponse>;
};
export { OAuthScope };
// @public
export type OAuthScope = string | string[];
export { oktaAuthApiRef };
// @public
export const oktaAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { oneloginAuthApiRef };
// @public
export const oneloginAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { OpenIdConnectApi };
// @public
export type OpenIdConnectApi = {
getIdToken(options?: AuthRequestOptions): Promise<string>;
};
export { openshiftAuthApiRef };
// @public
export const openshiftAuthApiRef: ApiRef<
OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi
>;
// @public (undocumented)
export interface OverridableExtensionDefinition<
@@ -1593,7 +1809,7 @@ export const PageBlueprint: ExtensionBlueprint<{
};
output:
| ExtensionDataRef<string, 'core.routing.path', {}>
| ExtensionDataRef<JSX_3.Element, 'core.reactElement', {}>
| ExtensionDataRef<JSX_3, 'core.reactElement', {}>
| ExtensionDataRef<
RouteRef<AnyRouteRefParams>,
'core.routing.ref',
@@ -1611,7 +1827,12 @@ export const PageBlueprint: ExtensionBlueprint<{
dataRefs: never;
}>;
export { PendingOAuthRequest };
// @public
export type PendingOAuthRequest = {
provider: AuthProviderInfo;
reject(): void;
trigger(): Promise<void>;
};
// @public (undocumented)
export interface PluginOptions<
@@ -1644,9 +1865,17 @@ export type PortableSchema<TOutput, TInput = TOutput> = {
schema: JsonObject;
};
export { ProfileInfo };
// @public
export type ProfileInfo = {
email?: string;
displayName?: string;
picture?: string;
};
export { ProfileInfoApi };
// @public
export type ProfileInfoApi = {
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
};
// @public (undocumented)
export const Progress: {
@@ -1736,9 +1965,29 @@ export interface RouteResolutionApi {
// @public
export const routeResolutionApiRef: ApiRef<RouteResolutionApi>;
export { SessionApi };
// @public
export type SessionApi = {
signIn(): Promise<void>;
signOut(): Promise<void>;
sessionState$(): Observable<SessionState>;
};
export { SessionState };
// @public
export const SessionState: {
readonly SignedIn: 'SignedIn';
readonly SignedOut: 'SignedOut';
};
// @public (undocumented)
export type SessionState = (typeof SessionState)[keyof typeof SessionState];
// @public (undocumented)
export namespace SessionState {
// (undocumented)
export type SignedIn = typeof SessionState.SignedIn;
// (undocumented)
export type SignedOut = typeof SessionState.SignedOut;
}
// @public
export const SignInPageBlueprint: ExtensionBlueprint<{
@@ -1763,11 +2012,38 @@ export const SignInPageBlueprint: ExtensionBlueprint<{
};
}>;
export { StorageApi };
// @public
export type SignInPageProps = {
onSignInSuccess(identityApi: IdentityApi): void;
children?: ReactNode;
};
export { storageApiRef };
// @public
export interface StorageApi {
forBucket(name: string): StorageApi;
observe$<T extends JsonValue>(
key: string,
): Observable<StorageValueSnapshot<T>>;
remove(key: string): Promise<void>;
set<T extends JsonValue>(key: string, data: T): Promise<void>;
snapshot<T extends JsonValue>(key: string): StorageValueSnapshot<T>;
}
export { StorageValueSnapshot };
// @public
export const storageApiRef: ApiRef<StorageApi>;
// @public
export type StorageValueSnapshot<TValue extends JsonValue> =
| {
key: string;
presence: 'unknown' | 'absent';
value?: undefined;
}
| {
key: string;
presence: 'present';
value: TValue;
};
// @public
export interface SubRouteRef<
@@ -1880,6 +2156,27 @@ export const ThemeBlueprint: ExtensionBlueprint<{
};
}>;
// @public (undocumented)
export type TranslationApi = {
getTranslation<
TMessages extends {
[key in string]: string;
},
>(
translationRef: TranslationRef<string, TMessages>,
): TranslationSnapshot<TMessages>;
translation$<
TMessages extends {
[key in string]: string;
},
>(
translationRef: TranslationRef<string, TMessages>,
): Observable<TranslationSnapshot<TMessages>>;
};
// @public (undocumented)
export const translationApiRef: ApiRef<TranslationApi>;
// @public
export const TranslationBlueprint: ExtensionBlueprint<{
kind: 'translation';
@@ -1917,26 +2214,169 @@ export const TranslationBlueprint: ExtensionBlueprint<{
};
}>;
export { TranslationMessages };
// @public (undocumented)
export type TranslationFunction<
TMessages extends {
[key in string]: string;
},
> = CollapsedMessages<TMessages> extends infer IMessages extends {
[key in string]: string;
}
? {
<TKey extends keyof IMessages>(
key: TKey,
...[args]: TranslationFunctionOptions<
NestedMessageKeys<TKey, IMessages>,
PluralKeys<TMessages>,
IMessages,
string
>
): IMessages[TKey];
<TKey extends keyof IMessages>(
key: TKey,
...[args]: TranslationFunctionOptions<
NestedMessageKeys<TKey, IMessages>,
PluralKeys<TMessages>,
IMessages,
string | JSX_3.Element
>
): JSX_3.Element;
}
: never;
export { TranslationMessagesOptions };
// @public
export interface TranslationMessages<
TId extends string = string,
TMessages extends {
[key in string]: string;
} = {
[key in string]: string;
},
TFull extends boolean = boolean,
> {
// (undocumented)
$$type: '@backstage/TranslationMessages';
full: TFull;
id: TId;
messages: TMessages;
}
export { TranslationRef };
// @public
export interface TranslationMessagesOptions<
TId extends string,
TMessages extends {
[key in string]: string;
},
TFull extends boolean,
> {
// (undocumented)
full?: TFull;
// (undocumented)
messages: false extends TFull
? {
[key in keyof TMessages]?: string | null;
}
: {
[key in keyof TMessages]: string | null;
};
// (undocumented)
ref: TranslationRef<TId, TMessages>;
}
export { TranslationRefOptions };
// @public (undocumented)
export interface TranslationRef<
TId extends string = string,
TMessages extends {
[key in string]: string;
} = {
[key in string]: string;
},
> {
// (undocumented)
$$type: '@backstage/TranslationRef';
// (undocumented)
id: TId;
// (undocumented)
T: TMessages;
}
export { TranslationResource };
// @public (undocumented)
export interface TranslationRefOptions<
TId extends string,
TNestedMessages extends AnyNestedMessages,
TTranslations extends {
[language in string]: () => Promise<{
default: {
[key in keyof FlattenedMessages<TNestedMessages>]: string | null;
};
}>;
},
> {
// (undocumented)
id: TId;
// (undocumented)
messages: TNestedMessages;
// (undocumented)
translations?: TTranslations;
}
export { TranslationResourceOptions };
// @public (undocumented)
export interface TranslationResource<TId extends string = string> {
// (undocumented)
$$type: '@backstage/TranslationResource';
// (undocumented)
id: TId;
}
export { TypesToApiRefs };
// @public (undocumented)
export interface TranslationResourceOptions<
TId extends string,
TMessages extends {
[key in string]: string;
},
TTranslations extends {
[language in string]: () => Promise<{
default:
| TranslationMessages<TId>
| {
[key in keyof TMessages]: string | null;
};
}>;
},
> {
// (undocumented)
ref: TranslationRef<TId, TMessages>;
// (undocumented)
translations: TTranslations;
}
// @public (undocumented)
export type TranslationSnapshot<
TMessages extends {
[key in string]: string;
},
> =
| {
ready: false;
}
| {
ready: true;
t: TranslationFunction<TMessages>;
};
// @public
export type TypesToApiRefs<T> = {
[key in keyof T]: ApiRef<T[key]>;
};
// @public
export function useAnalytics(): AnalyticsTracker;
export { useApi };
// @public
export function useApi<T>(apiRef: ApiRef<T>): T;
export { useApiHolder };
// @public
export function useApiHolder(): ApiHolder;
// @public
export function useAppNode(): AppNode | undefined;
@@ -1954,9 +2394,29 @@ export function useRouteRefParams<Params extends AnyRouteRefParams>(
_routeRef: RouteRef<Params> | SubRouteRef<Params>,
): Params;
export { useTranslationRef };
// @public (undocumented)
export const useTranslationRef: <TMessages extends { [key in string]: string }>(
translationRef: TranslationRef<string, TMessages>,
) => {
t: TranslationFunction<TMessages>;
};
export { vmwareCloudAuthApiRef };
// @public
export const vmwareCloudAuthApiRef: ApiRef<
OAuthApi &
OpenIdConnectApi &
ProfileInfoApi &
BackstageIdentityApi &
SessionApi
>;
export { withApis };
// @public
export function withApis<T extends {}>(
apis: TypesToApiRefs<T>,
): <TProps extends T>(
WrappedComponent: ComponentType<TProps>,
) => {
(props: PropsWithChildren<Omit<TProps, keyof T>>): JSX_2.Element;
displayName: string;
};
```
@@ -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.
@@ -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';
@@ -14,10 +14,27 @@
* limitations under the License.
*/
import { ComponentType, lazy } from 'react';
import { ComponentType, lazy, ReactNode } from 'react';
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
import { SignInPageProps } from '@backstage/core-plugin-api';
import { ExtensionBoundary } from '../components';
import { IdentityApi } from '../apis';
/**
* Props for the `SignInPage` component.
*
* @public
*/
export type SignInPageProps = {
/**
* Set the IdentityApi on successful sign-in. This should only be called once.
*/
onSignInSuccess(identityApi: IdentityApi): void;
/**
* The children to render.
*/
children?: ReactNode;
};
const componentDataRef = createExtensionDataRef<
ComponentType<SignInPageProps>
@@ -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({
@@ -30,7 +30,10 @@ export {
export { NavItemBlueprint } from './NavItemBlueprint';
export { PageBlueprint } from './PageBlueprint';
export { RouterBlueprint } from './RouterBlueprint';
export { SignInPageBlueprint } from './SignInPageBlueprint';
export {
type SignInPageProps,
SignInPageBlueprint,
} from './SignInPageBlueprint';
export { ThemeBlueprint } from './ThemeBlueprint';
export { TranslationBlueprint } from './TranslationBlueprint';
export { SwappableComponentBlueprint } from './SwappableComponentBlueprint';
@@ -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>;
}
@@ -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,
@@ -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,
@@ -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';
@@ -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({
@@ -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';
+1 -1
View File
@@ -14,7 +14,7 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
+1 -1
View File
@@ -5,7 +5,7 @@
```ts
import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
+3 -4
View File
@@ -13,15 +13,14 @@ import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent as IconComponent_2 } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { NavContentComponent } from '@backstage/frontend-plugin-api';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
import { ReactNode } from 'react';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { SignInPageProps } from '@backstage/core-plugin-api';
import { SignInPageProps } from '@backstage/frontend-plugin-api';
import { SwappableComponentRef } from '@backstage/frontend-plugin-api';
import { TranslationMessages } from '@backstage/frontend-plugin-api';
import { TranslationResource } from '@backstage/frontend-plugin-api';
@@ -458,7 +457,7 @@ const appPlugin: OverridableFrontendPlugin<
icons: ExtensionInput<
ConfigurableExtensionDataRef<
{
[x: string]: IconComponent_2;
[x: string]: IconComponent;
},
'core.icons',
{}
+1 -1
View File
@@ -112,8 +112,8 @@ export const catalogReactTranslationRef: TranslationRef<
readonly 'entityTableColumnTitle.description': 'Description';
readonly 'entityTableColumnTitle.domain': 'Domain';
readonly 'entityTableColumnTitle.system': 'System';
readonly 'entityTableColumnTitle.tags': 'Tags';
readonly 'entityTableColumnTitle.namespace': 'Namespace';
readonly 'entityTableColumnTitle.tags': 'Tags';
readonly 'entityTableColumnTitle.lifecycle': 'Lifecycle';
readonly 'entityTableColumnTitle.owner': 'Owner';
readonly 'entityTableColumnTitle.targets': 'Targets';
@@ -8,7 +8,7 @@ import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
+1 -1
View File
@@ -17,7 +17,7 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { IconLinkVerticalProps } from '@backstage/core-components';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
+1 -1
View File
@@ -8,7 +8,7 @@ import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
import { ApiFactory } from '@backstage/frontend-plugin-api';
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { JSX as JSX_2 } from 'react';
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
+1 -1
View File
@@ -20,7 +20,7 @@ import { FormField } from '@backstage/plugin-scaffolder-react/alpha';
import { formFieldsApiRef } from '@backstage/plugin-scaffolder-react/alpha';
import type { FormProps as FormProps_2 } from '@rjsf/core';
import { FormProps as FormProps_3 } from '@backstage/plugin-scaffolder-react';
import { IconComponent } from '@backstage/core-plugin-api';
import { IconComponent } from '@backstage/frontend-plugin-api';
import { IconLinkVerticalProps } from '@backstage/core-components';
import { JSX as JSX_2 } from 'react';
import { LayoutOptions } from '@backstage/plugin-scaffolder-react';
+1 -1
View File
@@ -4,7 +4,7 @@
```ts
import { ApiHolder } from '@backstage/core-plugin-api';
import { ApiRef } from '@backstage/core-plugin-api';
import { ApiRef } from '@backstage/frontend-plugin-api';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { ComponentType } from 'react';
import { createScaffolderFieldExtension as createScaffolderFieldExtension_2 } from '@backstage/plugin-scaffolder-react';

Some files were not shown because too many files have changed in this diff Show More