plugins: migrate usage of deprecated IdentityApi methods

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-01-04 23:08:05 +01:00
parent 2916a83b9c
commit 51fbedc445
38 changed files with 221 additions and 195 deletions
+25
View File
@@ -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;
}
+1 -1
View File
@@ -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
? {
+2 -3
View File
@@ -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 -12
View File
@@ -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', () => {
+49 -30
View File
@@ -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();
+1
View File
@@ -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>
</>
);
+11 -2
View File
@@ -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>;
+2 -2
View File
@@ -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;
+1 -1
View File
@@ -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,
+1 -1
View File
@@ -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];
}
}
+1 -1
View File
@@ -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}` } : {},
});
+4 -4
View File
@@ -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}` } : {},
});
+1 -1
View File
@@ -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',
+1 -1
View File
@@ -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', () => {
+1 -1
View File
@@ -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 };
+2 -4
View File
@@ -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);
+4 -4
View File
@@ -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
+1 -1
View File
@@ -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) {