plugins: migrate usage of deprecated IdentityApi methods
Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
---
|
||||
'@backstage/plugin-azure-devops': patch
|
||||
'@backstage/plugin-badges': patch
|
||||
'@backstage/plugin-bazaar': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
'@backstage/plugin-catalog-import': patch
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
'@backstage/plugin-fossa': patch
|
||||
'@backstage/plugin-ilert': patch
|
||||
'@backstage/plugin-kafka': patch
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
'@backstage/plugin-pagerduty': patch
|
||||
'@backstage/plugin-permission-react': patch
|
||||
'@backstage/plugin-rollbar': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
'@backstage/plugin-search': patch
|
||||
'@backstage/plugin-sentry': patch
|
||||
'@backstage/plugin-sonarqube': patch
|
||||
'@backstage/plugin-tech-insights': patch
|
||||
'@backstage/plugin-techdocs': patch
|
||||
'@backstage/plugin-todo': patch
|
||||
---
|
||||
|
||||
Migrated usage of deprecated `IdentityApi` methods.
|
||||
@@ -218,7 +218,7 @@ export class MyApi implements MyInterface {
|
||||
async getMyData() {
|
||||
const backendUrl = this.configApi.getString('backend.baseUrl');
|
||||
|
||||
+ const token = await this.identityApi.getIdToken();
|
||||
+ const { token } = await this.identityApi.getCredentials();
|
||||
const requestUrl = `${backendUrl}/api/data/`;
|
||||
- const response = await fetch(requestUrl);
|
||||
+ const response = await fetch(
|
||||
|
||||
@@ -96,7 +96,7 @@ export class AzureDevOpsClient implements AzureDevOpsApi {
|
||||
const baseUrl = `${await this.discoveryApi.getBaseUrl('azure-devops')}/`;
|
||||
const url = new URL(path, baseUrl);
|
||||
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(url.toString(), {
|
||||
headers: idToken ? { Authorization: `Bearer ${idToken}` } : {},
|
||||
});
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
*/
|
||||
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
export function useUserEmail(): string | undefined {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
return identityApi.getProfile().email;
|
||||
const state = useAsync(() => identityApi.getProfileInfo(), [identityApi]);
|
||||
return state.value?.email;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export class BadgesClient implements BadgesApi {
|
||||
|
||||
public async getEntityBadgeSpecs(entity: Entity): Promise<BadgeSpec[]> {
|
||||
const entityBadgeSpecsUrl = await this.getEntityBadgeSpecsUrl(entity);
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(entityBadgeSpecsUrl, {
|
||||
headers: token
|
||||
? {
|
||||
|
||||
@@ -121,6 +121,7 @@ export class BazaarClient implements BazaarApi {
|
||||
|
||||
async addMember(id: number, userId: string): Promise<void> {
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('bazaar');
|
||||
const { picture } = await this.identityApi.getProfileInfo();
|
||||
|
||||
await fetch(
|
||||
`${baseUrl}/projects/${encodeURIComponent(
|
||||
@@ -132,9 +133,7 @@ export class BazaarClient implements BazaarApi {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
picture: (await this.identityApi.getProfileInfo()).picture,
|
||||
}),
|
||||
body: JSON.stringify({ picture }),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -67,21 +67,12 @@ describe('CatalogImportClient', () => {
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: 'token' }),
|
||||
};
|
||||
const identityApi = {
|
||||
getUserId: () => {
|
||||
return 'user';
|
||||
},
|
||||
getProfile: () => {
|
||||
return {};
|
||||
},
|
||||
getIdToken: () => {
|
||||
return Promise.resolve('token');
|
||||
},
|
||||
signOut: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: 'token' }),
|
||||
};
|
||||
|
||||
const scmIntegrationsApi = ScmIntegrations.fromConfig(
|
||||
|
||||
@@ -173,13 +173,13 @@ the component will become available.\n\nFor more information, read an \
|
||||
}: {
|
||||
repo: string;
|
||||
}): Promise<PartialEntity[]> {
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(
|
||||
`${await this.discoveryApi.getBaseUrl('catalog')}/analyze-location`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(idToken && { Authorization: `Bearer ${idToken}` }),
|
||||
...(token && { Authorization: `Bearer ${token}` }),
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
|
||||
@@ -68,8 +68,12 @@ const mockConfigApi = {
|
||||
getOptionalString: () => '',
|
||||
} as Partial<ConfigApi>;
|
||||
const mockIdentityApi: Partial<IdentityApi> = {
|
||||
getUserId: () => 'guest',
|
||||
getIdToken: async () => undefined,
|
||||
getBackstageIdentity: async () => ({
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/guest',
|
||||
ownershipEntityRefs: [],
|
||||
}),
|
||||
getCredentials: async () => ({ token: undefined }),
|
||||
};
|
||||
const mockCatalogApi: Partial<CatalogApi> = {
|
||||
getEntities: jest.fn().mockImplementation(async () => ({ items: entities })),
|
||||
|
||||
@@ -14,7 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { UserEntity } from '@backstage/catalog-model';
|
||||
import {
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
parseEntityRef,
|
||||
UserEntity,
|
||||
} from '@backstage/catalog-model';
|
||||
import useAsync, { AsyncState } from 'react-use/lib/useAsync';
|
||||
import { catalogApiRef } from '../api';
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
@@ -26,15 +30,13 @@ export function useOwnUser(): AsyncState<UserEntity | undefined> {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const identityApi = useApi(identityApiRef);
|
||||
|
||||
// TODO: get the full entity (or at least the full entity name) from the
|
||||
// identityApi
|
||||
return useAsync(
|
||||
() =>
|
||||
catalogApi.getEntityByName({
|
||||
kind: 'User',
|
||||
namespace: 'default',
|
||||
name: identityApi.getUserId(),
|
||||
}) as Promise<UserEntity | undefined>,
|
||||
[catalogApi, identityApi],
|
||||
);
|
||||
return useAsync(async () => {
|
||||
const identity = await identityApi.getBackstageIdentity();
|
||||
return catalogApi.getEntityByName(
|
||||
parseEntityRef(identity.userEntityRef, {
|
||||
defaultKind: 'User',
|
||||
defaultNamespace: ENTITY_DEFAULT_NAMESPACE,
|
||||
}),
|
||||
) as Promise<UserEntity | undefined>;
|
||||
}, [catalogApi, identityApi]);
|
||||
}
|
||||
|
||||
@@ -28,38 +28,16 @@ const discoveryApi: DiscoveryApi = {
|
||||
},
|
||||
};
|
||||
const identityApi: IdentityApi = {
|
||||
getUserId() {
|
||||
return 'jane-fonda';
|
||||
},
|
||||
getProfile() {
|
||||
return { email: 'jane-fonda@spotify.com' };
|
||||
},
|
||||
async getIdToken() {
|
||||
return Promise.resolve('fake-id-token');
|
||||
},
|
||||
async signOut() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
signOut: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: 'fake-id-token' }),
|
||||
};
|
||||
const guestIdentityApi: IdentityApi = {
|
||||
getUserId() {
|
||||
return 'guest';
|
||||
},
|
||||
getProfile() {
|
||||
return {};
|
||||
},
|
||||
async getIdToken() {
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
async signOut() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
signOut: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: undefined }),
|
||||
};
|
||||
|
||||
describe('CatalogClientWrapper', () => {
|
||||
|
||||
@@ -51,89 +51,108 @@ export class CatalogClientWrapper implements CatalogApi {
|
||||
id: string,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location | undefined> {
|
||||
return await this.client.getLocationById(id, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.getLocationById(
|
||||
id,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async getEntities(
|
||||
request?: CatalogEntitiesRequest,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<CatalogListResponse<Entity>> {
|
||||
return await this.client.getEntities(request, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.getEntities(
|
||||
request,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async getEntityByName(
|
||||
compoundName: EntityName,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Entity | undefined> {
|
||||
return await this.client.getEntityByName(compoundName, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.getEntityByName(
|
||||
compoundName,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async addLocation(
|
||||
request: AddLocationRequest,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<AddLocationResponse> {
|
||||
return await this.client.addLocation(request, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.addLocation(
|
||||
request,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async getOriginLocationByEntity(
|
||||
entity: Entity,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location | undefined> {
|
||||
return await this.client.getOriginLocationByEntity(entity, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.getOriginLocationByEntity(
|
||||
entity,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async getLocationByEntity(
|
||||
entity: Entity,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<Location | undefined> {
|
||||
return await this.client.getLocationByEntity(entity, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.getLocationByEntity(
|
||||
entity,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async removeLocationById(
|
||||
id: string,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<void> {
|
||||
return await this.client.removeLocationById(id, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.removeLocationById(
|
||||
id,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async removeEntityByUid(
|
||||
uid: string,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<void> {
|
||||
return await this.client.removeEntityByUid(uid, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.removeEntityByUid(
|
||||
uid,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async refreshEntity(
|
||||
entityRef: string,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<void> {
|
||||
return await this.client.refreshEntity(entityRef, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.refreshEntity(
|
||||
entityRef,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
async getEntityAncestors(
|
||||
request: CatalogEntityAncestorsRequest,
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<CatalogEntityAncestorsResponse> {
|
||||
return await this.client.getEntityAncestors(request, {
|
||||
token: options?.token ?? (await this.identityApi.getIdToken()),
|
||||
});
|
||||
return await this.client.getEntityAncestors(
|
||||
request,
|
||||
await this.getCredentials(options),
|
||||
);
|
||||
}
|
||||
|
||||
private async getCredentials(
|
||||
options?: CatalogRequestOptions,
|
||||
): Promise<{ token?: string }> {
|
||||
if (options?.token) {
|
||||
return { token: options?.token };
|
||||
}
|
||||
return this.identityApi.getCredentials();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +120,13 @@ describe('DefaultCatalogPage', () => {
|
||||
displayName: 'Display Name',
|
||||
};
|
||||
const identityApi: Partial<IdentityApi> = {
|
||||
getUserId: () => 'tools',
|
||||
getIdToken: async () => undefined,
|
||||
getProfile: () => testProfile,
|
||||
getBackstageIdentity: async () => ({
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/guest',
|
||||
ownershipEntityRefs: ['user:default/guest', 'group:default/tools'],
|
||||
}),
|
||||
getCredentials: async () => ({ token: undefined }),
|
||||
getProfileInfo: async () => testProfile,
|
||||
};
|
||||
const storageApi = MockStorageApi.create();
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.9.9",
|
||||
"@backstage/config": "^0.1.12",
|
||||
"@backstage/core-components": "^0.8.4",
|
||||
"@backstage/core-plugin-api": "^0.5.0",
|
||||
|
||||
@@ -23,7 +23,7 @@ import { IdentityApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
describe('<CostInsightsHeader/>', () => {
|
||||
const identityApi: Partial<IdentityApi> = {
|
||||
getProfile: () => ({
|
||||
getProfileInfo: async () => ({
|
||||
email: 'test-email@example.com',
|
||||
displayName: 'User 1',
|
||||
}),
|
||||
|
||||
@@ -16,16 +16,15 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Typography } from '@material-ui/core';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { useCostInsightsStyles } from '../../utils/styles';
|
||||
import { Group } from '../../types';
|
||||
import {
|
||||
identityApiRef,
|
||||
ProfileInfo,
|
||||
useApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
function name(profile: ProfileInfo | undefined): string {
|
||||
return profile?.displayName || 'Mysterious Stranger';
|
||||
function useDisplayName(): string {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const state = useAsync(() => identityApi.getProfileInfo(), [identityApi]);
|
||||
return state.loading ? '' : state.value?.displayName || 'Mysterious Stranger';
|
||||
}
|
||||
|
||||
type CostInsightsHeaderProps = {
|
||||
@@ -39,7 +38,7 @@ const CostInsightsHeaderNoData = ({
|
||||
owner,
|
||||
groups,
|
||||
}: CostInsightsHeaderProps) => {
|
||||
const profile = useApi(identityApiRef).getProfile();
|
||||
const displayName = useDisplayName();
|
||||
const classes = useCostInsightsStyles();
|
||||
const hasMultipleGroups = groups.length > 1;
|
||||
|
||||
@@ -52,8 +51,8 @@ const CostInsightsHeaderNoData = ({
|
||||
Well this is awkward
|
||||
</Typography>
|
||||
<Typography className={classes.h6Subtle} align="center" gutterBottom>
|
||||
<b>Hey, {name(profile)}!</b> <b>{owner}</b> doesn't seem to have any
|
||||
cloud costs.
|
||||
<b>Hey, {displayName}!</b> <b>{owner}</b> doesn't seem to have any cloud
|
||||
costs.
|
||||
</Typography>
|
||||
{hasMultipleGroups && (
|
||||
<Typography align="center" gutterBottom>
|
||||
@@ -68,7 +67,7 @@ const CostInsightsHeaderAlerts = ({
|
||||
owner,
|
||||
alerts,
|
||||
}: CostInsightsHeaderProps) => {
|
||||
const profile = useApi(identityApiRef).getProfile();
|
||||
const displayName = useDisplayName();
|
||||
const classes = useCostInsightsStyles();
|
||||
|
||||
return (
|
||||
@@ -80,7 +79,7 @@ const CostInsightsHeaderAlerts = ({
|
||||
You have {alerts} thing{alerts > 1 && 's'} to look into
|
||||
</Typography>
|
||||
<Typography className={classes.h6Subtle} align="center" gutterBottom>
|
||||
<b>Hey, {name(profile)}!</b> We've identified{' '}
|
||||
<b>Hey, {displayName}!</b> We've identified{' '}
|
||||
{alerts > 1 ? 'a few things ' : 'one thing '}
|
||||
<b>{owner}</b> should look into next.
|
||||
</Typography>
|
||||
@@ -89,7 +88,7 @@ const CostInsightsHeaderAlerts = ({
|
||||
};
|
||||
|
||||
const CostInsightsHeaderNoAlerts = ({ owner }: CostInsightsHeaderProps) => {
|
||||
const profile = useApi(identityApiRef).getProfile();
|
||||
const displayName = useDisplayName();
|
||||
const classes = useCostInsightsStyles();
|
||||
|
||||
return (
|
||||
@@ -101,7 +100,7 @@ const CostInsightsHeaderNoAlerts = ({ owner }: CostInsightsHeaderProps) => {
|
||||
Your team is doing great
|
||||
</Typography>
|
||||
<Typography className={classes.h6Subtle} align="center" gutterBottom>
|
||||
<b>Hey, {name(profile)}!</b> <b>{owner}</b> is doing well. No major
|
||||
<b>Hey, {displayName}!</b> <b>{owner}</b> is doing well. No major
|
||||
changes this month.
|
||||
</Typography>
|
||||
</>
|
||||
@@ -109,7 +108,7 @@ const CostInsightsHeaderNoAlerts = ({ owner }: CostInsightsHeaderProps) => {
|
||||
};
|
||||
|
||||
export const CostInsightsHeaderNoGroups = () => {
|
||||
const profile = useApi(identityApiRef).getProfile();
|
||||
const displayName = useDisplayName();
|
||||
const classes = useCostInsightsStyles();
|
||||
return (
|
||||
<>
|
||||
@@ -120,8 +119,7 @@ export const CostInsightsHeaderNoGroups = () => {
|
||||
Well this is awkward
|
||||
</Typography>
|
||||
<Typography className={classes.h6Subtle} align="center" gutterBottom>
|
||||
<b>Hey, {name(profile)}!</b> It doesn't look like you belong to any
|
||||
teams.
|
||||
<b>Hey, {displayName}!</b> It doesn't look like you belong to any teams.
|
||||
</Typography>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -26,6 +26,10 @@ import { MapLoadingToProps, useLoading } from './useLoading';
|
||||
import { Group, Maybe } from '../types';
|
||||
import { DefaultLoadingAction } from '../utils/loading';
|
||||
import { useApi, identityApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
|
||||
type GroupsProviderLoadingProps = {
|
||||
dispatchLoadingGroups: (isLoading: boolean) => void;
|
||||
@@ -47,7 +51,7 @@ export const GroupsContext = React.createContext<
|
||||
>(undefined);
|
||||
|
||||
export const GroupsProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
const userId = useApi(identityApiRef).getUserId();
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const client = useApi(costInsightsApiRef);
|
||||
const [error, setError] = useState<Maybe<Error>>(null);
|
||||
const { dispatchLoadingGroups } = useLoading(mapLoadingToProps);
|
||||
@@ -59,6 +63,11 @@ export const GroupsProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
|
||||
async function getUserGroups() {
|
||||
try {
|
||||
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
||||
const { name: userId } = parseEntityRef(userEntityRef, {
|
||||
defaultKind: 'User',
|
||||
defaultNamespace: ENTITY_DEFAULT_NAMESPACE,
|
||||
});
|
||||
const g = await client.getUserGroups(userId);
|
||||
setGroups(g);
|
||||
} catch (e) {
|
||||
@@ -69,7 +78,7 @@ export const GroupsProvider = ({ children }: PropsWithChildren<{}>) => {
|
||||
}
|
||||
|
||||
getUserGroups();
|
||||
}, [userId, client]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [client]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
if (error) {
|
||||
return <Alert severity="error">{error.message}</Alert>;
|
||||
|
||||
@@ -25,8 +25,8 @@ import { UrlPatternDiscovery } from '@backstage/core-app-api';
|
||||
const server = setupServer();
|
||||
|
||||
const identityApi = {
|
||||
async getIdToken() {
|
||||
return Promise.resolve('fake-id-token');
|
||||
async getCredentials() {
|
||||
return { token: 'fake-id-token' };
|
||||
},
|
||||
} as IdentityApi;
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ export class FossaClient implements FossaApi {
|
||||
query: Record<string, any>,
|
||||
): Promise<T> {
|
||||
const apiUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/fossa`;
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(
|
||||
`${apiUrl}/${path}?${new URLSearchParams(query).toString()}`,
|
||||
{
|
||||
|
||||
@@ -27,6 +27,10 @@ import {
|
||||
identityApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { Progress, Link } from '@backstage/core-components';
|
||||
import {
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
|
||||
export const IncidentActionsMenu = ({
|
||||
incident,
|
||||
@@ -40,7 +44,6 @@ export const IncidentActionsMenu = ({
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const alertApi = useApi(alertApiRef);
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const userName = identityApi.getUserId();
|
||||
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
|
||||
const callback = onIncidentChanged || ((_: Incident): void => {});
|
||||
const setProcessing = setIsLoading || ((_: boolean): void => {});
|
||||
@@ -64,6 +67,12 @@ export const IncidentActionsMenu = ({
|
||||
try {
|
||||
handleCloseMenu();
|
||||
setProcessing(true);
|
||||
|
||||
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
||||
const { name: userName } = parseEntityRef(userEntityRef, {
|
||||
defaultKind: 'User',
|
||||
defaultNamespace: ENTITY_DEFAULT_NAMESPACE,
|
||||
});
|
||||
const newIncident = await ilertApi.acceptIncident(incident, userName);
|
||||
alertApi.post({ message: 'Incident accepted.' });
|
||||
|
||||
@@ -79,6 +88,11 @@ export const IncidentActionsMenu = ({
|
||||
try {
|
||||
handleCloseMenu();
|
||||
setProcessing(true);
|
||||
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
||||
const { name: userName } = parseEntityRef(userEntityRef, {
|
||||
defaultKind: 'User',
|
||||
defaultNamespace: ENTITY_DEFAULT_NAMESPACE,
|
||||
});
|
||||
const newIncident = await ilertApi.resolveIncident(incident, userName);
|
||||
alertApi.post({ message: 'Incident resolved.' });
|
||||
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import {
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import Alert from '@material-ui/lab/Alert';
|
||||
import Button from '@material-ui/core/Button';
|
||||
@@ -80,7 +84,6 @@ export const IncidentNewModal = ({
|
||||
const ilertApi = useApi(ilertApiRef);
|
||||
const alertApi = useApi(alertApiRef);
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const userName = identityApi.getUserId();
|
||||
const source = window.location.toString();
|
||||
const classes = useStyles();
|
||||
const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)');
|
||||
@@ -102,6 +105,11 @@ export const IncidentNewModal = ({
|
||||
setIsLoading(true);
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
||||
const { name: userName } = parseEntityRef(userEntityRef, {
|
||||
defaultKind: 'User',
|
||||
defaultNamespace: ENTITY_DEFAULT_NAMESPACE,
|
||||
});
|
||||
await ilertApi.createIncident({
|
||||
integrationKey,
|
||||
summary,
|
||||
|
||||
@@ -31,7 +31,7 @@ export class KafkaBackendClient implements KafkaApi {
|
||||
|
||||
private async internalGet(path: string): Promise<any> {
|
||||
const url = `${await this.discoveryApi.getBaseUrl('kafka')}${path}`;
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
|
||||
@@ -56,7 +56,7 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
requestBody: KubernetesRequestBody,
|
||||
): Promise<any> {
|
||||
const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}${path}`;
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
@@ -79,7 +79,7 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
}
|
||||
|
||||
async getClusters(): Promise<{ name: string; authProvider: string }[]> {
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}/clusters`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
|
||||
@@ -22,17 +22,9 @@ import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { TriggerButton } from './';
|
||||
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
import {
|
||||
alertApiRef,
|
||||
IdentityApi,
|
||||
identityApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { alertApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
describe('TriggerButton', () => {
|
||||
const mockIdentityApi: Partial<IdentityApi> = {
|
||||
getUserId: () => 'guest@example.com',
|
||||
};
|
||||
|
||||
const mockTriggerAlarmFn = jest.fn();
|
||||
const mockPagerDutyApi = {
|
||||
triggerAlarm: mockTriggerAlarmFn,
|
||||
@@ -40,7 +32,6 @@ describe('TriggerButton', () => {
|
||||
|
||||
const apis = TestApiRegistry.from(
|
||||
[alertApiRef, {}],
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[pagerDutyApiRef, mockPagerDutyApi],
|
||||
);
|
||||
|
||||
|
||||
@@ -30,7 +30,11 @@ import {
|
||||
|
||||
describe('TriggerDialog', () => {
|
||||
const mockIdentityApi: Partial<IdentityApi> = {
|
||||
getUserId: () => 'guest@example.com',
|
||||
getBackstageIdentity: async () => ({
|
||||
type: 'user',
|
||||
userEntityRef: 'user:default/guest',
|
||||
ownershipEntityRefs: [],
|
||||
}),
|
||||
};
|
||||
|
||||
const mockTriggerAlarmFn = jest.fn();
|
||||
@@ -89,7 +93,7 @@ describe('TriggerDialog', () => {
|
||||
entity!.metadata!.annotations!['pagerduty.com/integration-key'],
|
||||
source: window.location.toString(),
|
||||
description,
|
||||
userName: 'guest@example.com',
|
||||
userName: 'guest',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,6 +34,10 @@ import {
|
||||
alertApiRef,
|
||||
identityApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
ENTITY_DEFAULT_NAMESPACE,
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
|
||||
type Props = {
|
||||
showDialog: boolean;
|
||||
@@ -49,18 +53,23 @@ export const TriggerDialog = ({
|
||||
const { name, integrationKey } = usePagerdutyEntity();
|
||||
const alertApi = useApi(alertApiRef);
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const userName = identityApi.getUserId();
|
||||
const api = useApi(pagerDutyApiRef);
|
||||
const [description, setDescription] = useState<string>('');
|
||||
|
||||
const [{ value, loading, error }, handleTriggerAlarm] = useAsyncFn(
|
||||
async (descriptions: string) =>
|
||||
async (descriptions: string) => {
|
||||
const { userEntityRef } = await identityApi.getBackstageIdentity();
|
||||
const { name: userName } = parseEntityRef(userEntityRef, {
|
||||
defaultKind: 'User',
|
||||
defaultNamespace: ENTITY_DEFAULT_NAMESPACE,
|
||||
});
|
||||
await api.triggerAlarm({
|
||||
integrationKey: integrationKey as string,
|
||||
source: window.location.toString(),
|
||||
description: descriptions,
|
||||
userName,
|
||||
}),
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const descriptionChanged = (
|
||||
@@ -73,7 +82,7 @@ export const TriggerDialog = ({
|
||||
if (value) {
|
||||
(async () => {
|
||||
alertApi.post({
|
||||
message: `Alarm successfully triggered by ${userName}`,
|
||||
message: `Alarm successfully triggered`,
|
||||
});
|
||||
|
||||
handleDialog();
|
||||
@@ -83,7 +92,7 @@ export const TriggerDialog = ({
|
||||
onIncidentCreated?.();
|
||||
})();
|
||||
}
|
||||
}, [value, alertApi, handleDialog, userName, onIncidentCreated]);
|
||||
}, [value, alertApi, handleDialog, onIncidentCreated]);
|
||||
|
||||
if (error) {
|
||||
alertApi.post({
|
||||
|
||||
@@ -45,9 +45,10 @@ export class IdentityPermissionApi implements PermissionApi {
|
||||
}
|
||||
|
||||
async authorize(request: AuthorizeRequest): Promise<AuthorizeResponse> {
|
||||
const response = await this.permissionClient.authorize([request], {
|
||||
token: await this.identityApi.getIdToken(),
|
||||
});
|
||||
const response = await this.permissionClient.authorize(
|
||||
[request],
|
||||
await this.identityApi.getCredentials(),
|
||||
);
|
||||
return response[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ export class RollbarClient implements RollbarApi {
|
||||
|
||||
private async get(path: string): Promise<any> {
|
||||
const url = `${await this.discoveryApi.getBaseUrl('rollbar')}${path}`;
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(url, {
|
||||
headers: idToken ? { Authorization: `Bearer ${idToken}` } : {},
|
||||
});
|
||||
|
||||
@@ -124,7 +124,7 @@ export class ScaffolderClient implements ScaffolderApi {
|
||||
): Promise<TemplateParameterSchema> {
|
||||
const { namespace, kind, name } = templateName;
|
||||
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');
|
||||
const templatePath = [namespace, kind, name]
|
||||
.map(s => encodeURIComponent(s))
|
||||
@@ -156,7 +156,7 @@ export class ScaffolderClient implements ScaffolderApi {
|
||||
templateName: string,
|
||||
values: Record<string, any>,
|
||||
): Promise<string> {
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const url = `${await this.discoveryApi.getBaseUrl('scaffolder')}/v2/tasks`;
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
@@ -178,7 +178,7 @@ export class ScaffolderClient implements ScaffolderApi {
|
||||
}
|
||||
|
||||
async getTask(taskId: string) {
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');
|
||||
const url = `${baseUrl}/v2/tasks/${encodeURIComponent(taskId)}`;
|
||||
const response = await fetch(url, {
|
||||
@@ -295,7 +295,7 @@ export class ScaffolderClient implements ScaffolderApi {
|
||||
*/
|
||||
async listActions(): Promise<ListActionsResponse> {
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('scaffolder');
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(`${baseUrl}/v2/actions`, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ export class SearchClient implements SearchApi {
|
||||
}
|
||||
|
||||
async query(query: SearchQuery): Promise<SearchResultSet> {
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const queryString = qs.stringify(query);
|
||||
const url = `${await this.discoveryApi.getBaseUrl(
|
||||
'search/query',
|
||||
|
||||
@@ -55,7 +55,7 @@ export class ProductionSentryApi implements SentryApi {
|
||||
if (!this.identityApi) {
|
||||
return {};
|
||||
}
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
return {
|
||||
headers: {
|
||||
authorization: `Bearer ${token}`,
|
||||
|
||||
@@ -25,38 +25,16 @@ import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
const server = setupServer();
|
||||
|
||||
const identityApiAuthenticated: IdentityApi = {
|
||||
getUserId() {
|
||||
return 'jane-fonda';
|
||||
},
|
||||
getProfile() {
|
||||
return { email: 'jane-fonda@spotify.com' };
|
||||
},
|
||||
async getIdToken() {
|
||||
return Promise.resolve('fake-id-token');
|
||||
},
|
||||
async signOut() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
signOut: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: 'fake-id-token' }),
|
||||
};
|
||||
const identityApiGuest: IdentityApi = {
|
||||
getUserId() {
|
||||
return 'guest';
|
||||
},
|
||||
getProfile() {
|
||||
return {};
|
||||
},
|
||||
async getIdToken() {
|
||||
return Promise.resolve(undefined);
|
||||
},
|
||||
async signOut() {
|
||||
return Promise.resolve();
|
||||
},
|
||||
signOut: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
getCredentials: jest.fn(),
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: undefined }),
|
||||
};
|
||||
|
||||
describe('SonarQubeClient', () => {
|
||||
|
||||
@@ -42,7 +42,7 @@ export class SonarQubeClient implements SonarQubeApi {
|
||||
path: string,
|
||||
query: { [key in string]: any },
|
||||
): Promise<T | undefined> {
|
||||
const idToken = await this.identityApi.getIdToken();
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
|
||||
const apiUrl = `${await this.discoveryApi.getBaseUrl('proxy')}/sonarqube`;
|
||||
const response = await fetch(
|
||||
|
||||
@@ -20,24 +20,15 @@ import { TestApiRegistry, wrapInTestApp } from '@backstage/test-utils';
|
||||
import { splunkOnCallApiRef } from '../../api';
|
||||
import { MOCK_TEAM, MOCK_INCIDENT } from '../../api/mocks';
|
||||
|
||||
import {
|
||||
alertApiRef,
|
||||
IdentityApi,
|
||||
identityApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { alertApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiProvider } from '@backstage/core-app-api';
|
||||
|
||||
const mockIdentityApi: Partial<IdentityApi> = {
|
||||
getUserId: () => 'test',
|
||||
};
|
||||
|
||||
const mockSplunkOnCallApi = {
|
||||
getIncidents: jest.fn(),
|
||||
getTeams: jest.fn(),
|
||||
};
|
||||
const apis = TestApiRegistry.from(
|
||||
[alertApiRef, {}],
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[splunkOnCallApiRef, mockSplunkOnCallApi],
|
||||
);
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ export class TechInsightsClient implements TechInsightsApi {
|
||||
|
||||
async getAllChecks(): Promise<Check[]> {
|
||||
const url = await this.discoveryApi.getBaseUrl('tech-insights');
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(`${url}/checks`, {
|
||||
headers: token
|
||||
? {
|
||||
@@ -78,7 +78,7 @@ export class TechInsightsClient implements TechInsightsApi {
|
||||
checks?: Check[],
|
||||
): Promise<CheckResult[]> {
|
||||
const url = await this.discoveryApi.getBaseUrl('tech-insights');
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const { namespace, kind, name } = entityParams;
|
||||
const checkIds = checks ? checks.map(check => check.id) : [];
|
||||
const requestBody = { checks: checkIds.length > 0 ? checkIds : undefined };
|
||||
|
||||
@@ -40,9 +40,6 @@ describe('TechDocsStorageClient', () => {
|
||||
} as Partial<Config>;
|
||||
const discoveryApi = UrlPatternDiscovery.compile(mockBaseUrl);
|
||||
const identityApi: jest.Mocked<IdentityApi> = {
|
||||
getIdToken: jest.fn(),
|
||||
getProfile: jest.fn(),
|
||||
getUserId: jest.fn(),
|
||||
signOut: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
@@ -51,6 +48,7 @@ describe('TechDocsStorageClient', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
identityApi.getCredentials.mockResolvedValue({ token: undefined });
|
||||
});
|
||||
|
||||
it('should return correct base url based on defined storage', async () => {
|
||||
@@ -122,7 +120,7 @@ describe('TechDocsStorageClient', () => {
|
||||
},
|
||||
);
|
||||
|
||||
identityApi.getIdToken.mockResolvedValue('token');
|
||||
identityApi.getCredentials.mockResolvedValue({ token: 'token' });
|
||||
|
||||
await storageApi.syncEntityDocs(mockEntity);
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export class TechDocsClient implements TechDocsApi {
|
||||
|
||||
const apiOrigin = await this.getApiOrigin();
|
||||
const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`;
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
|
||||
const request = await fetch(`${requestUrl}`, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
@@ -93,7 +93,7 @@ export class TechDocsClient implements TechDocsApi {
|
||||
|
||||
const apiOrigin = await this.getApiOrigin();
|
||||
const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`;
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
|
||||
const request = await fetch(`${requestUrl}`, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
@@ -160,7 +160,7 @@ export class TechDocsStorageClient implements TechDocsStorageApi {
|
||||
|
||||
const storageUrl = await this.getStorageUrl();
|
||||
const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`;
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
|
||||
const request = await fetch(
|
||||
`${url.endsWith('/') ? url : `${url}/`}index.html`,
|
||||
@@ -207,7 +207,7 @@ export class TechDocsStorageClient implements TechDocsStorageApi {
|
||||
|
||||
const apiOrigin = await this.getApiOrigin();
|
||||
const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`;
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Polyfill is used to add support for custom headers and auth
|
||||
|
||||
@@ -46,7 +46,7 @@ export class TodoClient implements TodoApi {
|
||||
async listTodos(options: TodoListOptions): Promise<TodoListResult> {
|
||||
const { entity, offset, limit, orderBy, filters } = options;
|
||||
const baseUrl = await this.discoveryApi.getBaseUrl('todo');
|
||||
const token = await this.identityApi.getIdToken();
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
|
||||
const query = new URLSearchParams();
|
||||
if (entity) {
|
||||
|
||||
Reference in New Issue
Block a user