Merge pull request #24878 from alexef/more-fetch
fix: use fetchApi instead of explicit identityApi token in remaining plugins
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-react': minor
|
||||
'@backstage/plugin-catalog-import': minor
|
||||
'@backstage/plugin-kubernetes': patch
|
||||
'@backstage/plugin-search': patch
|
||||
---
|
||||
|
||||
Migrate from identityApi to fetchApi in frontend plugins.
|
||||
@@ -13,8 +13,8 @@ import { ConfigApi } from '@backstage/core-plugin-api';
|
||||
import { Controller } from 'react-hook-form';
|
||||
import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { FetchApi } from '@backstage/core-plugin-api';
|
||||
import { FieldErrors } from 'react-hook-form';
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { InfoCardVariants } from '@backstage/core-components';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { default as React_2 } from 'react';
|
||||
@@ -102,7 +102,7 @@ export class CatalogImportClient implements CatalogImportApi {
|
||||
constructor(options: {
|
||||
discoveryApi: DiscoveryApi;
|
||||
scmAuthApi: ScmAuthApi;
|
||||
identityApi: IdentityApi;
|
||||
fetchApi: FetchApi;
|
||||
scmIntegrationsApi: ScmIntegrationRegistry;
|
||||
catalogApi: CatalogApi;
|
||||
configApi: ConfigApi;
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
configApiRef,
|
||||
createApiFactory,
|
||||
discoveryApiRef,
|
||||
identityApiRef,
|
||||
fetchApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
compatWrapper,
|
||||
@@ -55,7 +55,7 @@ const catalogImportApi = createApiExtension({
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
scmAuthApi: scmAuthApiRef,
|
||||
identityApi: identityApiRef,
|
||||
fetchApi: fetchApiRef,
|
||||
scmIntegrationsApi: scmIntegrationsApiRef,
|
||||
catalogApi: catalogApiRef,
|
||||
configApi: configApiRef,
|
||||
@@ -63,7 +63,7 @@ const catalogImportApi = createApiExtension({
|
||||
factory: ({
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
scmIntegrationsApi,
|
||||
catalogApi,
|
||||
configApi,
|
||||
@@ -72,7 +72,7 @@ const catalogImportApi = createApiExtension({
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
scmIntegrationsApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
catalogApi,
|
||||
configApi,
|
||||
}),
|
||||
|
||||
@@ -50,7 +50,7 @@ import { ConfigReader, UrlPatternDiscovery } from '@backstage/core-app-api';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import { ScmAuthApi } from '@backstage/integration-react';
|
||||
import { CatalogApi } from '@backstage/plugin-catalog-react';
|
||||
import { setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import { MockFetchApi, setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import { Octokit } from '@octokit/rest';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
@@ -66,14 +66,7 @@ describe('CatalogImportClient', () => {
|
||||
const scmAuthApi: jest.Mocked<ScmAuthApi> = {
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: 'token' }),
|
||||
};
|
||||
const identityApi = {
|
||||
signOut: () => {
|
||||
return Promise.resolve();
|
||||
},
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
getCredentials: jest.fn().mockResolvedValue({ token: 'token' }),
|
||||
};
|
||||
const fetchApi = new MockFetchApi();
|
||||
|
||||
const scmIntegrationsApi = ScmIntegrations.fromConfig(
|
||||
new ConfigReader({
|
||||
@@ -110,7 +103,7 @@ describe('CatalogImportClient', () => {
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
scmIntegrationsApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
catalogApi: catalogApi as Partial<CatalogApi> as CatalogApi,
|
||||
configApi: new ConfigReader({
|
||||
app: {
|
||||
@@ -456,7 +449,7 @@ describe('CatalogImportClient', () => {
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
scmIntegrationsApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
catalogApi: catalogApi as Partial<CatalogApi> as CatalogApi,
|
||||
configApi: new ConfigReader({
|
||||
catalog: {
|
||||
@@ -659,7 +652,7 @@ describe('CatalogImportClient', () => {
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
scmIntegrationsApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
catalogApi: catalogApi as Partial<CatalogApi> as CatalogApi,
|
||||
configApi: new ConfigReader({
|
||||
catalog: {
|
||||
@@ -745,7 +738,7 @@ describe('CatalogImportClient', () => {
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
scmIntegrationsApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
catalogApi: catalogApi as Partial<CatalogApi> as CatalogApi,
|
||||
configApi: new ConfigReader({
|
||||
catalog: {
|
||||
|
||||
@@ -15,11 +15,7 @@
|
||||
*/
|
||||
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import {
|
||||
ConfigApi,
|
||||
DiscoveryApi,
|
||||
IdentityApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { ConfigApi, DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
GithubIntegrationConfig,
|
||||
ScmIntegrationRegistry,
|
||||
@@ -41,7 +37,7 @@ import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
*/
|
||||
export class CatalogImportClient implements CatalogImportApi {
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
private readonly identityApi: IdentityApi;
|
||||
private readonly fetchApi: FetchApi;
|
||||
private readonly scmAuthApi: ScmAuthApi;
|
||||
private readonly scmIntegrationsApi: ScmIntegrationRegistry;
|
||||
private readonly catalogApi: CatalogApi;
|
||||
@@ -50,14 +46,14 @@ export class CatalogImportClient implements CatalogImportApi {
|
||||
constructor(options: {
|
||||
discoveryApi: DiscoveryApi;
|
||||
scmAuthApi: ScmAuthApi;
|
||||
identityApi: IdentityApi;
|
||||
fetchApi: FetchApi;
|
||||
scmIntegrationsApi: ScmIntegrationRegistry;
|
||||
catalogApi: CatalogApi;
|
||||
configApi: ConfigApi;
|
||||
}) {
|
||||
this.discoveryApi = options.discoveryApi;
|
||||
this.scmAuthApi = options.scmAuthApi;
|
||||
this.identityApi = options.identityApi;
|
||||
this.fetchApi = options.fetchApi;
|
||||
this.scmIntegrationsApi = options.scmIntegrationsApi;
|
||||
this.catalogApi = options.catalogApi;
|
||||
this.configApi = options.configApi;
|
||||
@@ -206,29 +202,29 @@ the component will become available.\n\nFor more information, read an \
|
||||
private async analyzeLocation(options: {
|
||||
repo: string;
|
||||
}): Promise<AnalyzeLocationResponse> {
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(
|
||||
`${await this.discoveryApi.getBaseUrl('catalog')}/analyze-location`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(token && { Authorization: `Bearer ${token}` }),
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
location: { type: 'url', target: options.repo },
|
||||
...(this.configApi.getOptionalString(
|
||||
'catalog.import.entityFilename',
|
||||
) && {
|
||||
catalogFilename: this.configApi.getOptionalString(
|
||||
const response = await this.fetchApi
|
||||
.fetch(
|
||||
`${await this.discoveryApi.getBaseUrl('catalog')}/analyze-location`,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
location: { type: 'url', target: options.repo },
|
||||
...(this.configApi.getOptionalString(
|
||||
'catalog.import.entityFilename',
|
||||
),
|
||||
) && {
|
||||
catalogFilename: this.configApi.getOptionalString(
|
||||
'catalog.import.entityFilename',
|
||||
),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
).catch(e => {
|
||||
throw new Error(`Failed to generate entity definitions, ${e.message}`);
|
||||
});
|
||||
},
|
||||
)
|
||||
.catch(e => {
|
||||
throw new Error(`Failed to generate entity definitions, ${e.message}`);
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`Failed to generate entity definitions. Received http response ${response.status}: ${response.statusText}`,
|
||||
|
||||
@@ -25,22 +25,8 @@ import { catalogImportApiRef, CatalogImportClient } from '../../api';
|
||||
import { DefaultImportPage } from './DefaultImportPage';
|
||||
|
||||
describe('<DefaultImportPage />', () => {
|
||||
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(),
|
||||
const fetchApi = {
|
||||
fetch: jest.fn(),
|
||||
};
|
||||
|
||||
let apis: TestApiRegistry;
|
||||
@@ -56,7 +42,7 @@ describe('<DefaultImportPage />', () => {
|
||||
scmAuthApi: {
|
||||
getCredentials: async () => ({ token: 'token', headers: {} }),
|
||||
},
|
||||
identityApi,
|
||||
fetchApi,
|
||||
scmIntegrationsApi: {} as any,
|
||||
catalogApi: {} as any,
|
||||
configApi: {} as any,
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { ApiProvider, ConfigReader } from '@backstage/core-app-api';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { FetchApi, configApiRef } from '@backstage/core-plugin-api';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils';
|
||||
import { screen } from '@testing-library/react';
|
||||
@@ -31,22 +31,8 @@ jest.mock('react-router-dom', () => ({
|
||||
}));
|
||||
|
||||
describe('<ImportPage />', () => {
|
||||
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(),
|
||||
const fetchApi: FetchApi = {
|
||||
fetch: jest.fn(),
|
||||
};
|
||||
|
||||
let apis: TestApiRegistry;
|
||||
@@ -59,7 +45,7 @@ describe('<ImportPage />', () => {
|
||||
catalogImportApiRef,
|
||||
new CatalogImportClient({
|
||||
discoveryApi: {} as any,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
scmAuthApi: {} as any,
|
||||
scmIntegrationsApi: {} as any,
|
||||
catalogApi: {} as any,
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
createRoutableExtension,
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
identityApiRef,
|
||||
fetchApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
scmAuthApiRef,
|
||||
@@ -48,7 +48,7 @@ export const catalogImportPlugin = createPlugin({
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
scmAuthApi: scmAuthApiRef,
|
||||
identityApi: identityApiRef,
|
||||
fetchApi: fetchApiRef,
|
||||
scmIntegrationsApi: scmIntegrationsApiRef,
|
||||
catalogApi: catalogApiRef,
|
||||
configApi: configApiRef,
|
||||
@@ -56,7 +56,7 @@ export const catalogImportPlugin = createPlugin({
|
||||
factory: ({
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
scmIntegrationsApi,
|
||||
catalogApi,
|
||||
configApi,
|
||||
@@ -65,7 +65,7 @@ export const catalogImportPlugin = createPlugin({
|
||||
discoveryApi,
|
||||
scmAuthApi,
|
||||
scmIntegrationsApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
catalogApi,
|
||||
configApi,
|
||||
}),
|
||||
|
||||
@@ -16,10 +16,10 @@ import { DetectedErrorsByCluster } from '@backstage/plugin-kubernetes-common';
|
||||
import { DiscoveryApi } from '@backstage/core-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Event as Event_2 } from 'kubernetes-models/v1';
|
||||
import { FetchApi } from '@backstage/core-plugin-api';
|
||||
import { GroupedResponses } from '@backstage/plugin-kubernetes-common';
|
||||
import { IContainer } from 'kubernetes-models/v1';
|
||||
import { IContainerStatus } from 'kubernetes-models/v1';
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { IIoK8sApimachineryPkgApisMetaV1ObjectMeta } from '@kubernetes-models/apimachinery/apis/meta/v1/ObjectMeta';
|
||||
import { IObjectMeta } from '@kubernetes-models/apimachinery/apis/meta/v1/ObjectMeta';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
@@ -402,7 +402,7 @@ export const kubernetesAuthProvidersApiRef: ApiRef<KubernetesAuthProvidersApi>;
|
||||
export class KubernetesBackendClient implements KubernetesApi {
|
||||
constructor(options: {
|
||||
discoveryApi: DiscoveryApi;
|
||||
identityApi: IdentityApi;
|
||||
fetchApi: FetchApi;
|
||||
kubernetesAuthProvidersApi: KubernetesAuthProvidersApi;
|
||||
});
|
||||
// (undocumented)
|
||||
|
||||
@@ -19,7 +19,7 @@ import { KubernetesBackendClient } from './KubernetesBackendClient';
|
||||
import { rest } from 'msw';
|
||||
import { UrlPatternDiscovery } from '@backstage/core-app-api';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import { MockFetchApi, setupRequestMockHandlers } from '@backstage/test-utils';
|
||||
import {
|
||||
CustomObjectsByEntityRequest,
|
||||
KubernetesRequestBody,
|
||||
@@ -44,6 +44,7 @@ describe('KubernetesBackendClient', () => {
|
||||
getBackstageIdentity: jest.fn(),
|
||||
signOut: jest.fn(),
|
||||
};
|
||||
const fetchApi = new MockFetchApi({ injectIdentityAuth: { identityApi } });
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
@@ -51,7 +52,7 @@ describe('KubernetesBackendClient', () => {
|
||||
discoveryApi: UrlPatternDiscovery.compile(
|
||||
'http://localhost:1234/api/{{ pluginId }}',
|
||||
),
|
||||
identityApi,
|
||||
fetchApi,
|
||||
kubernetesAuthProvidersApi,
|
||||
});
|
||||
mockResponse = {
|
||||
@@ -454,6 +455,7 @@ describe('KubernetesBackendClient', () => {
|
||||
});
|
||||
|
||||
it('hits the /proxy API with serviceAccount as auth provider', async () => {
|
||||
identityApi.getCredentials.mockResolvedValue({ token: 'idToken' });
|
||||
worker.use(
|
||||
rest.get(
|
||||
'http://localhost:1234/api/kubernetes/clusters',
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
WorkloadsByEntityRequest,
|
||||
CustomObjectsByEntityRequest,
|
||||
} from '@backstage/plugin-kubernetes-common';
|
||||
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { KubernetesAuthProvidersApi } from '../kubernetes-auth-provider';
|
||||
import { NotFoundError } from '@backstage/errors';
|
||||
@@ -29,16 +29,16 @@ import { NotFoundError } from '@backstage/errors';
|
||||
/** @public */
|
||||
export class KubernetesBackendClient implements KubernetesApi {
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
private readonly identityApi: IdentityApi;
|
||||
private readonly fetchApi: FetchApi;
|
||||
private readonly kubernetesAuthProvidersApi: KubernetesAuthProvidersApi;
|
||||
|
||||
constructor(options: {
|
||||
discoveryApi: DiscoveryApi;
|
||||
identityApi: IdentityApi;
|
||||
fetchApi: FetchApi;
|
||||
kubernetesAuthProvidersApi: KubernetesAuthProvidersApi;
|
||||
}) {
|
||||
this.discoveryApi = options.discoveryApi;
|
||||
this.identityApi = options.identityApi;
|
||||
this.fetchApi = options.fetchApi;
|
||||
this.kubernetesAuthProvidersApi = options.kubernetesAuthProvidersApi;
|
||||
}
|
||||
|
||||
@@ -62,12 +62,10 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
|
||||
private async postRequired(path: string, requestBody: any): Promise<any> {
|
||||
const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}${path}`;
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const response = await fetch(url, {
|
||||
const response = await this.fetchApi.fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
...(idToken && { Authorization: `Bearer ${idToken}` }),
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
});
|
||||
@@ -130,14 +128,8 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
}
|
||||
|
||||
async getClusters(): Promise<{ name: string; authProvider: string }[]> {
|
||||
const { token: idToken } = await this.identityApi.getCredentials();
|
||||
const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}/clusters`;
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...(idToken && { Authorization: `Bearer ${idToken}` }),
|
||||
},
|
||||
});
|
||||
const response = await this.fetchApi.fetch(url);
|
||||
|
||||
return (await this.handleResponse(response)).items;
|
||||
}
|
||||
@@ -157,15 +149,13 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
const url = `${await this.discoveryApi.getBaseUrl('kubernetes')}/proxy${
|
||||
options.path
|
||||
}`;
|
||||
const identityResponse = await this.identityApi.getCredentials();
|
||||
const headers = KubernetesBackendClient.getKubernetesHeaders(
|
||||
options,
|
||||
kubernetesCredentials?.token,
|
||||
identityResponse,
|
||||
authProvider,
|
||||
oidcTokenProvider,
|
||||
);
|
||||
return await fetch(url, { ...options.init, headers });
|
||||
return await this.fetchApi.fetch(url, { ...options.init, headers });
|
||||
}
|
||||
|
||||
private static getKubernetesHeaders(
|
||||
@@ -175,7 +165,6 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
init?: RequestInit;
|
||||
},
|
||||
k8sToken: string | undefined,
|
||||
identityResponse: { token?: string },
|
||||
authProvider: string,
|
||||
oidcTokenProvider: string | undefined,
|
||||
) {
|
||||
@@ -190,9 +179,6 @@ export class KubernetesBackendClient implements KubernetesApi {
|
||||
...(k8sToken && {
|
||||
[kubernetesAuthHeader]: k8sToken,
|
||||
}),
|
||||
...(identityResponse.token && {
|
||||
Authorization: `Bearer ${identityResponse.token}`,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -30,13 +30,13 @@ import {
|
||||
createPlugin,
|
||||
createRouteRef,
|
||||
discoveryApiRef,
|
||||
identityApiRef,
|
||||
gitlabAuthApiRef,
|
||||
googleAuthApiRef,
|
||||
microsoftAuthApiRef,
|
||||
oktaAuthApiRef,
|
||||
oneloginAuthApiRef,
|
||||
createRoutableExtension,
|
||||
fetchApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootCatalogKubernetesRouteRef = createRouteRef({
|
||||
@@ -50,13 +50,13 @@ export const kubernetesPlugin = createPlugin({
|
||||
api: kubernetesApiRef,
|
||||
deps: {
|
||||
discoveryApi: discoveryApiRef,
|
||||
identityApi: identityApiRef,
|
||||
fetchApi: fetchApiRef,
|
||||
kubernetesAuthProvidersApi: kubernetesAuthProvidersApiRef,
|
||||
},
|
||||
factory: ({ discoveryApi, identityApi, kubernetesAuthProvidersApi }) =>
|
||||
factory: ({ discoveryApi, fetchApi, kubernetesAuthProvidersApi }) =>
|
||||
new KubernetesBackendClient({
|
||||
discoveryApi,
|
||||
identityApi,
|
||||
fetchApi,
|
||||
kubernetesAuthProvidersApi,
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -32,9 +32,9 @@ import {
|
||||
import {
|
||||
useApi,
|
||||
DiscoveryApi,
|
||||
IdentityApi,
|
||||
discoveryApiRef,
|
||||
identityApiRef,
|
||||
FetchApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
import {
|
||||
@@ -81,12 +81,12 @@ export const searchApi = createApiExtension({
|
||||
api: searchApiRef,
|
||||
deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
|
||||
factory: ({
|
||||
identityApi,
|
||||
fetchApi,
|
||||
discoveryApi,
|
||||
}: {
|
||||
identityApi: IdentityApi;
|
||||
fetchApi: FetchApi;
|
||||
discoveryApi: DiscoveryApi;
|
||||
}) => new SearchClient({ discoveryApi, identityApi }),
|
||||
}) => new SearchClient({ discoveryApi, fetchApi }),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { MockFetchApi } from '@backstage/test-utils';
|
||||
import { SearchClient } from './apis';
|
||||
|
||||
describe('apis', () => {
|
||||
@@ -26,47 +27,35 @@ describe('apis', () => {
|
||||
const baseUrl = 'https://base-url.com/';
|
||||
const getBaseUrl = jest.fn().mockResolvedValue(baseUrl);
|
||||
|
||||
const token = 'AUTHTOKEN';
|
||||
const withToken = jest.fn().mockResolvedValue({ token });
|
||||
const withoutToken = jest.fn().mockResolvedValue({ token: undefined });
|
||||
const createIdentityApiMock = (getCredentials: any) => ({
|
||||
signOut: jest.fn(),
|
||||
const identityApi = {
|
||||
getCredentials: jest.fn(),
|
||||
getProfileInfo: jest.fn(),
|
||||
getBackstageIdentity: jest.fn(),
|
||||
getCredentials,
|
||||
signOut: jest.fn(),
|
||||
};
|
||||
const json = jest.fn();
|
||||
const mockFetch = jest.fn().mockResolvedValue({
|
||||
ok: true,
|
||||
json,
|
||||
});
|
||||
const fetchApi = new MockFetchApi({
|
||||
baseImplementation: mockFetch,
|
||||
injectIdentityAuth: { identityApi },
|
||||
});
|
||||
|
||||
const client = new SearchClient({
|
||||
discoveryApi: { getBaseUrl },
|
||||
identityApi: createIdentityApiMock(withoutToken),
|
||||
});
|
||||
|
||||
const json = jest.fn();
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = jest.fn().mockResolvedValue({ json, ok: true });
|
||||
|
||||
afterAll(() => {
|
||||
window.fetch = originalFetch;
|
||||
fetchApi,
|
||||
});
|
||||
|
||||
it('Fetch is called with expected URL (including stringified Q params)', async () => {
|
||||
identityApi.getCredentials.mockResolvedValue({});
|
||||
await client.query(query);
|
||||
expect(getBaseUrl).toHaveBeenLastCalledWith('search');
|
||||
expect(fetch).toHaveBeenLastCalledWith(`${baseUrl}/query?term=`, {
|
||||
headers: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('Sets Authorization if token is available', async () => {
|
||||
const authedClient = new SearchClient({
|
||||
discoveryApi: { getBaseUrl },
|
||||
identityApi: createIdentityApiMock(withToken),
|
||||
});
|
||||
await authedClient.query(query);
|
||||
expect(getBaseUrl).toHaveBeenLastCalledWith('search');
|
||||
expect(fetch).toHaveBeenLastCalledWith(`${baseUrl}/query?term=`, {
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
});
|
||||
expect(mockFetch).toHaveBeenLastCalledWith(
|
||||
`${baseUrl}/query?term=`,
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('Resolves JSON from fetch response', async () => {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { DiscoveryApi, FetchApi } from '@backstage/core-plugin-api';
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
import { SearchApi } from '@backstage/plugin-search-react';
|
||||
import { SearchQuery, SearchResultSet } from '@backstage/plugin-search-common';
|
||||
@@ -23,25 +23,19 @@ import qs from 'qs';
|
||||
|
||||
export class SearchClient implements SearchApi {
|
||||
private readonly discoveryApi: DiscoveryApi;
|
||||
private readonly identityApi: IdentityApi;
|
||||
private readonly fetchApi: FetchApi;
|
||||
|
||||
constructor(options: {
|
||||
discoveryApi: DiscoveryApi;
|
||||
identityApi: IdentityApi;
|
||||
}) {
|
||||
constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {
|
||||
this.discoveryApi = options.discoveryApi;
|
||||
this.identityApi = options.identityApi;
|
||||
this.fetchApi = options.fetchApi;
|
||||
}
|
||||
|
||||
async query(query: SearchQuery): Promise<SearchResultSet> {
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
const queryString = qs.stringify(query);
|
||||
const url = `${await this.discoveryApi.getBaseUrl(
|
||||
'search',
|
||||
)}/query?${queryString}`;
|
||||
const response = await fetch(url, {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
});
|
||||
const response = await this.fetchApi.fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
throw await ResponseError.fromResponse(response);
|
||||
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
createRoutableExtension,
|
||||
discoveryApiRef,
|
||||
createComponentExtension,
|
||||
identityApiRef,
|
||||
fetchApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
@@ -38,9 +38,9 @@ export const searchPlugin = createPlugin({
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: searchApiRef,
|
||||
deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
|
||||
factory: ({ discoveryApi, identityApi }) => {
|
||||
return new SearchClient({ discoveryApi, identityApi });
|
||||
deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },
|
||||
factory: ({ discoveryApi, fetchApi }) => {
|
||||
return new SearchClient({ discoveryApi, fetchApi });
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user