@backstage/core-app-api 0.3.1 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/index.d.ts +24 -77
- package/dist/index.esm.js +148 -81
- package/dist/index.esm.js.map +1 -1
- package/package.json +6 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# @backstage/core-app-api
|
|
2
2
|
|
|
3
|
+
## 0.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- f959c22787: Asynchronous methods on the identity API can now reliably be called at any time, including early in the bootstrap process or prior to successful sign-in.
|
|
8
|
+
|
|
9
|
+
Previously in such situations, a `Tried to access IdentityApi before app was loaded` error would be thrown. Now, those methods will wait and resolve eventually (as soon as a concrete identity API is provided).
|
|
10
|
+
|
|
11
|
+
## 0.5.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- ceebe25391: Removed deprecated `SignInResult` type, which was replaced with the new `onSignInSuccess` callback.
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- fb565073ec: Add an `allowUrl` callback option to `FetchMiddlewares.injectIdentityAuth`
|
|
20
|
+
- f050eec2c0: Added validation during the application startup that detects if there are any plugins present that have not had their required external routes bound. Failing the validation will cause a hard crash as it is a programmer error. It lets you detect early on that there are dangling routes, rather than having them cause an error later on.
|
|
21
|
+
- Updated dependencies
|
|
22
|
+
- @backstage/core-plugin-api@0.6.0
|
|
23
|
+
- @backstage/config@0.1.13
|
|
24
|
+
|
|
25
|
+
## 0.5.0-next.0
|
|
26
|
+
|
|
27
|
+
### Minor Changes
|
|
28
|
+
|
|
29
|
+
- ceebe25391: Removed deprecated `SignInResult` type, which was replaced with the new `onSignInSuccess` callback.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- Updated dependencies
|
|
34
|
+
- @backstage/core-plugin-api@0.6.0-next.0
|
|
35
|
+
- @backstage/config@0.1.13-next.0
|
|
36
|
+
|
|
37
|
+
## 0.4.0
|
|
38
|
+
|
|
39
|
+
### Minor Changes
|
|
40
|
+
|
|
41
|
+
- e2eb92c109: Removed previously deprecated `ApiRegistry` export.
|
|
42
|
+
|
|
43
|
+
### Patch Changes
|
|
44
|
+
|
|
45
|
+
- 34442cd5cf: Fixed an issue where valid SAML and GitHub sessions would be considered invalid and not be stored.
|
|
46
|
+
|
|
47
|
+
Deprecated the `SamlSession` and `GithubSession` types.
|
|
48
|
+
|
|
49
|
+
- 784d8078ab: Removed direct and transitive MUI dependencies.
|
|
50
|
+
- Updated dependencies
|
|
51
|
+
- @backstage/config@0.1.12
|
|
52
|
+
- @backstage/core-plugin-api@0.5.0
|
|
53
|
+
|
|
3
54
|
## 0.3.1
|
|
4
55
|
|
|
5
56
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode, PropsWithChildren, ComponentType } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
|
-
import { ApiHolder, ApiRef, ApiFactory, AnyApiRef, ProfileInfo,
|
|
3
|
+
import { ApiHolder, ApiRef, ApiFactory, AnyApiRef, ProfileInfo, BackstageIdentityResponse, OAuthRequestApi, DiscoveryApi, AuthProviderInfo, OAuthApi, SessionApi, SessionState, AuthRequestOptions, gitlabAuthApiRef, googleAuthApiRef, OpenIdConnectApi, ProfileInfoApi, BackstageIdentityApi, oktaAuthApiRef, auth0AuthApiRef, microsoftAuthApiRef, oneloginAuthApiRef, bitbucketAuthApiRef, atlassianAuthApiRef, AlertApi, AlertMessage, AnalyticsApi, AnalyticsEvent, AppThemeApi, AppTheme, ErrorApi, ErrorApiError, ErrorApiErrorContext, FeatureFlagsApi, FeatureFlag, FeatureFlagsSaveOptions, FetchApi, IdentityApi, OAuthRequesterOptions, OAuthRequester, PendingOAuthRequest, StorageApi, StorageValueSnapshot, BackstagePlugin, IconComponent, ExternalRouteRef, AnyApiFactory, RouteRef, SubRouteRef } from '@backstage/core-plugin-api';
|
|
4
4
|
import * as _backstage_types from '@backstage/types';
|
|
5
5
|
import { Observable, JsonValue } from '@backstage/types';
|
|
6
6
|
import { Config, AppConfig } from '@backstage/config';
|
|
@@ -30,45 +30,6 @@ declare const ApiProvider: {
|
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
32
|
|
|
33
|
-
declare type ApiImpl<T = unknown> = readonly [ApiRef<T>, T];
|
|
34
|
-
declare class ApiRegistryBuilder {
|
|
35
|
-
private apis;
|
|
36
|
-
add<T, I extends T>(api: ApiRef<T>, impl: I): I;
|
|
37
|
-
build(): ApiRegistry;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* A registry for utility APIs.
|
|
41
|
-
*
|
|
42
|
-
* @public
|
|
43
|
-
* @deprecated Will be removed, use {@link @backstage/test-utils#TestApiProvider} or {@link @backstage/test-utils#TestApiRegistry} instead.
|
|
44
|
-
*/
|
|
45
|
-
declare class ApiRegistry implements ApiHolder {
|
|
46
|
-
private readonly apis;
|
|
47
|
-
static builder(): ApiRegistryBuilder;
|
|
48
|
-
/**
|
|
49
|
-
* Creates a new ApiRegistry with a list of API implementations.
|
|
50
|
-
*
|
|
51
|
-
* @param apis - A list of pairs mapping an ApiRef to its respective implementation
|
|
52
|
-
*/
|
|
53
|
-
static from(apis: ApiImpl[]): ApiRegistry;
|
|
54
|
-
/**
|
|
55
|
-
* Creates a new ApiRegistry with a single API implementation.
|
|
56
|
-
*
|
|
57
|
-
* @param api - ApiRef for the API to add
|
|
58
|
-
* @param impl - Implementation of the API to add
|
|
59
|
-
*/
|
|
60
|
-
static with<T>(api: ApiRef<T>, impl: T): ApiRegistry;
|
|
61
|
-
constructor(apis: Map<string, unknown>);
|
|
62
|
-
/**
|
|
63
|
-
* Returns a new ApiRegistry with the provided API added to the existing ones.
|
|
64
|
-
*
|
|
65
|
-
* @param api - ApiRef for the API to add
|
|
66
|
-
* @param impl - Implementation of the API to add
|
|
67
|
-
*/
|
|
68
|
-
with<T>(api: ApiRef<T>, impl: T): ApiRegistry;
|
|
69
|
-
get<T>(api: ApiRef<T>): T | undefined;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
33
|
/**
|
|
73
34
|
* @public
|
|
74
35
|
*/
|
|
@@ -134,6 +95,7 @@ declare class ApiFactoryRegistry implements ApiFactoryHolder {
|
|
|
134
95
|
* Session information for GitHub auth.
|
|
135
96
|
*
|
|
136
97
|
* @public
|
|
98
|
+
* @deprecated This type is internal and will be removed
|
|
137
99
|
*/
|
|
138
100
|
declare type GithubSession = {
|
|
139
101
|
providerInfo: {
|
|
@@ -142,7 +104,7 @@ declare type GithubSession = {
|
|
|
142
104
|
expiresAt?: Date;
|
|
143
105
|
};
|
|
144
106
|
profile: ProfileInfo;
|
|
145
|
-
backstageIdentity:
|
|
107
|
+
backstageIdentity: BackstageIdentityResponse;
|
|
146
108
|
};
|
|
147
109
|
|
|
148
110
|
/**
|
|
@@ -176,7 +138,7 @@ declare class GithubAuth implements OAuthApi, SessionApi {
|
|
|
176
138
|
signOut(): Promise<void>;
|
|
177
139
|
sessionState$(): Observable<SessionState>;
|
|
178
140
|
getAccessToken(scope?: string, options?: AuthRequestOptions): Promise<string>;
|
|
179
|
-
getBackstageIdentity(options?: AuthRequestOptions): Promise<
|
|
141
|
+
getBackstageIdentity(options?: AuthRequestOptions): Promise<BackstageIdentityResponse | undefined>;
|
|
180
142
|
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
|
|
181
143
|
static normalizeScope(scope?: string): Set<string>;
|
|
182
144
|
}
|
|
@@ -221,7 +183,7 @@ declare class OAuth2 implements OAuthApi, OpenIdConnectApi, ProfileInfoApi, Back
|
|
|
221
183
|
sessionState$(): Observable<SessionState>;
|
|
222
184
|
getAccessToken(scope?: string | string[], options?: AuthRequestOptions): Promise<string>;
|
|
223
185
|
getIdToken(options?: AuthRequestOptions): Promise<string>;
|
|
224
|
-
getBackstageIdentity(options?: AuthRequestOptions): Promise<
|
|
186
|
+
getBackstageIdentity(options?: AuthRequestOptions): Promise<BackstageIdentityResponse | undefined>;
|
|
225
187
|
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
|
|
226
188
|
private static normalizeScopes;
|
|
227
189
|
}
|
|
@@ -239,7 +201,7 @@ declare type OAuth2Session = {
|
|
|
239
201
|
expiresAt: Date;
|
|
240
202
|
};
|
|
241
203
|
profile: ProfileInfo;
|
|
242
|
-
backstageIdentity:
|
|
204
|
+
backstageIdentity: BackstageIdentityResponse;
|
|
243
205
|
};
|
|
244
206
|
|
|
245
207
|
/**
|
|
@@ -263,7 +225,7 @@ declare class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionA
|
|
|
263
225
|
private constructor();
|
|
264
226
|
signIn(): Promise<void>;
|
|
265
227
|
signOut(): Promise<void>;
|
|
266
|
-
getBackstageIdentity(options?: AuthRequestOptions): Promise<
|
|
228
|
+
getBackstageIdentity(options?: AuthRequestOptions): Promise<BackstageIdentityResponse | undefined>;
|
|
267
229
|
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
|
|
268
230
|
}
|
|
269
231
|
|
|
@@ -271,11 +233,12 @@ declare class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionA
|
|
|
271
233
|
* Session information for SAML auth.
|
|
272
234
|
*
|
|
273
235
|
* @public
|
|
236
|
+
* @deprecated This type is internal and will be removed
|
|
274
237
|
*/
|
|
275
|
-
declare type
|
|
238
|
+
declare type ExportedSamlSession = {
|
|
276
239
|
userId: string;
|
|
277
240
|
profile: ProfileInfo;
|
|
278
|
-
backstageIdentity:
|
|
241
|
+
backstageIdentity: BackstageIdentityResponse;
|
|
279
242
|
};
|
|
280
243
|
|
|
281
244
|
/**
|
|
@@ -344,7 +307,7 @@ declare type BitbucketSession = {
|
|
|
344
307
|
expiresAt?: Date;
|
|
345
308
|
};
|
|
346
309
|
profile: ProfileInfo;
|
|
347
|
-
backstageIdentity:
|
|
310
|
+
backstageIdentity: BackstageIdentityResponse;
|
|
348
311
|
};
|
|
349
312
|
|
|
350
313
|
/**
|
|
@@ -547,14 +510,16 @@ declare class FetchMiddlewares {
|
|
|
547
510
|
*
|
|
548
511
|
* The header injection only happens on allowlisted URLs. Per default, if the
|
|
549
512
|
* `config` option is passed in, the `backend.baseUrl` is allowlisted, unless
|
|
550
|
-
* the `urlPrefixAllowlist`
|
|
551
|
-
* precedence. If you pass in neither config nor an
|
|
552
|
-
* will have no effect
|
|
513
|
+
* the `urlPrefixAllowlist` or `allowUrl` options are passed in, in which case
|
|
514
|
+
* they take precedence. If you pass in neither config nor an
|
|
515
|
+
* allowlist/callback, the middleware will have no effect since effectively no
|
|
516
|
+
* request will match the (nonexistent) rules.
|
|
553
517
|
*/
|
|
554
518
|
static injectIdentityAuth(options: {
|
|
555
519
|
identityApi: IdentityApi;
|
|
556
520
|
config?: Config;
|
|
557
521
|
urlPrefixAllowlist?: string[];
|
|
522
|
+
allowUrl?: (url: string) => boolean;
|
|
558
523
|
header?: {
|
|
559
524
|
name: string;
|
|
560
525
|
value: (backstageToken: string) => string;
|
|
@@ -615,27 +580,6 @@ declare type BootErrorPageProps = {
|
|
|
615
580
|
step: 'load-config' | 'load-chunk';
|
|
616
581
|
error: Error;
|
|
617
582
|
};
|
|
618
|
-
/**
|
|
619
|
-
* The outcome of signing in on the sign-in page.
|
|
620
|
-
*
|
|
621
|
-
* @public
|
|
622
|
-
* @deprecated replaced by passing the {@link @backstage/core-plugin-api#IdentityApi} to the {@link SignInPageProps.onSignInSuccess} instead.
|
|
623
|
-
*/
|
|
624
|
-
declare type SignInResult = {
|
|
625
|
-
/**
|
|
626
|
-
* User ID that will be returned by the IdentityApi
|
|
627
|
-
*/
|
|
628
|
-
userId: string;
|
|
629
|
-
profile: ProfileInfo;
|
|
630
|
-
/**
|
|
631
|
-
* Function used to retrieve an ID token for the signed in user.
|
|
632
|
-
*/
|
|
633
|
-
getIdToken?: () => Promise<string>;
|
|
634
|
-
/**
|
|
635
|
-
* Sign out handler that will be called if the user requests to sign out.
|
|
636
|
-
*/
|
|
637
|
-
signOut?: () => Promise<void>;
|
|
638
|
-
};
|
|
639
583
|
/**
|
|
640
584
|
* Props for the `SignInPage` component of {@link AppComponents}.
|
|
641
585
|
*
|
|
@@ -782,11 +726,14 @@ declare type AppOptions = {
|
|
|
782
726
|
/**
|
|
783
727
|
* A list of all plugins to include in the app.
|
|
784
728
|
*/
|
|
785
|
-
plugins?:
|
|
786
|
-
output():
|
|
729
|
+
plugins?: Array<BackstagePlugin<any, any> & {
|
|
730
|
+
output?(): Array<{
|
|
731
|
+
type: 'feature-flag';
|
|
732
|
+
name: string;
|
|
733
|
+
} | {
|
|
787
734
|
type: string;
|
|
788
|
-
}
|
|
789
|
-
}
|
|
735
|
+
}>;
|
|
736
|
+
}>;
|
|
790
737
|
/**
|
|
791
738
|
* Supply components to the app to override the default ones.
|
|
792
739
|
*/
|
|
@@ -971,4 +918,4 @@ declare type FeatureFlaggedProps = {
|
|
|
971
918
|
*/
|
|
972
919
|
declare const FeatureFlagged: (props: FeatureFlaggedProps) => JSX.Element;
|
|
973
920
|
|
|
974
|
-
export { AlertApiForwarder, ApiFactoryHolder, ApiFactoryRegistry, ApiFactoryScope, ApiProvider, ApiProviderProps,
|
|
921
|
+
export { AlertApiForwarder, ApiFactoryHolder, ApiFactoryRegistry, ApiFactoryScope, ApiProvider, ApiProviderProps, ApiResolver, AppComponents, AppConfigLoader, AppContext, AppIcons, AppOptions, AppRouteBinder, AppThemeSelector, AtlassianAuth, Auth0Auth, AuthApiCreateOptions, BackstageApp, BitbucketAuth, BitbucketSession, BootErrorPageProps, ErrorAlerter, ErrorApiForwarder, ErrorBoundaryFallbackProps, FeatureFlagged, FeatureFlaggedProps, FetchMiddleware, FetchMiddlewares, FlatRoutes, FlatRoutesProps, GithubAuth, GithubSession, GitlabAuth, GoogleAuth, LocalStorageFeatureFlags, MicrosoftAuth, NoOpAnalyticsApi, OAuth2, OAuth2CreateOptions, OAuth2Session, OAuthApiCreateOptions, OAuthRequestManager, OktaAuth, OneLoginAuth, OneLoginAuthCreateOptions, SamlAuth, ExportedSamlSession as SamlSession, SignInPageProps, UnhandledErrorForwarder, UrlPatternDiscovery, WebStorage, createFetchApi, createSpecializedApp, defaultConfigLoader };
|
package/dist/index.esm.js
CHANGED
|
@@ -8,7 +8,6 @@ import { ConfigReader } from '@backstage/config';
|
|
|
8
8
|
export { ConfigReader } from '@backstage/config';
|
|
9
9
|
import { matchRoutes, generatePath, useLocation, Routes, Route, useRoutes } from 'react-router-dom';
|
|
10
10
|
import useAsync from 'react-use/lib/useAsync';
|
|
11
|
-
import { UserIdentity } from '@backstage/core-components';
|
|
12
11
|
import useObservable from 'react-use/lib/useObservable';
|
|
13
12
|
|
|
14
13
|
class ApiAggregator {
|
|
@@ -42,39 +41,6 @@ ApiProvider.propTypes = {
|
|
|
42
41
|
children: PropTypes.node
|
|
43
42
|
};
|
|
44
43
|
|
|
45
|
-
class ApiRegistryBuilder {
|
|
46
|
-
constructor() {
|
|
47
|
-
this.apis = [];
|
|
48
|
-
}
|
|
49
|
-
add(api, impl) {
|
|
50
|
-
this.apis.push([api.id, impl]);
|
|
51
|
-
return impl;
|
|
52
|
-
}
|
|
53
|
-
build() {
|
|
54
|
-
return new ApiRegistry(new Map(this.apis));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
class ApiRegistry {
|
|
58
|
-
constructor(apis) {
|
|
59
|
-
this.apis = apis;
|
|
60
|
-
}
|
|
61
|
-
static builder() {
|
|
62
|
-
return new ApiRegistryBuilder();
|
|
63
|
-
}
|
|
64
|
-
static from(apis) {
|
|
65
|
-
return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));
|
|
66
|
-
}
|
|
67
|
-
static with(api, impl) {
|
|
68
|
-
return new ApiRegistry(/* @__PURE__ */ new Map([[api.id, impl]]));
|
|
69
|
-
}
|
|
70
|
-
with(api, impl) {
|
|
71
|
-
return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));
|
|
72
|
-
}
|
|
73
|
-
get(api) {
|
|
74
|
-
return this.apis.get(api.id);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
44
|
class ApiResolver {
|
|
79
45
|
constructor(factories) {
|
|
80
46
|
this.factories = factories;
|
|
@@ -1119,7 +1085,6 @@ class OktaAuth {
|
|
|
1119
1085
|
}
|
|
1120
1086
|
|
|
1121
1087
|
const samlSessionSchema = z.object({
|
|
1122
|
-
userId: z.string(),
|
|
1123
1088
|
profile: z.object({
|
|
1124
1089
|
email: z.string().optional(),
|
|
1125
1090
|
displayName: z.string().optional(),
|
|
@@ -1532,29 +1497,24 @@ function createFetchApi(options) {
|
|
|
1532
1497
|
}
|
|
1533
1498
|
|
|
1534
1499
|
class IdentityAuthInjectorFetchMiddleware {
|
|
1535
|
-
constructor(identityApi,
|
|
1500
|
+
constructor(identityApi, allowUrl, headerName, headerValue) {
|
|
1536
1501
|
this.identityApi = identityApi;
|
|
1537
|
-
this.
|
|
1502
|
+
this.allowUrl = allowUrl;
|
|
1538
1503
|
this.headerName = headerName;
|
|
1539
1504
|
this.headerValue = headerValue;
|
|
1540
1505
|
}
|
|
1541
1506
|
static create(options) {
|
|
1542
1507
|
var _a, _b;
|
|
1543
|
-
const
|
|
1544
|
-
if (options.urlPrefixAllowlist) {
|
|
1545
|
-
allowlist.push(...options.urlPrefixAllowlist);
|
|
1546
|
-
} else if (options.config) {
|
|
1547
|
-
allowlist.push(options.config.getString("backend.baseUrl"));
|
|
1548
|
-
}
|
|
1508
|
+
const matcher = buildMatcher(options);
|
|
1549
1509
|
const headerName = ((_a = options.header) == null ? void 0 : _a.name) || "authorization";
|
|
1550
1510
|
const headerValue = ((_b = options.header) == null ? void 0 : _b.value) || ((token) => `Bearer ${token}`);
|
|
1551
|
-
return new IdentityAuthInjectorFetchMiddleware(options.identityApi,
|
|
1511
|
+
return new IdentityAuthInjectorFetchMiddleware(options.identityApi, matcher, headerName, headerValue);
|
|
1552
1512
|
}
|
|
1553
1513
|
apply(next) {
|
|
1554
1514
|
return async (input, init) => {
|
|
1555
1515
|
const request = new Request(input, init);
|
|
1556
1516
|
const { token } = await this.identityApi.getCredentials();
|
|
1557
|
-
if (request.headers.get(this.headerName) ||
|
|
1517
|
+
if (request.headers.get(this.headerName) || typeof token !== "string" || !token || !this.allowUrl(request.url)) {
|
|
1558
1518
|
return next(input, init);
|
|
1559
1519
|
}
|
|
1560
1520
|
request.headers.set(this.headerName, this.headerValue(token));
|
|
@@ -1562,6 +1522,20 @@ class IdentityAuthInjectorFetchMiddleware {
|
|
|
1562
1522
|
};
|
|
1563
1523
|
}
|
|
1564
1524
|
}
|
|
1525
|
+
function buildMatcher(options) {
|
|
1526
|
+
if (options.allowUrl) {
|
|
1527
|
+
return options.allowUrl;
|
|
1528
|
+
} else if (options.urlPrefixAllowlist) {
|
|
1529
|
+
return buildPrefixMatcher(options.urlPrefixAllowlist);
|
|
1530
|
+
} else if (options.config) {
|
|
1531
|
+
return buildPrefixMatcher([options.config.getString("backend.baseUrl")]);
|
|
1532
|
+
}
|
|
1533
|
+
return () => false;
|
|
1534
|
+
}
|
|
1535
|
+
function buildPrefixMatcher(prefixes) {
|
|
1536
|
+
const trimmedPrefixes = prefixes.map((prefix) => prefix.replace(/\/$/, ""));
|
|
1537
|
+
return (url) => trimmedPrefixes.some((prefix) => url === prefix || url.startsWith(`${prefix}/`));
|
|
1538
|
+
}
|
|
1565
1539
|
|
|
1566
1540
|
function join(left, right) {
|
|
1567
1541
|
if (!right || right === "/") {
|
|
@@ -2173,7 +2147,7 @@ const RouteTracker = ({ tree }) => {
|
|
|
2173
2147
|
}));
|
|
2174
2148
|
};
|
|
2175
2149
|
|
|
2176
|
-
function
|
|
2150
|
+
function validateRouteParameters(routePaths, routeParents) {
|
|
2177
2151
|
const notLeafRoutes = new Set(routeParents.values());
|
|
2178
2152
|
notLeafRoutes.delete(void 0);
|
|
2179
2153
|
for (const route of routeParents.keys()) {
|
|
@@ -2202,6 +2176,21 @@ function validateRoutes(routePaths, routeParents) {
|
|
|
2202
2176
|
}
|
|
2203
2177
|
}
|
|
2204
2178
|
}
|
|
2179
|
+
function validateRouteBindings(routeBindings, plugins) {
|
|
2180
|
+
for (const plugin of plugins) {
|
|
2181
|
+
if (!plugin.externalRoutes) {
|
|
2182
|
+
continue;
|
|
2183
|
+
}
|
|
2184
|
+
for (const [name, externalRouteRef] of Object.entries(plugin.externalRoutes)) {
|
|
2185
|
+
if (externalRouteRef.optional) {
|
|
2186
|
+
continue;
|
|
2187
|
+
}
|
|
2188
|
+
if (!routeBindings.has(externalRouteRef)) {
|
|
2189
|
+
throw new Error(`External route '${name}' of the '${plugin.getId()}' plugin must be bound to a target route. See https://backstage.io/link?bind-routes for details.`);
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2205
2194
|
|
|
2206
2195
|
const AppContext = createVersionedContext("app-context");
|
|
2207
2196
|
const AppContextProvider = ({
|
|
@@ -2218,51 +2207,65 @@ const AppContextProvider = ({
|
|
|
2218
2207
|
function mkError(thing) {
|
|
2219
2208
|
return new Error(`Tried to access IdentityApi ${thing} before app was loaded`);
|
|
2220
2209
|
}
|
|
2210
|
+
function logDeprecation(thing) {
|
|
2211
|
+
console.warn(`WARNING: Call to ${thing} is deprecated and will break in the future`);
|
|
2212
|
+
}
|
|
2221
2213
|
class AppIdentityProxy {
|
|
2214
|
+
constructor() {
|
|
2215
|
+
this.resolveTarget = () => {
|
|
2216
|
+
};
|
|
2217
|
+
this.waitForTarget = new Promise((resolve) => {
|
|
2218
|
+
this.resolveTarget = resolve;
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2222
2221
|
setTarget(identityApi) {
|
|
2223
2222
|
this.target = identityApi;
|
|
2223
|
+
this.resolveTarget(identityApi);
|
|
2224
2224
|
}
|
|
2225
2225
|
getUserId() {
|
|
2226
2226
|
if (!this.target) {
|
|
2227
2227
|
throw mkError("getUserId");
|
|
2228
2228
|
}
|
|
2229
|
+
if (!this.target.getUserId) {
|
|
2230
|
+
throw new Error("IdentityApi does not implement getUserId");
|
|
2231
|
+
}
|
|
2232
|
+
logDeprecation("getUserId");
|
|
2229
2233
|
return this.target.getUserId();
|
|
2230
2234
|
}
|
|
2231
2235
|
getProfile() {
|
|
2232
2236
|
if (!this.target) {
|
|
2233
2237
|
throw mkError("getProfile");
|
|
2234
2238
|
}
|
|
2239
|
+
if (!this.target.getProfile) {
|
|
2240
|
+
throw new Error("IdentityApi does not implement getProfile");
|
|
2241
|
+
}
|
|
2242
|
+
logDeprecation("getProfile");
|
|
2235
2243
|
return this.target.getProfile();
|
|
2236
2244
|
}
|
|
2237
2245
|
async getProfileInfo() {
|
|
2238
|
-
|
|
2239
|
-
throw mkError("getProfileInfo");
|
|
2240
|
-
}
|
|
2241
|
-
return this.target.getProfileInfo();
|
|
2246
|
+
return this.waitForTarget.then((target) => target.getProfileInfo());
|
|
2242
2247
|
}
|
|
2243
2248
|
async getBackstageIdentity() {
|
|
2244
|
-
|
|
2245
|
-
|
|
2249
|
+
const identity = await this.waitForTarget.then((target) => target.getBackstageIdentity());
|
|
2250
|
+
if (!identity.userEntityRef.match(/^.*:.*\/.*$/)) {
|
|
2251
|
+
console.warn(`WARNING: The App IdentityApi provided an invalid userEntityRef, '${identity.userEntityRef}'. It must be a full Entity Reference of the form '<kind>:<namespace>/<name>'.`);
|
|
2246
2252
|
}
|
|
2247
|
-
return
|
|
2253
|
+
return identity;
|
|
2248
2254
|
}
|
|
2249
2255
|
async getCredentials() {
|
|
2250
|
-
|
|
2251
|
-
throw mkError("getCredentials");
|
|
2252
|
-
}
|
|
2253
|
-
return this.target.getCredentials();
|
|
2256
|
+
return this.waitForTarget.then((target) => target.getCredentials());
|
|
2254
2257
|
}
|
|
2255
2258
|
async getIdToken() {
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2259
|
+
return this.waitForTarget.then((target) => {
|
|
2260
|
+
if (!target.getIdToken) {
|
|
2261
|
+
throw new Error("IdentityApi does not implement getIdToken");
|
|
2262
|
+
}
|
|
2263
|
+
logDeprecation("getIdToken");
|
|
2264
|
+
return target.getIdToken();
|
|
2265
|
+
});
|
|
2260
2266
|
}
|
|
2261
2267
|
async signOut() {
|
|
2262
|
-
|
|
2263
|
-
throw mkError("signOut");
|
|
2264
|
-
}
|
|
2265
|
-
await this.target.signOut();
|
|
2268
|
+
await this.waitForTarget.then((target) => target.signOut());
|
|
2266
2269
|
location.reload();
|
|
2267
2270
|
}
|
|
2268
2271
|
}
|
|
@@ -2344,7 +2347,40 @@ const defaultConfigLoader = async (runtimeConfigJson = "__APP_INJECTED_RUNTIME_C
|
|
|
2344
2347
|
return configs;
|
|
2345
2348
|
};
|
|
2346
2349
|
|
|
2347
|
-
|
|
2350
|
+
class ApiRegistryBuilder {
|
|
2351
|
+
constructor() {
|
|
2352
|
+
this.apis = [];
|
|
2353
|
+
}
|
|
2354
|
+
add(api, impl) {
|
|
2355
|
+
this.apis.push([api.id, impl]);
|
|
2356
|
+
return impl;
|
|
2357
|
+
}
|
|
2358
|
+
build() {
|
|
2359
|
+
return new ApiRegistry(new Map(this.apis));
|
|
2360
|
+
}
|
|
2361
|
+
}
|
|
2362
|
+
class ApiRegistry {
|
|
2363
|
+
constructor(apis) {
|
|
2364
|
+
this.apis = apis;
|
|
2365
|
+
}
|
|
2366
|
+
static builder() {
|
|
2367
|
+
return new ApiRegistryBuilder();
|
|
2368
|
+
}
|
|
2369
|
+
static from(apis) {
|
|
2370
|
+
return new ApiRegistry(new Map(apis.map(([api, impl]) => [api.id, impl])));
|
|
2371
|
+
}
|
|
2372
|
+
static with(api, impl) {
|
|
2373
|
+
return new ApiRegistry(/* @__PURE__ */ new Map([[api.id, impl]]));
|
|
2374
|
+
}
|
|
2375
|
+
with(api, impl) {
|
|
2376
|
+
return new ApiRegistry(new Map([...this.apis, [api.id, impl]]));
|
|
2377
|
+
}
|
|
2378
|
+
get(api) {
|
|
2379
|
+
return this.apis.get(api.id);
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
|
|
2383
|
+
function resolveRouteBindings(bindRoutes) {
|
|
2348
2384
|
const result = /* @__PURE__ */ new Map();
|
|
2349
2385
|
if (bindRoutes) {
|
|
2350
2386
|
const bind = (externalRoutes, targetRoutes) => {
|
|
@@ -2365,6 +2401,7 @@ function generateBoundRoutes(bindRoutes) {
|
|
|
2365
2401
|
}
|
|
2366
2402
|
return result;
|
|
2367
2403
|
}
|
|
2404
|
+
|
|
2368
2405
|
function getBasePath(configApi) {
|
|
2369
2406
|
var _a;
|
|
2370
2407
|
let { pathname } = new URL((_a = configApi.getOptionalString("app.baseUrl")) != null ? _a : "/", "http://dummy.dev");
|
|
@@ -2390,7 +2427,7 @@ function useConfigLoader(configLoader, components, appThemeApi) {
|
|
|
2390
2427
|
if (noConfigNode) {
|
|
2391
2428
|
return {
|
|
2392
2429
|
node: /* @__PURE__ */ React.createElement(ApiProvider, {
|
|
2393
|
-
apis: ApiRegistry.
|
|
2430
|
+
apis: ApiRegistry.with(appThemeApiRef, appThemeApi)
|
|
2394
2431
|
}, /* @__PURE__ */ React.createElement(ThemeProvider, null, noConfigNode))
|
|
2395
2432
|
};
|
|
2396
2433
|
}
|
|
@@ -2436,9 +2473,16 @@ class AppManager {
|
|
|
2436
2473
|
}
|
|
2437
2474
|
getProvider() {
|
|
2438
2475
|
const appContext = new AppContextImpl(this);
|
|
2476
|
+
let routesHaveBeenValidated = false;
|
|
2439
2477
|
const Provider = ({ children }) => {
|
|
2440
2478
|
const appThemeApi = useMemo(() => AppThemeSelector.createWithStorage(this.themes), []);
|
|
2441
|
-
const {
|
|
2479
|
+
const {
|
|
2480
|
+
routePaths,
|
|
2481
|
+
routeParents,
|
|
2482
|
+
routeObjects,
|
|
2483
|
+
featureFlags,
|
|
2484
|
+
routeBindings
|
|
2485
|
+
} = useMemo(() => {
|
|
2442
2486
|
const result = traverseElementTree({
|
|
2443
2487
|
root: children,
|
|
2444
2488
|
discoverers: [childDiscoverer, routeElementDiscoverer],
|
|
@@ -2450,12 +2494,19 @@ class AppManager {
|
|
|
2450
2494
|
featureFlags: featureFlagCollector
|
|
2451
2495
|
}
|
|
2452
2496
|
});
|
|
2453
|
-
validateRoutes(result.routePaths, result.routeParents);
|
|
2454
2497
|
result.collectedPlugins.forEach((plugin) => this.plugins.add(plugin));
|
|
2455
2498
|
this.verifyPlugins(this.plugins);
|
|
2456
2499
|
this.getApiHolder();
|
|
2457
|
-
return
|
|
2500
|
+
return {
|
|
2501
|
+
...result,
|
|
2502
|
+
routeBindings: resolveRouteBindings(this.bindRoutes)
|
|
2503
|
+
};
|
|
2458
2504
|
}, [children]);
|
|
2505
|
+
if (!routesHaveBeenValidated) {
|
|
2506
|
+
routesHaveBeenValidated = true;
|
|
2507
|
+
validateRouteParameters(routePaths, routeParents);
|
|
2508
|
+
validateRouteBindings(routeBindings, this.plugins);
|
|
2509
|
+
}
|
|
2459
2510
|
const loadedConfig = useConfigLoader(this.configLoader, this.components, appThemeApi);
|
|
2460
2511
|
const hasConfigApi = "api" in loadedConfig;
|
|
2461
2512
|
if (hasConfigApi) {
|
|
@@ -2475,14 +2526,11 @@ class AppManager {
|
|
|
2475
2526
|
}
|
|
2476
2527
|
} else {
|
|
2477
2528
|
for (const output of plugin.output()) {
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
});
|
|
2484
|
-
break;
|
|
2485
|
-
}
|
|
2529
|
+
if (output.type === "feature-flag") {
|
|
2530
|
+
featureFlagsApi.registerFlag({
|
|
2531
|
+
name: output.name,
|
|
2532
|
+
pluginId: plugin.getId()
|
|
2533
|
+
});
|
|
2486
2534
|
}
|
|
2487
2535
|
}
|
|
2488
2536
|
}
|
|
@@ -2504,7 +2552,7 @@ class AppManager {
|
|
|
2504
2552
|
routePaths,
|
|
2505
2553
|
routeParents,
|
|
2506
2554
|
routeObjects,
|
|
2507
|
-
routeBindings
|
|
2555
|
+
routeBindings,
|
|
2508
2556
|
basePath: getBasePath(loadedConfig.api)
|
|
2509
2557
|
}, children))));
|
|
2510
2558
|
};
|
|
@@ -2529,7 +2577,26 @@ class AppManager {
|
|
|
2529
2577
|
const configApi = useApi(configApiRef);
|
|
2530
2578
|
const mountPath = `${getBasePath(configApi)}/*`;
|
|
2531
2579
|
if (!SignInPageComponent) {
|
|
2532
|
-
this.appIdentityProxy.setTarget(
|
|
2580
|
+
this.appIdentityProxy.setTarget({
|
|
2581
|
+
getUserId: () => "guest",
|
|
2582
|
+
getIdToken: async () => void 0,
|
|
2583
|
+
getProfile: () => ({
|
|
2584
|
+
email: "guest@example.com",
|
|
2585
|
+
displayName: "Guest"
|
|
2586
|
+
}),
|
|
2587
|
+
getProfileInfo: async () => ({
|
|
2588
|
+
email: "guest@example.com",
|
|
2589
|
+
displayName: "Guest"
|
|
2590
|
+
}),
|
|
2591
|
+
getBackstageIdentity: async () => ({
|
|
2592
|
+
type: "user",
|
|
2593
|
+
userEntityRef: "user:default/guest",
|
|
2594
|
+
ownershipEntityRefs: ["user:default/guest"]
|
|
2595
|
+
}),
|
|
2596
|
+
getCredentials: async () => ({}),
|
|
2597
|
+
signOut: async () => {
|
|
2598
|
+
}
|
|
2599
|
+
});
|
|
2533
2600
|
return /* @__PURE__ */ React.createElement(RouterComponent, null, /* @__PURE__ */ React.createElement(RouteTracker, {
|
|
2534
2601
|
tree: children
|
|
2535
2602
|
}), /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
|
|
@@ -2653,5 +2720,5 @@ const FlatRoutes = (props) => {
|
|
|
2653
2720
|
return useRoutes(routes);
|
|
2654
2721
|
};
|
|
2655
2722
|
|
|
2656
|
-
export { AlertApiForwarder, ApiFactoryRegistry, ApiProvider,
|
|
2723
|
+
export { AlertApiForwarder, ApiFactoryRegistry, ApiProvider, ApiResolver, AppThemeSelector, AtlassianAuth, Auth0Auth, BitbucketAuth, ErrorAlerter, ErrorApiForwarder, FeatureFlagged, FetchMiddlewares, FlatRoutes, GithubAuth, GitlabAuth, GoogleAuth, LocalStorageFeatureFlags, MicrosoftAuth, NoOpAnalyticsApi, OAuth2, OAuthRequestManager, OktaAuth, OneLoginAuth, SamlAuth, UnhandledErrorForwarder, UrlPatternDiscovery, WebStorage, createFetchApi, createSpecializedApp, defaultConfigLoader };
|
|
2657
2724
|
//# sourceMappingURL=index.esm.js.map
|