Skip to content

Commit

Permalink
address comments
Browse files Browse the repository at this point in the history
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
  • Loading branch information
freben committed Dec 15, 2021
1 parent afb7f31 commit 3fa31ec
Show file tree
Hide file tree
Showing 22 changed files with 443 additions and 234 deletions.
25 changes: 25 additions & 0 deletions .changeset/itchy-grapes-think.md
@@ -0,0 +1,25 @@
---
'@backstage/plugin-catalog': patch
---

Deprecated the `CatalogClientWrapper` class.

The default implementation of `catalogApiRef` that this plugin exposes, is now powered by the new `fetchApiRef`. The default implementation of _that_ API, in turn, has the ability to inject the user's Backstage token in requests in a similar manner to what the deprecated `CatalogClientWrapper` used to do. The latter has therefore been taken out of the default catalog API implementation.

If you use a custom `fetchApiRef` implementation that does NOT issue tokens, or use a custom `catalogApiRef` implementation which does NOT use the default `fetchApiRef`, you can still for some time wrap your catalog API in this class to get back the old behavior:

```ts
// Add this to your packages/app/src/plugins.ts if you want to get back the old
// catalog client behavior:
createApiFactory({
api: catalogApiRef,
deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
factory: ({ discoveryApi, identityApi }) =>
new CatalogClientWrapper({
client: new CatalogClient({ discoveryApi }),
identityApi,
}),
}),
```

But do consider migrating to making use of the `fetchApiRef` as soon as convenient, since the wrapper class will be removed in a future release.
32 changes: 17 additions & 15 deletions packages/app-defaults/src/defaults/apis.ts
Expand Up @@ -34,9 +34,8 @@ import {
OneLoginAuth,
UnhandledErrorForwarder,
AtlassianAuth,
FetchApiBuilder,
IdentityAwareFetchMiddleware,
BackstageProtocolResolverFetchMiddleware,
createFetchApi,
FetchMiddlewares,
} from '@backstage/core-app-api';

import {
Expand Down Expand Up @@ -99,20 +98,23 @@ export const apis = [
}),
createApiFactory({
api: fetchApiRef,
deps: { identityApi: identityApiRef, discoveryApi: discoveryApiRef },
factory: ({ identityApi, discoveryApi }) => {
return FetchApiBuilder.create()
.with(
new IdentityAwareFetchMiddleware(() => {
return identityApi.getCredentials().then(r => r.token);
deps: {
configApi: configApiRef,
identityApi: identityApiRef,
discoveryApi: discoveryApiRef,
},
factory: ({ configApi, identityApi, discoveryApi }) => {
return createFetchApi({
middleware: [
FetchMiddlewares.resolvePluginProtocol({
discoveryApi,
}),
)
.with(
new BackstageProtocolResolverFetchMiddleware(plugin => {
return discoveryApi.getBaseUrl(plugin);
FetchMiddlewares.injectIdentityAuth({
identityApi,
config: configApi,
}),
)
.build();
],
});
},
}),
createApiFactory({
Expand Down
3 changes: 1 addition & 2 deletions packages/catalog-client/api-report.md
Expand Up @@ -5,7 +5,6 @@
```ts
import { Entity } from '@backstage/catalog-model';
import { EntityName } from '@backstage/catalog-model';
import { default as fetch_2 } from 'cross-fetch';
import { Location as Location_2 } from '@backstage/catalog-model';

// @public
Expand Down Expand Up @@ -159,6 +158,6 @@ export const ENTITY_STATUS_CATALOG_PROCESSING_TYPE =

// @public
export type FetchApi = {
fetch: typeof fetch_2;
fetch: typeof fetch;
};
```
2 changes: 0 additions & 2 deletions packages/catalog-client/src/types/fetch.ts
Expand Up @@ -14,8 +14,6 @@
* limitations under the License.
*/

import fetch from 'cross-fetch';

/**
* This is a copy of FetchApi, to avoid importing core-plugin-api.
*
Expand Down
50 changes: 22 additions & 28 deletions packages/core-app-api/api-report.md
Expand Up @@ -24,9 +24,9 @@ import { BackstageIdentityApi } from '@backstage/core-plugin-api';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { bitbucketAuthApiRef } from '@backstage/core-plugin-api';
import { ComponentType } from 'react';
import { Config } from '@backstage/config';
import { ConfigReader } from '@backstage/config';
import { createApp as createApp_2 } from '@backstage/app-defaults';
import crossFetch from 'cross-fetch';
import { DiscoveryApi } from '@backstage/core-plugin-api';
import { ErrorApi } from '@backstage/core-plugin-api';
import { ErrorApiError } from '@backstage/core-plugin-api';
Expand Down Expand Up @@ -290,14 +290,6 @@ export type BackstagePluginWithAnyOutput = Omit<
)[];
};

// @public
export class BackstageProtocolResolverFetchMiddleware
implements FetchMiddleware
{
constructor(discovery: (pluginId: string) => Promise<string>);
apply(next: FetchFunction): FetchFunction;
}

// @public
export class BitbucketAuth {
// (undocumented)
Expand Down Expand Up @@ -328,6 +320,12 @@ export function createApp(
options?: Parameters<typeof createApp_2>[0],
): BackstageApp & AppContext;

// @public
export function createFetchApi(options: {
baseImplementation?: typeof fetch | undefined;
middleware?: FetchMiddleware | FetchMiddleware[] | undefined;
}): FetchApi;

// @public
export function createSpecializedApp(options: AppOptions): BackstageApp;

Expand Down Expand Up @@ -380,21 +378,24 @@ export type FeatureFlaggedProps = {
);

// @public
export class FetchApiBuilder {
// (undocumented)
build(): FetchApi;
// (undocumented)
static create(): FetchApiBuilder;
// (undocumented)
with(middleware: FetchMiddleware): FetchApiBuilder;
export interface FetchMiddleware {
apply(next: typeof fetch): typeof fetch;
}

// @public
export type FetchFunction = typeof crossFetch;

// @public
export interface FetchMiddleware {
apply(next: FetchFunction): FetchFunction;
export class FetchMiddlewares {
static injectIdentityAuth(options: {
identityApi: IdentityApi;
config?: Config;
urlPrefixAllowlist?: string[];
header?: {
name: string;
value: (backstageToken: string) => string;
};
}): FetchMiddleware;
static resolvePluginProtocol(options: {
discoveryApi: DiscoveryApi;
}): FetchMiddleware;
}

// @public
Expand Down Expand Up @@ -454,13 +455,6 @@ export class GoogleAuth {
static create(options: OAuthApiCreateOptions): typeof googleAuthApiRef.T;
}

// @public
export class IdentityAwareFetchMiddleware implements FetchMiddleware {
constructor(tokenFunction: () => Promise<string | undefined>);
apply(next: FetchFunction): FetchFunction;
setHeaderName(name: string): IdentityAwareFetchMiddleware;
}

// @public
export class LocalStorageFeatureFlags implements FeatureFlagsApi {
// (undocumented)
Expand Down
1 change: 0 additions & 1 deletion packages/core-app-api/package.json
Expand Up @@ -39,7 +39,6 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@types/prop-types": "^15.7.3",
"cross-fetch": "^3.0.6",
"prop-types": "^15.7.2",
"react-router-dom": "6.0.0-beta.0",
"react-use": "^17.2.4",
Expand Down
@@ -0,0 +1,76 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { Config } from '@backstage/config';
import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
import { IdentityAuthInjectorFetchMiddleware } from './IdentityAuthInjectorFetchMiddleware';
import { PluginProtocolResolverFetchMiddleware } from './PluginProtocolResolverFetchMiddleware';
import { FetchMiddleware } from './types';

/**
* A collection of common middlewares for the FetchApi.
*
* @public
*/
export class FetchMiddlewares {
/**
* Handles translation from `plugin://` URLs to concrete http(s) URLs based on
* the discovery API.
*
* @remarks
*
* If the request is for `plugin://catalog/entities?filter=x=y`, the discovery
* API will be queried for `'catalog'`. If it returned
* `https://backstage.example.net/api/catalog`, the resulting query would be
* `https://backstage.example.net/api/catalog/entities?filter=x=y`.
*
* If the incoming URL protocol was not `plugin`, the request is just passed
* through verbatim to the underlying implementation.
*/
static resolvePluginProtocol(options: {
discoveryApi: DiscoveryApi;
}): FetchMiddleware {
return new PluginProtocolResolverFetchMiddleware(options.discoveryApi);
}

/**
* Injects a Backstage token header when the user is signed in.
*
* @remarks
*
* Per default, an `Authorization: Bearer <token>` is generated. This can be
* customized using the `header` option.
*
* The header injection only happens on allowlisted URLs. Per default, if the
* `config` option is passed in, the `backend.baseUrl` is allowlisted, unless
* the `urlPrefixAllowlist` option is passed in, in which case it takes
* precedence. If you pass in neither config nor an allowlist, the middleware
* will have no effect.
*/
static injectIdentityAuth(options: {
identityApi: IdentityApi;
config?: Config;
urlPrefixAllowlist?: string[];
header?: {
name: string;
value: (backstageToken: string) => string;
};
}): FetchMiddleware {
return IdentityAuthInjectorFetchMiddleware.create(options);
}

private constructor() {}
}

0 comments on commit 3fa31ec

Please sign in to comment.