@backstage/core-app-api 0.5.0-next.0 → 0.5.2
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 +38 -0
- package/dist/index.d.ts +11 -14
- package/dist/index.esm.js +106 -201
- package/dist/index.esm.js.map +1 -1
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# @backstage/core-app-api
|
|
2
2
|
|
|
3
|
+
## 0.5.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 40775bd263: Switched out the `GithubAuth` implementation to use the common `OAuth2` implementation. This relies on the simultaneous change in `@backstage/plugin-auth-backend` that enabled access token storage in cookies rather than the current solution that's based on `LocalStorage`.
|
|
8
|
+
|
|
9
|
+
> **NOTE:** Make sure you upgrade the `auth-backend` deployment before or at the same time as you deploy this change.
|
|
10
|
+
|
|
11
|
+
## 0.5.2-next.0
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- 40775bd263: Switched out the `GithubAuth` implementation to use the common `OAuth2` implementation. This relies on the simultaneous change in `@backstage/plugin-auth-backend` that enabled access token storage in cookies rather than the current solution that's based on `LocalStorage`.
|
|
16
|
+
|
|
17
|
+
> **NOTE:** Make sure you upgrade the `auth-backend` deployment before or at the same time as you deploy this change.
|
|
18
|
+
|
|
19
|
+
## 0.5.1
|
|
20
|
+
|
|
21
|
+
### Patch Changes
|
|
22
|
+
|
|
23
|
+
- 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.
|
|
24
|
+
|
|
25
|
+
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).
|
|
26
|
+
|
|
27
|
+
## 0.5.0
|
|
28
|
+
|
|
29
|
+
### Minor Changes
|
|
30
|
+
|
|
31
|
+
- ceebe25391: Removed deprecated `SignInResult` type, which was replaced with the new `onSignInSuccess` callback.
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- fb565073ec: Add an `allowUrl` callback option to `FetchMiddlewares.injectIdentityAuth`
|
|
36
|
+
- 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.
|
|
37
|
+
- Updated dependencies
|
|
38
|
+
- @backstage/core-plugin-api@0.6.0
|
|
39
|
+
- @backstage/config@0.1.13
|
|
40
|
+
|
|
3
41
|
## 0.5.0-next.0
|
|
4
42
|
|
|
5
43
|
### Minor 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, BackstageIdentityResponse, OAuthRequestApi, DiscoveryApi, AuthProviderInfo,
|
|
3
|
+
import { ApiHolder, ApiRef, ApiFactory, AnyApiRef, ProfileInfo, BackstageIdentityResponse, OAuthRequestApi, DiscoveryApi, AuthProviderInfo, githubAuthApiRef, gitlabAuthApiRef, googleAuthApiRef, OAuthApi, OpenIdConnectApi, ProfileInfoApi, BackstageIdentityApi, SessionApi, SessionState, AuthRequestOptions, 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';
|
|
@@ -130,16 +130,11 @@ declare type AuthApiCreateOptions = {
|
|
|
130
130
|
*
|
|
131
131
|
* @public
|
|
132
132
|
*/
|
|
133
|
-
declare class GithubAuth
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
signOut(): Promise<void>;
|
|
139
|
-
sessionState$(): Observable<SessionState>;
|
|
140
|
-
getAccessToken(scope?: string, options?: AuthRequestOptions): Promise<string>;
|
|
141
|
-
getBackstageIdentity(options?: AuthRequestOptions): Promise<BackstageIdentityResponse | undefined>;
|
|
142
|
-
getProfile(options?: AuthRequestOptions): Promise<ProfileInfo | undefined>;
|
|
133
|
+
declare class GithubAuth {
|
|
134
|
+
static create(options: OAuthApiCreateOptions): typeof githubAuthApiRef.T;
|
|
135
|
+
/**
|
|
136
|
+
* @deprecated This method is deprecated and will be removed in a future release.
|
|
137
|
+
*/
|
|
143
138
|
static normalizeScope(scope?: string): Set<string>;
|
|
144
139
|
}
|
|
145
140
|
|
|
@@ -510,14 +505,16 @@ declare class FetchMiddlewares {
|
|
|
510
505
|
*
|
|
511
506
|
* The header injection only happens on allowlisted URLs. Per default, if the
|
|
512
507
|
* `config` option is passed in, the `backend.baseUrl` is allowlisted, unless
|
|
513
|
-
* the `urlPrefixAllowlist`
|
|
514
|
-
* precedence. If you pass in neither config nor an
|
|
515
|
-
* will have no effect
|
|
508
|
+
* the `urlPrefixAllowlist` or `allowUrl` options are passed in, in which case
|
|
509
|
+
* they take precedence. If you pass in neither config nor an
|
|
510
|
+
* allowlist/callback, the middleware will have no effect since effectively no
|
|
511
|
+
* request will match the (nonexistent) rules.
|
|
516
512
|
*/
|
|
517
513
|
static injectIdentityAuth(options: {
|
|
518
514
|
identityApi: IdentityApi;
|
|
519
515
|
config?: Config;
|
|
520
516
|
urlPrefixAllowlist?: string[];
|
|
517
|
+
allowUrl?: (url: string) => boolean;
|
|
521
518
|
header?: {
|
|
522
519
|
name: string;
|
|
523
520
|
value: (backstageToken: string) => string;
|
package/dist/index.esm.js
CHANGED
|
@@ -725,167 +725,7 @@ class AuthSessionStore {
|
|
|
725
725
|
}
|
|
726
726
|
}
|
|
727
727
|
|
|
728
|
-
class OptionalRefreshSessionManagerMux {
|
|
729
|
-
constructor(options) {
|
|
730
|
-
this.stateTracker = new SessionStateTracker();
|
|
731
|
-
this.sessionCanRefresh = options.sessionCanRefresh;
|
|
732
|
-
this.staticSessionManager = options.staticSessionManager;
|
|
733
|
-
this.refreshingSessionManager = options.refreshingSessionManager;
|
|
734
|
-
}
|
|
735
|
-
async getSession(options) {
|
|
736
|
-
const staticSession = await this.staticSessionManager.getSession({
|
|
737
|
-
...options,
|
|
738
|
-
optional: true
|
|
739
|
-
});
|
|
740
|
-
if (staticSession) {
|
|
741
|
-
this.stateTracker.setIsSignedIn(true);
|
|
742
|
-
return staticSession;
|
|
743
|
-
}
|
|
744
|
-
const session = await this.refreshingSessionManager.getSession(options);
|
|
745
|
-
if (!session) {
|
|
746
|
-
this.stateTracker.setIsSignedIn(false);
|
|
747
|
-
return void 0;
|
|
748
|
-
}
|
|
749
|
-
if (this.sessionCanRefresh(session)) {
|
|
750
|
-
this.stateTracker.setIsSignedIn(true);
|
|
751
|
-
return session;
|
|
752
|
-
}
|
|
753
|
-
this.staticSessionManager.setSession(session);
|
|
754
|
-
this.stateTracker.setIsSignedIn(true);
|
|
755
|
-
return session;
|
|
756
|
-
}
|
|
757
|
-
async removeSession() {
|
|
758
|
-
await Promise.all([
|
|
759
|
-
this.refreshingSessionManager.removeSession(),
|
|
760
|
-
this.staticSessionManager.removeSession()
|
|
761
|
-
]);
|
|
762
|
-
this.stateTracker.setIsSignedIn(false);
|
|
763
|
-
}
|
|
764
|
-
sessionState$() {
|
|
765
|
-
return this.stateTracker.sessionState$();
|
|
766
|
-
}
|
|
767
|
-
}
|
|
768
|
-
|
|
769
|
-
const githubSessionSchema = z.object({
|
|
770
|
-
providerInfo: z.object({
|
|
771
|
-
accessToken: z.string(),
|
|
772
|
-
scopes: z.set(z.string()),
|
|
773
|
-
expiresAt: z.date().optional()
|
|
774
|
-
}),
|
|
775
|
-
profile: z.object({
|
|
776
|
-
email: z.string().optional(),
|
|
777
|
-
displayName: z.string().optional(),
|
|
778
|
-
picture: z.string().optional()
|
|
779
|
-
}),
|
|
780
|
-
backstageIdentity: z.object({
|
|
781
|
-
id: z.string(),
|
|
782
|
-
token: z.string(),
|
|
783
|
-
identity: z.object({
|
|
784
|
-
type: z.literal("user"),
|
|
785
|
-
userEntityRef: z.string(),
|
|
786
|
-
ownershipEntityRefs: z.array(z.string())
|
|
787
|
-
})
|
|
788
|
-
})
|
|
789
|
-
});
|
|
790
|
-
|
|
791
728
|
const DEFAULT_PROVIDER$a = {
|
|
792
|
-
id: "github",
|
|
793
|
-
title: "GitHub",
|
|
794
|
-
icon: () => null
|
|
795
|
-
};
|
|
796
|
-
class GithubAuth {
|
|
797
|
-
constructor(sessionManager) {
|
|
798
|
-
this.sessionManager = sessionManager;
|
|
799
|
-
}
|
|
800
|
-
static create(options) {
|
|
801
|
-
const {
|
|
802
|
-
discoveryApi,
|
|
803
|
-
environment = "development",
|
|
804
|
-
provider = DEFAULT_PROVIDER$a,
|
|
805
|
-
oauthRequestApi,
|
|
806
|
-
defaultScopes = ["read:user"]
|
|
807
|
-
} = options;
|
|
808
|
-
const connector = new DefaultAuthConnector({
|
|
809
|
-
discoveryApi,
|
|
810
|
-
environment,
|
|
811
|
-
provider,
|
|
812
|
-
oauthRequestApi,
|
|
813
|
-
sessionTransform(res) {
|
|
814
|
-
return {
|
|
815
|
-
...res,
|
|
816
|
-
providerInfo: {
|
|
817
|
-
accessToken: res.providerInfo.accessToken,
|
|
818
|
-
scopes: GithubAuth.normalizeScope(res.providerInfo.scope),
|
|
819
|
-
expiresAt: res.providerInfo.expiresInSeconds ? new Date(Date.now() + res.providerInfo.expiresInSeconds * 1e3) : void 0
|
|
820
|
-
}
|
|
821
|
-
};
|
|
822
|
-
}
|
|
823
|
-
});
|
|
824
|
-
const refreshingSessionManager = new RefreshingAuthSessionManager({
|
|
825
|
-
connector,
|
|
826
|
-
defaultScopes: new Set(defaultScopes),
|
|
827
|
-
sessionScopes: (session) => session.providerInfo.scopes,
|
|
828
|
-
sessionShouldRefresh: (session) => {
|
|
829
|
-
const { expiresAt } = session.providerInfo;
|
|
830
|
-
if (!expiresAt) {
|
|
831
|
-
return false;
|
|
832
|
-
}
|
|
833
|
-
const expiresInSec = (expiresAt.getTime() - Date.now()) / 1e3;
|
|
834
|
-
return expiresInSec < 60 * 5;
|
|
835
|
-
}
|
|
836
|
-
});
|
|
837
|
-
const staticSessionManager = new AuthSessionStore({
|
|
838
|
-
manager: new StaticAuthSessionManager({
|
|
839
|
-
connector,
|
|
840
|
-
defaultScopes: new Set(defaultScopes),
|
|
841
|
-
sessionScopes: (session) => session.providerInfo.scopes
|
|
842
|
-
}),
|
|
843
|
-
storageKey: `${provider.id}Session`,
|
|
844
|
-
schema: githubSessionSchema,
|
|
845
|
-
sessionScopes: (session) => session.providerInfo.scopes
|
|
846
|
-
});
|
|
847
|
-
const sessionManagerMux = new OptionalRefreshSessionManagerMux({
|
|
848
|
-
refreshingSessionManager,
|
|
849
|
-
staticSessionManager,
|
|
850
|
-
sessionCanRefresh: (session) => session.providerInfo.expiresAt !== void 0
|
|
851
|
-
});
|
|
852
|
-
return new GithubAuth(sessionManagerMux);
|
|
853
|
-
}
|
|
854
|
-
async signIn() {
|
|
855
|
-
await this.getAccessToken();
|
|
856
|
-
}
|
|
857
|
-
async signOut() {
|
|
858
|
-
await this.sessionManager.removeSession();
|
|
859
|
-
}
|
|
860
|
-
sessionState$() {
|
|
861
|
-
return this.sessionManager.sessionState$();
|
|
862
|
-
}
|
|
863
|
-
async getAccessToken(scope, options) {
|
|
864
|
-
var _a;
|
|
865
|
-
const session = await this.sessionManager.getSession({
|
|
866
|
-
...options,
|
|
867
|
-
scopes: GithubAuth.normalizeScope(scope)
|
|
868
|
-
});
|
|
869
|
-
return (_a = session == null ? void 0 : session.providerInfo.accessToken) != null ? _a : "";
|
|
870
|
-
}
|
|
871
|
-
async getBackstageIdentity(options = {}) {
|
|
872
|
-
const session = await this.sessionManager.getSession(options);
|
|
873
|
-
return session == null ? void 0 : session.backstageIdentity;
|
|
874
|
-
}
|
|
875
|
-
async getProfile(options = {}) {
|
|
876
|
-
const session = await this.sessionManager.getSession(options);
|
|
877
|
-
return session == null ? void 0 : session.profile;
|
|
878
|
-
}
|
|
879
|
-
static normalizeScope(scope) {
|
|
880
|
-
if (!scope) {
|
|
881
|
-
return /* @__PURE__ */ new Set();
|
|
882
|
-
}
|
|
883
|
-
const scopeList = Array.isArray(scope) ? scope : scope.split(/[\s|,]/).filter(Boolean);
|
|
884
|
-
return new Set(scopeList);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
const DEFAULT_PROVIDER$9 = {
|
|
889
729
|
id: "oauth2",
|
|
890
730
|
title: "Your Identity Provider",
|
|
891
731
|
icon: () => null
|
|
@@ -895,7 +735,7 @@ class OAuth2 {
|
|
|
895
735
|
const {
|
|
896
736
|
discoveryApi,
|
|
897
737
|
environment = "development",
|
|
898
|
-
provider = DEFAULT_PROVIDER$
|
|
738
|
+
provider = DEFAULT_PROVIDER$a,
|
|
899
739
|
oauthRequestApi,
|
|
900
740
|
defaultScopes = [],
|
|
901
741
|
scopeTransform = (x) => x
|
|
@@ -972,6 +812,37 @@ class OAuth2 {
|
|
|
972
812
|
}
|
|
973
813
|
}
|
|
974
814
|
|
|
815
|
+
const DEFAULT_PROVIDER$9 = {
|
|
816
|
+
id: "github",
|
|
817
|
+
title: "GitHub",
|
|
818
|
+
icon: () => null
|
|
819
|
+
};
|
|
820
|
+
class GithubAuth {
|
|
821
|
+
static create(options) {
|
|
822
|
+
const {
|
|
823
|
+
discoveryApi,
|
|
824
|
+
environment = "development",
|
|
825
|
+
provider = DEFAULT_PROVIDER$9,
|
|
826
|
+
oauthRequestApi,
|
|
827
|
+
defaultScopes = ["read:user"]
|
|
828
|
+
} = options;
|
|
829
|
+
return OAuth2.create({
|
|
830
|
+
discoveryApi,
|
|
831
|
+
oauthRequestApi,
|
|
832
|
+
provider,
|
|
833
|
+
environment,
|
|
834
|
+
defaultScopes
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
static normalizeScope(scope) {
|
|
838
|
+
if (!scope) {
|
|
839
|
+
return /* @__PURE__ */ new Set();
|
|
840
|
+
}
|
|
841
|
+
const scopeList = Array.isArray(scope) ? scope : scope.split(/[\s|,]/).filter(Boolean);
|
|
842
|
+
return new Set(scopeList);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
975
846
|
const DEFAULT_PROVIDER$8 = {
|
|
976
847
|
id: "gitlab",
|
|
977
848
|
title: "GitLab",
|
|
@@ -1497,29 +1368,24 @@ function createFetchApi(options) {
|
|
|
1497
1368
|
}
|
|
1498
1369
|
|
|
1499
1370
|
class IdentityAuthInjectorFetchMiddleware {
|
|
1500
|
-
constructor(identityApi,
|
|
1371
|
+
constructor(identityApi, allowUrl, headerName, headerValue) {
|
|
1501
1372
|
this.identityApi = identityApi;
|
|
1502
|
-
this.
|
|
1373
|
+
this.allowUrl = allowUrl;
|
|
1503
1374
|
this.headerName = headerName;
|
|
1504
1375
|
this.headerValue = headerValue;
|
|
1505
1376
|
}
|
|
1506
1377
|
static create(options) {
|
|
1507
1378
|
var _a, _b;
|
|
1508
|
-
const
|
|
1509
|
-
if (options.urlPrefixAllowlist) {
|
|
1510
|
-
allowlist.push(...options.urlPrefixAllowlist);
|
|
1511
|
-
} else if (options.config) {
|
|
1512
|
-
allowlist.push(options.config.getString("backend.baseUrl"));
|
|
1513
|
-
}
|
|
1379
|
+
const matcher = buildMatcher(options);
|
|
1514
1380
|
const headerName = ((_a = options.header) == null ? void 0 : _a.name) || "authorization";
|
|
1515
1381
|
const headerValue = ((_b = options.header) == null ? void 0 : _b.value) || ((token) => `Bearer ${token}`);
|
|
1516
|
-
return new IdentityAuthInjectorFetchMiddleware(options.identityApi,
|
|
1382
|
+
return new IdentityAuthInjectorFetchMiddleware(options.identityApi, matcher, headerName, headerValue);
|
|
1517
1383
|
}
|
|
1518
1384
|
apply(next) {
|
|
1519
1385
|
return async (input, init) => {
|
|
1520
1386
|
const request = new Request(input, init);
|
|
1521
1387
|
const { token } = await this.identityApi.getCredentials();
|
|
1522
|
-
if (request.headers.get(this.headerName) ||
|
|
1388
|
+
if (request.headers.get(this.headerName) || typeof token !== "string" || !token || !this.allowUrl(request.url)) {
|
|
1523
1389
|
return next(input, init);
|
|
1524
1390
|
}
|
|
1525
1391
|
request.headers.set(this.headerName, this.headerValue(token));
|
|
@@ -1527,6 +1393,20 @@ class IdentityAuthInjectorFetchMiddleware {
|
|
|
1527
1393
|
};
|
|
1528
1394
|
}
|
|
1529
1395
|
}
|
|
1396
|
+
function buildMatcher(options) {
|
|
1397
|
+
if (options.allowUrl) {
|
|
1398
|
+
return options.allowUrl;
|
|
1399
|
+
} else if (options.urlPrefixAllowlist) {
|
|
1400
|
+
return buildPrefixMatcher(options.urlPrefixAllowlist);
|
|
1401
|
+
} else if (options.config) {
|
|
1402
|
+
return buildPrefixMatcher([options.config.getString("backend.baseUrl")]);
|
|
1403
|
+
}
|
|
1404
|
+
return () => false;
|
|
1405
|
+
}
|
|
1406
|
+
function buildPrefixMatcher(prefixes) {
|
|
1407
|
+
const trimmedPrefixes = prefixes.map((prefix) => prefix.replace(/\/$/, ""));
|
|
1408
|
+
return (url) => trimmedPrefixes.some((prefix) => url === prefix || url.startsWith(`${prefix}/`));
|
|
1409
|
+
}
|
|
1530
1410
|
|
|
1531
1411
|
function join(left, right) {
|
|
1532
1412
|
if (!right || right === "/") {
|
|
@@ -2138,7 +2018,7 @@ const RouteTracker = ({ tree }) => {
|
|
|
2138
2018
|
}));
|
|
2139
2019
|
};
|
|
2140
2020
|
|
|
2141
|
-
function
|
|
2021
|
+
function validateRouteParameters(routePaths, routeParents) {
|
|
2142
2022
|
const notLeafRoutes = new Set(routeParents.values());
|
|
2143
2023
|
notLeafRoutes.delete(void 0);
|
|
2144
2024
|
for (const route of routeParents.keys()) {
|
|
@@ -2167,6 +2047,21 @@ function validateRoutes(routePaths, routeParents) {
|
|
|
2167
2047
|
}
|
|
2168
2048
|
}
|
|
2169
2049
|
}
|
|
2050
|
+
function validateRouteBindings(routeBindings, plugins) {
|
|
2051
|
+
for (const plugin of plugins) {
|
|
2052
|
+
if (!plugin.externalRoutes) {
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
for (const [name, externalRouteRef] of Object.entries(plugin.externalRoutes)) {
|
|
2056
|
+
if (externalRouteRef.optional) {
|
|
2057
|
+
continue;
|
|
2058
|
+
}
|
|
2059
|
+
if (!routeBindings.has(externalRouteRef)) {
|
|
2060
|
+
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.`);
|
|
2061
|
+
}
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2170
2065
|
|
|
2171
2066
|
const AppContext = createVersionedContext("app-context");
|
|
2172
2067
|
const AppContextProvider = ({
|
|
@@ -2187,8 +2082,16 @@ function logDeprecation(thing) {
|
|
|
2187
2082
|
console.warn(`WARNING: Call to ${thing} is deprecated and will break in the future`);
|
|
2188
2083
|
}
|
|
2189
2084
|
class AppIdentityProxy {
|
|
2085
|
+
constructor() {
|
|
2086
|
+
this.resolveTarget = () => {
|
|
2087
|
+
};
|
|
2088
|
+
this.waitForTarget = new Promise((resolve) => {
|
|
2089
|
+
this.resolveTarget = resolve;
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2190
2092
|
setTarget(identityApi) {
|
|
2191
2093
|
this.target = identityApi;
|
|
2094
|
+
this.resolveTarget(identityApi);
|
|
2192
2095
|
}
|
|
2193
2096
|
getUserId() {
|
|
2194
2097
|
if (!this.target) {
|
|
@@ -2211,42 +2114,29 @@ class AppIdentityProxy {
|
|
|
2211
2114
|
return this.target.getProfile();
|
|
2212
2115
|
}
|
|
2213
2116
|
async getProfileInfo() {
|
|
2214
|
-
|
|
2215
|
-
throw mkError("getProfileInfo");
|
|
2216
|
-
}
|
|
2217
|
-
return this.target.getProfileInfo();
|
|
2117
|
+
return this.waitForTarget.then((target) => target.getProfileInfo());
|
|
2218
2118
|
}
|
|
2219
2119
|
async getBackstageIdentity() {
|
|
2220
|
-
|
|
2221
|
-
throw mkError("getBackstageIdentity");
|
|
2222
|
-
}
|
|
2223
|
-
const identity = await this.target.getBackstageIdentity();
|
|
2120
|
+
const identity = await this.waitForTarget.then((target) => target.getBackstageIdentity());
|
|
2224
2121
|
if (!identity.userEntityRef.match(/^.*:.*\/.*$/)) {
|
|
2225
2122
|
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>'.`);
|
|
2226
2123
|
}
|
|
2227
2124
|
return identity;
|
|
2228
2125
|
}
|
|
2229
2126
|
async getCredentials() {
|
|
2230
|
-
|
|
2231
|
-
throw mkError("getCredentials");
|
|
2232
|
-
}
|
|
2233
|
-
return this.target.getCredentials();
|
|
2127
|
+
return this.waitForTarget.then((target) => target.getCredentials());
|
|
2234
2128
|
}
|
|
2235
2129
|
async getIdToken() {
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
return this.target.getIdToken();
|
|
2130
|
+
return this.waitForTarget.then((target) => {
|
|
2131
|
+
if (!target.getIdToken) {
|
|
2132
|
+
throw new Error("IdentityApi does not implement getIdToken");
|
|
2133
|
+
}
|
|
2134
|
+
logDeprecation("getIdToken");
|
|
2135
|
+
return target.getIdToken();
|
|
2136
|
+
});
|
|
2244
2137
|
}
|
|
2245
2138
|
async signOut() {
|
|
2246
|
-
|
|
2247
|
-
throw mkError("signOut");
|
|
2248
|
-
}
|
|
2249
|
-
await this.target.signOut();
|
|
2139
|
+
await this.waitForTarget.then((target) => target.signOut());
|
|
2250
2140
|
location.reload();
|
|
2251
2141
|
}
|
|
2252
2142
|
}
|
|
@@ -2361,7 +2251,7 @@ class ApiRegistry {
|
|
|
2361
2251
|
}
|
|
2362
2252
|
}
|
|
2363
2253
|
|
|
2364
|
-
function
|
|
2254
|
+
function resolveRouteBindings(bindRoutes) {
|
|
2365
2255
|
const result = /* @__PURE__ */ new Map();
|
|
2366
2256
|
if (bindRoutes) {
|
|
2367
2257
|
const bind = (externalRoutes, targetRoutes) => {
|
|
@@ -2382,6 +2272,7 @@ function generateBoundRoutes(bindRoutes) {
|
|
|
2382
2272
|
}
|
|
2383
2273
|
return result;
|
|
2384
2274
|
}
|
|
2275
|
+
|
|
2385
2276
|
function getBasePath(configApi) {
|
|
2386
2277
|
var _a;
|
|
2387
2278
|
let { pathname } = new URL((_a = configApi.getOptionalString("app.baseUrl")) != null ? _a : "/", "http://dummy.dev");
|
|
@@ -2453,9 +2344,16 @@ class AppManager {
|
|
|
2453
2344
|
}
|
|
2454
2345
|
getProvider() {
|
|
2455
2346
|
const appContext = new AppContextImpl(this);
|
|
2347
|
+
let routesHaveBeenValidated = false;
|
|
2456
2348
|
const Provider = ({ children }) => {
|
|
2457
2349
|
const appThemeApi = useMemo(() => AppThemeSelector.createWithStorage(this.themes), []);
|
|
2458
|
-
const {
|
|
2350
|
+
const {
|
|
2351
|
+
routePaths,
|
|
2352
|
+
routeParents,
|
|
2353
|
+
routeObjects,
|
|
2354
|
+
featureFlags,
|
|
2355
|
+
routeBindings
|
|
2356
|
+
} = useMemo(() => {
|
|
2459
2357
|
const result = traverseElementTree({
|
|
2460
2358
|
root: children,
|
|
2461
2359
|
discoverers: [childDiscoverer, routeElementDiscoverer],
|
|
@@ -2467,12 +2365,19 @@ class AppManager {
|
|
|
2467
2365
|
featureFlags: featureFlagCollector
|
|
2468
2366
|
}
|
|
2469
2367
|
});
|
|
2470
|
-
validateRoutes(result.routePaths, result.routeParents);
|
|
2471
2368
|
result.collectedPlugins.forEach((plugin) => this.plugins.add(plugin));
|
|
2472
2369
|
this.verifyPlugins(this.plugins);
|
|
2473
2370
|
this.getApiHolder();
|
|
2474
|
-
return
|
|
2371
|
+
return {
|
|
2372
|
+
...result,
|
|
2373
|
+
routeBindings: resolveRouteBindings(this.bindRoutes)
|
|
2374
|
+
};
|
|
2475
2375
|
}, [children]);
|
|
2376
|
+
if (!routesHaveBeenValidated) {
|
|
2377
|
+
routesHaveBeenValidated = true;
|
|
2378
|
+
validateRouteParameters(routePaths, routeParents);
|
|
2379
|
+
validateRouteBindings(routeBindings, this.plugins);
|
|
2380
|
+
}
|
|
2476
2381
|
const loadedConfig = useConfigLoader(this.configLoader, this.components, appThemeApi);
|
|
2477
2382
|
const hasConfigApi = "api" in loadedConfig;
|
|
2478
2383
|
if (hasConfigApi) {
|
|
@@ -2518,7 +2423,7 @@ class AppManager {
|
|
|
2518
2423
|
routePaths,
|
|
2519
2424
|
routeParents,
|
|
2520
2425
|
routeObjects,
|
|
2521
|
-
routeBindings
|
|
2426
|
+
routeBindings,
|
|
2522
2427
|
basePath: getBasePath(loadedConfig.api)
|
|
2523
2428
|
}, children))));
|
|
2524
2429
|
};
|