removed unused dependency react-router-dom
This commit is contained in:
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@backstage/errors': patch
|
||||
---
|
||||
|
||||
Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`.
|
||||
Rename the error name for unknown errors from `unknown` to `error`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/integration': patch
|
||||
---
|
||||
|
||||
Narrow the types returned by the request option functions, to only the specifics that they actually do return. The reason for this change is that a full `RequestInit` is unfortunate to return because it's different between `cross-fetch` and `node-fetch`.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-tech-insights-backend': patch
|
||||
'@backstage/plugin-tech-insights-backend-module-jsonfc': patch
|
||||
---
|
||||
|
||||
Update README docs to use correct function/parameter names
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Incorporate usage of the tokenManager into the backend created using `create-app`.
|
||||
|
||||
In existing backends, update the `PluginEnvironment` to include a `tokenManager`:
|
||||
|
||||
```diff
|
||||
// packages/backend/src/types.ts
|
||||
|
||||
...
|
||||
import {
|
||||
...
|
||||
+ TokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
|
||||
export type PluginEnvironment = {
|
||||
...
|
||||
+ tokenManager: TokenManager;
|
||||
};
|
||||
```
|
||||
|
||||
Then, create a `ServerTokenManager`. This can either be a `noop` that requires no secret and validates all requests by default, or one that uses a secret from your `app-config.yaml` to generate and validate tokens.
|
||||
|
||||
```diff
|
||||
// packages/backend/src/index.ts
|
||||
|
||||
...
|
||||
import {
|
||||
...
|
||||
+ ServerTokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
...
|
||||
|
||||
function makeCreateEnv(config: Config) {
|
||||
...
|
||||
// CHOOSE ONE
|
||||
// TokenManager not requiring a secret
|
||||
+ const tokenManager = ServerTokenManager.noop();
|
||||
// OR TokenManager requiring a secret
|
||||
+ const tokenManager = ServerTokenManager.fromConfig(config);
|
||||
|
||||
...
|
||||
return (plugin: string): PluginEnvironment => {
|
||||
...
|
||||
- return { logger, cache, database, config, reader, discovery };
|
||||
+ return { logger, cache, database, config, reader, discovery, tokenManager };
|
||||
};
|
||||
}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Bump `react-jsonschema-form`
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
---
|
||||
|
||||
Switched to using the standardized JSON error responses for all provider endpoints.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
'@backstage/config-loader': patch
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
'@backstage/plugin-badges-backend': patch
|
||||
'@backstage/plugin-bitrise': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
'@backstage/plugin-catalog-backend-module-msgraph': patch
|
||||
'@backstage/plugin-code-coverage-backend': patch
|
||||
'@backstage/plugin-fossa': patch
|
||||
'@backstage/plugin-jenkins-backend': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-scaffolder-backend-module-cookiecutter': patch
|
||||
'@backstage/plugin-sonarqube': patch
|
||||
'@backstage/plugin-tech-insights-backend': patch
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
'@backstage/plugin-todo-backend': patch
|
||||
---
|
||||
|
||||
Align on usage of `cross-fetch` vs `node-fetch` in frontend vs backend packages, and remove some unnecessary imports of either one of them
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Switch the default test coverage provider from the jest default one to `'v8'`, which provides much better coverage information when using the default Backstage test setup. This is considered a bug fix as the current coverage information is often very inaccurate.
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING** `DefaultTechDocsCollator` has a new required option `tokenManager`. See the create-app changelog for how to create a `tokenManager` and add it to the `PluginEnvironment`. It can then be passed to the collator in `createPlugin`:
|
||||
|
||||
```diff
|
||||
// packages/backend/src/plugins/search.ts
|
||||
|
||||
...
|
||||
export default async function createPlugin({
|
||||
...
|
||||
+ tokenManager,
|
||||
}: PluginEnvironment) {
|
||||
...
|
||||
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: DefaultTechDocsCollator.fromConfig(config, {
|
||||
discovery,
|
||||
logger,
|
||||
+ tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Disable ES transforms in tests transformed by the `jestSucraseTransform.js`. This is not considered a breaking change since all code is already transpiled this way in the development setup.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
DefaultTechDocsCollator is now included in the search backend, and the Search Page updated with the SearchType component that includes the techdocs type
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Create a `TokenManager` interface and `ServerTokenManager` implementation to generate and validate server tokens for authenticated backend-to-backend API requests.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
'@techdocs/cli': patch
|
||||
---
|
||||
|
||||
Bump react-dev-utils to v12
|
||||
@@ -1,34 +0,0 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
'@backstage/cli': patch
|
||||
'@backstage/core-app-api': patch
|
||||
'@backstage/create-app': patch
|
||||
'@backstage/techdocs-common': patch
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
'@backstage/plugin-azure-devops-backend': patch
|
||||
'@backstage/plugin-badges-backend': patch
|
||||
'@backstage/plugin-bazaar-backend': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
'@backstage/plugin-code-coverage-backend': patch
|
||||
'@backstage/plugin-github-actions': patch
|
||||
'@backstage/plugin-jenkins-backend': patch
|
||||
'@backstage/plugin-proxy-backend': patch
|
||||
'@backstage/plugin-rollbar-backend': patch
|
||||
'@backstage/plugin-search-backend': patch
|
||||
'@backstage/plugin-techdocs': patch
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
---
|
||||
|
||||
Change default port of backend from 7000 to 7007.
|
||||
|
||||
This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
|
||||
|
||||
You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
|
||||
|
||||
```
|
||||
backend:
|
||||
listen: 0.0.0.0:7123
|
||||
baseUrl: http://localhost:7123
|
||||
```
|
||||
|
||||
More information can be found here: https://backstage.io/docs/conf/writing
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-permission-common': minor
|
||||
---
|
||||
|
||||
Accept configApi rather than enabled flag in PermissionClient constructor.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-plugin-api': patch
|
||||
---
|
||||
|
||||
Tweaked the logged deprecation warning for `createRouteRef` to hopefully make it more clear.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-config-schema': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Fixed a missing `await` when throwing server side errors
|
||||
@@ -1,42 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING** EntitiesSearchFilter fields have changed.
|
||||
|
||||
EntitiesSearchFilter now has only two fields: `key` and `value`. The `matchValueIn` and `matchValueExists` fields are no longer are supported. Previous filters written using the `matchValueIn` and `matchValueExists` fields can be rewritten as follows:
|
||||
|
||||
Filtering by existence of key only:
|
||||
|
||||
```diff
|
||||
filter: {
|
||||
{
|
||||
key: 'abc',
|
||||
- matchValueExists: true,
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Filtering by key and values:
|
||||
|
||||
```diff
|
||||
filter: {
|
||||
{
|
||||
key: 'abc',
|
||||
- matchValueExists: true,
|
||||
- matchValueIn: ['xyz'],
|
||||
+ values: ['xyz'],
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
Negation of filters can now be achieved through a `not` object:
|
||||
|
||||
```
|
||||
filter: {
|
||||
not: {
|
||||
key: 'abc',
|
||||
values: ['xyz'],
|
||||
},
|
||||
}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Index User entities by displayName to be able to search by full name. Added displayName (if present) to the 'text' field in the indexed document.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
---
|
||||
|
||||
Expose catalog lib in plugin-auth-backend, i.e `CatalogIdentityClient` class is exposed now.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Update the default routes to use id instead of title
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
'@backstage/core-app-api': patch
|
||||
'@backstage/test-utils': patch
|
||||
---
|
||||
|
||||
The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`.
|
||||
|
||||
These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs.
|
||||
|
||||
When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code:
|
||||
|
||||
```tsx
|
||||
render(
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[identityApiRef, mockIdentityApi as unknown as IdentityApi]
|
||||
])}
|
||||
>
|
||||
{...}
|
||||
</ApiProvider>
|
||||
)
|
||||
```
|
||||
|
||||
Would be migrated to this:
|
||||
|
||||
```tsx
|
||||
render(
|
||||
<TestApiProvider apis={[[identityApiRef, mockIdentityApi]]}>
|
||||
{...}
|
||||
</TestApiProvider>
|
||||
)
|
||||
```
|
||||
|
||||
In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array.
|
||||
|
||||
Usage that looks like this:
|
||||
|
||||
```ts
|
||||
const apis = ApiRegistry.with(
|
||||
identityApiRef,
|
||||
mockIdentityApi as unknown as IdentityApi,
|
||||
).with(configApiRef, new ConfigReader({}));
|
||||
```
|
||||
|
||||
OR like this:
|
||||
|
||||
```ts
|
||||
const apis = ApiRegistry.from([
|
||||
[identityApiRef, mockIdentityApi as unknown as IdentityApi],
|
||||
[configApiRef, new ConfigReader({})],
|
||||
]);
|
||||
```
|
||||
|
||||
Would be migrated to this:
|
||||
|
||||
```ts
|
||||
const apis = TestApiRegistry.from(
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[configApiRef, new ConfigReader({})],
|
||||
);
|
||||
```
|
||||
|
||||
If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Add group filtering to the scaffolder page so that individuals can surface specific templates to end users ahead of others, or group templates together. This can be accomplished by passing in a `groups` prop to the `ScaffolderPage`
|
||||
|
||||
```
|
||||
<ScaffolderPage
|
||||
groups={[
|
||||
{
|
||||
title: "Recommended",
|
||||
filter: entity =>
|
||||
entity?.metadata?.tags?.includes('recommended') ?? false,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Pin sidebar by default for easier navigation
|
||||
@@ -0,0 +1,27 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING** `DefaultCatalogCollator` has a new required option `tokenManager`. See the create-app changelog for how to create a `tokenManager` and add it to the `PluginEnvironment`. It can then be passed to the collator in `createPlugin`:
|
||||
|
||||
```diff
|
||||
// packages/backend/src/plugins/search.ts
|
||||
|
||||
...
|
||||
export default async function createPlugin({
|
||||
...
|
||||
+ tokenManager,
|
||||
}: PluginEnvironment) {
|
||||
...
|
||||
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: DefaultCatalogCollator.fromConfig(config, {
|
||||
discovery,
|
||||
+ tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
...
|
||||
}
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-graphiql': patch
|
||||
---
|
||||
|
||||
Letting GraphiQL use headers
|
||||
@@ -18,6 +18,13 @@
|
||||
/plugins/techdocs-backend @backstage/techdocs-core
|
||||
/plugins/ilert @backstage/reviewers @yacut
|
||||
/plugins/home @backstage/techdocs-core
|
||||
/plugins/azure-devops @backstage/reviewers @marleypowell @awanlin
|
||||
/plugins/azure-devops-backend @backstage/reviewers @marleypowell @awanlin
|
||||
/plugins/azure-devops-common @backstage/reviewers @marleypowell @awanlin
|
||||
/tech-insights-backend @backstage/reviewers @xantier @iain-b
|
||||
/tech-insights-backend-module-jsonfc @backstage/reviewers @xantier @iain-b
|
||||
/tech-insights-tech-insights-common @backstage/reviewers @xantier @iain-b
|
||||
/tech-insights-tech-insights-node @backstage/reviewers @xantier @iain-b
|
||||
/packages/embedded-techdocs-app @backstage/techdocs-core
|
||||
/packages/search-common @backstage/techdocs-core
|
||||
/packages/techdocs-cli @backstage/techdocs-core
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
- plugin
|
||||
- help wanted
|
||||
- good first issue
|
||||
- rfc
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
@@ -189,6 +189,7 @@ oidc
|
||||
Okta
|
||||
onboarding
|
||||
Onboarding
|
||||
OpenShift
|
||||
orgs
|
||||
pagerduty
|
||||
pageview
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
name: 'Stale workflow'
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '*/10 * * * *' # run every 10 minutes as it also removes labels.
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@main
|
||||
id: stale
|
||||
with:
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
days-before-issue-stale: 60
|
||||
days-before-issue-close: 7
|
||||
exempt-issue-labels: 'pinned,security,plugin,help wanted,good first issue,rfc'
|
||||
stale-issue-label: stale
|
||||
stale-pr-message: >
|
||||
This PR has been automatically marked as stale because it has not had
|
||||
recent activity from the author. It will be closed if no further activity occurs.
|
||||
If you are the author and the PR has been closed, feel free to re-open the PR and continue the contribution!
|
||||
days-before-pr-stale: 7
|
||||
days-before-pr-close: 3
|
||||
exempt-pr-labels: reviewer-approved,awaiting-review
|
||||
stale-pr-label: stale
|
||||
operations-per-run: 100
|
||||
@@ -71,3 +71,4 @@
|
||||
| [Epic Games](https://www.epicgames.com) | [Brian Jung](https://github.com/brian-at-epic), [Jeff Goldian](https://github.com/jeffgoldian-Epic) | Developer Portal: Service Catalog, Documentation, Software Templates and more making our internal teams' lives easier! |
|
||||
| [Globo](https://globo.com) | [Carlos Gusmão](https://github.com/caeugusmao), [Guilherme Vierno](https://github.com/vierno), [Denis Aoki](https://github.com/dnsaoki2), [Maycon Dionisio](https://github.com/MayconDionisio), | Reduce the friction of accessing the information engineers need about Globo's digital services through a coherent and centralized experience. |
|
||||
| [QBE](https://www.qbe.com/) | [Daniel Steel](https://github.com/danielsteelqbe), [Pete Jespers](https://github.com/petejespersqbe) | Developer portal allowing our global teams to explore and create applications, documentation and cloud infrastructure easily and quickly 🚀 |
|
||||
| [LogMeIn](https://www.logmein.com) | [Lorenzo Orsatti](https://github.com/lorsatti) | Improve onboarding experience of new developers. Discover faster and painlessly developer documentation, API definitions and team information. Provide useful dev metrics in a central place. Provide easy-to-use templates for new services. |
|
||||
|
||||
@@ -23,6 +23,11 @@ app:
|
||||
title: '#backstage'
|
||||
|
||||
backend:
|
||||
# Used for enabling authentication, secret is shared by all backend plugins
|
||||
# See backend-to-backend-auth.md in the docs for information on the format
|
||||
# auth:
|
||||
# keys:
|
||||
# - secret: ${BACKEND_SECRET}
|
||||
baseUrl: http://localhost:7007
|
||||
listen:
|
||||
port: 7007
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
{{- $frontendUrl := urlParse .Values.appConfig.app.baseUrl}}
|
||||
{{- $backendUrl := urlParse .Values.appConfig.backend.baseUrl}}
|
||||
{{- $lighthouseUrl := urlParse .Values.appConfig.lighthouse.baseUrl}}
|
||||
|
||||
{{/* Determine the api type for the ingress */}}
|
||||
{{- if lt .Capabilities.KubeVersion.Minor "19" }}
|
||||
apiVersion: networking.k8s.io/v1beta1
|
||||
{{- else if ge .Capabilities.KubeVersion.Minor "19" }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
{{- end }}
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ include "backstage.fullname" . }}-ingress
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -154,13 +154,17 @@ import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
discovery,
|
||||
tokenManager,
|
||||
}: PluginEnvironment) {
|
||||
const searchEngine = new LunrSearchEngine({ logger });
|
||||
const indexBuilder = new IndexBuilder({ logger, searchEngine });
|
||||
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: new DefaultCatalogCollator({ discovery }),
|
||||
collator: new DefaultCatalogCollator({
|
||||
discovery,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
const { scheduler } = await indexBuilder.build();
|
||||
@@ -285,7 +289,10 @@ const indexBuilder = new IndexBuilder({ logger, searchEngine });
|
||||
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: new DefaultCatalogCollator({ discovery }),
|
||||
collator: new DefaultCatalogCollator({
|
||||
discovery,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
indexBuilder.addCollator({
|
||||
@@ -303,6 +310,9 @@ its `defaultRefreshIntervalSeconds` value, like this:
|
||||
```typescript {3}
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: new DefaultCatalogCollator({ discovery }),
|
||||
collator: new DefaultCatalogCollator({
|
||||
discovery,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
id: how-to-guides
|
||||
title: Search "HOW TO" guides
|
||||
sidebar_label: "HOW TO" guides
|
||||
description: Search "HOW TO" guides
|
||||
description: Search "HOW TO" guides
|
||||
---
|
||||
|
||||
## How to implement your own Search API
|
||||
@@ -74,6 +74,7 @@ indexBuilder.addCollator({
|
||||
collator: DefaultTechDocsCollator.fromConfig(config, {
|
||||
discovery,
|
||||
logger,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
@@ -53,3 +53,30 @@ You can do so by including the following lines in the last step of your
|
||||
RUN apt-get update && apt-get install -y python3 python3-pip
|
||||
RUN pip3 install cookiecutter
|
||||
```
|
||||
|
||||
### Customizing the ScaffolderPage with Grouping and Filtering
|
||||
|
||||
Once you have more than a few software templates you may want to customize your
|
||||
`ScaffolderPage` by grouping and surfacing certain templates together. You can
|
||||
accomplish this by creating `groups` and passing them to your `ScaffolderPage`
|
||||
like below
|
||||
|
||||
```
|
||||
<ScaffolderPage
|
||||
groups={[
|
||||
{
|
||||
title: "Recommended",
|
||||
filter: entity =>
|
||||
entity?.metadata?.tags?.includes('recommended') ?? false,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
```
|
||||
|
||||
This code will group all templates with the 'recommended' tag together at the
|
||||
top of the page above any other templates not filtered by this group or others.
|
||||
|
||||
You can also further customize groups by passing in a `titleComponent` instead
|
||||
of a `title` which will be a component to use as the header instead of just the
|
||||
default `ContentHeader` with the `title` set as it's value.
|
||||

|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
---
|
||||
id: discovery
|
||||
title: Azure DevOps Discovery
|
||||
sidebar_label: Discovery
|
||||
# prettier-ignore
|
||||
description: Automatically discovering catalog entities from repositories in an Azure DevOps organization
|
||||
---
|
||||
|
||||
The Azure DevOps integration has a special discovery processor for discovering
|
||||
catalog entities within an Azure DevOps. The processor will crawl the Azure
|
||||
DevOps organization and register entities matching the configured path. This can
|
||||
be useful as an alternative to static locations or manually adding things to the
|
||||
catalog.
|
||||
|
||||
To use the discovery processor, you'll need a GitHub integration
|
||||
[set up](locations.md) with a `AZURE_TOKEN`. Then you can add a location target
|
||||
to the catalog configuration:
|
||||
|
||||
```yaml
|
||||
catalog:
|
||||
locations:
|
||||
# Scan all repositories for a catalog-info.yaml in the root of the default branch
|
||||
- type: azure-discovery
|
||||
target: https://dev.azure.com/myorg/myproject
|
||||
# Or use a custom pattern for a subset of all repositories with default repository
|
||||
- type: azure-discovery
|
||||
target: https://dev.azure.com/myorg/myproject/_git/service-*
|
||||
# Or use a custom file format and location
|
||||
- type: azure-discovery
|
||||
target: https://dev.azure.com/myorg/myproject/_git/*?path=/src/*/catalog-info.yaml
|
||||
```
|
||||
|
||||
Note the `azure-discovery` type, as this is not a regular `url` processor.
|
||||
|
||||
When using a custom pattern, the target is composed of five parts:
|
||||
|
||||
- The base instance URL, `https://dev.azure.com` in this case
|
||||
- The organization name which is required, `myorg` in this case
|
||||
- The project name which is required, `myproject` in this case
|
||||
- The repository blob to scan, which accepts \* wildcard tokens and must be
|
||||
added after `_git/`. This can simply be `*` to scan all repositories in the
|
||||
project.
|
||||
- The path within each repository to find the catalog YAML file. This will
|
||||
usually be `/catalog-info.yaml`, `/src/*/catalog-info.yaml` or a similar
|
||||
variation for catalog files stored in the root directory of each repository.
|
||||
|
||||
_Note:_ the path parameter follows the same rules as the search on Azure DevOps
|
||||
web interface. For more details visit the
|
||||
[official search documentation](https://docs.microsoft.com/en-us/azure/devops/project/search/get-started-search?view=azure-devops)
|
||||
@@ -27,7 +27,7 @@ point building on top of the previous one:
|
||||
and the new APIs can be used in parallel. This deprecation must have been
|
||||
released for at least two weeks before the deprecated API is removed in a
|
||||
minor version bump.
|
||||
- **3** - The time limit for the deprecation is 3 months instead of two days.
|
||||
- **3** - The time limit for the deprecation is 3 months instead of two weeks.
|
||||
|
||||
TL;DR:
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
id: backend-to-backend-auth
|
||||
title: Backend-to-Backend Authentication
|
||||
description:
|
||||
Guide for authenticating API requests between Backstage plugin backends
|
||||
---
|
||||
|
||||
This tutorial describes the steps needed to handle _backend-to-backend
|
||||
authentication_, which allows plugin backends to determine whether a given
|
||||
request originates from a legitimate Backstage backend by verifying a token
|
||||
signed with a shared secret. This system has limited use for now, but will be
|
||||
needed to support the upcoming framework for permissions and authorization (see
|
||||
[the PRFC on the topic](https://github.com/backstage/backstage/pull/7761) for
|
||||
more details).
|
||||
|
||||
Backends have no concept of a Backstage identity, so instead they use a token
|
||||
generated using a shared key stored in config. You can generate a unique key for
|
||||
your app in a terminal, and set the `BACKEND_SECRET` environment variable to the
|
||||
resulting value.
|
||||
|
||||
```bash
|
||||
node -p 'require("crypto").randomBytes(24).toString("base64")'
|
||||
```
|
||||
|
||||
Requests originating from a backend plugin can be authenticated by decorating
|
||||
them with a backend token. Backend tokens can be generated using a
|
||||
`TokenManager`, which can be passed to plugin backends via the
|
||||
`PluginEnvironment`. The `TokenManager` provided in new Backstage instances
|
||||
generated by `create-app` is a stub, which returns empty tokens and accepts any
|
||||
input string as valid. To enable backend-to-backend authentication, you'll need
|
||||
to instantiate a new one using the secret from your config instead:
|
||||
|
||||
```diff
|
||||
// packages/backend/src/index.ts
|
||||
|
||||
function makeCreateEnv(config: Config) {
|
||||
const root = getRootLogger();
|
||||
const reader = UrlReaders.default({ logger: root, config });
|
||||
const discovery = SingleHostDiscovery.fromConfig(config);
|
||||
|
||||
root.info(`Created UrlReader ${reader}`);
|
||||
|
||||
const cacheManager = CacheManager.fromConfig(config);
|
||||
const databaseManager = DatabaseManager.fromConfig(config);
|
||||
- const tokenManager = ServerTokenManager.noop();
|
||||
+ const tokenManager = ServerTokenManager.fromConfig(config);
|
||||
```
|
||||
|
||||
With this `tokenManager`, you can then generate a server token for requests:
|
||||
|
||||
```typescript
|
||||
const { token } = await this.tokenManager.getToken();
|
||||
|
||||
const response = await fetch(pluginBackendApiUrl, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
...headers,
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
You can use the same `tokenManager` to authenticate tokens supplied on incoming
|
||||
requests:
|
||||
|
||||
```typescript
|
||||
await tokenManager.authenticate(token); // throws if token is invalid
|
||||
```
|
||||
@@ -19,7 +19,7 @@
|
||||
"@spotify/prettier-config": "^12.0.0",
|
||||
"docusaurus": "^2.0.0-alpha.70",
|
||||
"js-yaml": "^4.1.0",
|
||||
"prettier": "^2.4.1",
|
||||
"prettier": "^2.5.0",
|
||||
"yarn-lock-check": "^1.0.5"
|
||||
},
|
||||
"prettier": "@spotify/prettier-config"
|
||||
|
||||
@@ -125,7 +125,11 @@
|
||||
{
|
||||
"type": "subcategory",
|
||||
"label": "Azure",
|
||||
"ids": ["integrations/azure/locations", "integrations/azure/org"]
|
||||
"ids": [
|
||||
"integrations/azure/locations",
|
||||
"integrations/azure/discovery",
|
||||
"integrations/azure/org"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "subcategory",
|
||||
|
||||
+4
-4
@@ -5190,10 +5190,10 @@ prepend-http@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||
|
||||
prettier@^2.4.1:
|
||||
version "2.4.1"
|
||||
resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c"
|
||||
integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==
|
||||
prettier@^2.5.0:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.npmjs.org/prettier/-/prettier-2.5.0.tgz#a6370e2d4594e093270419d9cc47f7670488f893"
|
||||
integrity sha512-FM/zAKgWTxj40rH03VxzIPdXmj39SwSjwG0heUcNFwI+EMZJnY93yAiKXM3dObIKAM5TA88werc8T/EwhB45eg==
|
||||
|
||||
prismjs@^1.22.0:
|
||||
version "1.25.0"
|
||||
|
||||
@@ -88,6 +88,7 @@ nav:
|
||||
- Discovery: 'integrations/aws-s3/discovery.md'
|
||||
- Azure:
|
||||
- Locations: 'integrations/azure/locations.md'
|
||||
- Discovery: 'integrations/azure/discovery.md'
|
||||
- Org Data: 'integrations/azure/org.md'
|
||||
- Bitbucket:
|
||||
- Locations: 'integrations/bitbucket/locations.md'
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
"prettier:check": "prettier --check .",
|
||||
"lerna": "lerna",
|
||||
"storybook": "yarn workspace storybook start",
|
||||
"snyk:test": "npx snyk test --yarn-workspaces --strict-out-of-sync=false",
|
||||
"snyk:test:package": "yarn snyk:test --include",
|
||||
"build-storybook": "yarn workspace storybook build-storybook",
|
||||
"techdocs-cli": "node scripts/techdocs-cli.js",
|
||||
"techdocs-cli:dev": "cross-env TECHDOCS_CLI_DEV_MODE=true node scripts/techdocs-cli.js",
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
"devDependencies": {
|
||||
"@backstage/test-utils": "^0.1.22",
|
||||
"@rjsf/core": "^3.2.1",
|
||||
"@testing-library/cypress": "^7.0.1",
|
||||
"@testing-library/cypress": "^8.0.2",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^13.1.8",
|
||||
|
||||
@@ -175,7 +175,20 @@ const routes = (
|
||||
>
|
||||
{techDocsPage}
|
||||
</Route>
|
||||
<Route path="/create" element={<ScaffolderPage />}>
|
||||
<Route
|
||||
path="/create"
|
||||
element={
|
||||
<ScaffolderPage
|
||||
groups={[
|
||||
{
|
||||
title: 'Recommended',
|
||||
filter: entity =>
|
||||
entity?.metadata?.tags?.includes('recommended') ?? false,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<ScaffolderFieldExtensions>
|
||||
<LowerCaseValuePickerFieldExtension />
|
||||
</ScaffolderFieldExtensions>
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# @backstage/backend-common
|
||||
|
||||
## 0.9.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bab752e2b3: Change default port of backend from 7000 to 7007.
|
||||
|
||||
This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
|
||||
|
||||
You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
|
||||
|
||||
```
|
||||
backend:
|
||||
listen: 0.0.0.0:7123
|
||||
baseUrl: http://localhost:7123
|
||||
```
|
||||
|
||||
More information can be found here: https://backstage.io/docs/conf/writing
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/errors@0.1.5
|
||||
|
||||
## 0.9.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -526,6 +526,20 @@ export type SearchResponseFile = {
|
||||
content(): Promise<Buffer>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export class ServerTokenManager implements TokenManager {
|
||||
// (undocumented)
|
||||
authenticate(token: string): Promise<void>;
|
||||
// (undocumented)
|
||||
static fromConfig(config: Config): ServerTokenManager;
|
||||
// (undocumented)
|
||||
getToken(): Promise<{
|
||||
token: string;
|
||||
}>;
|
||||
// (undocumented)
|
||||
static noop(): TokenManager;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type ServiceBuilder = {
|
||||
loadConfig(config: Config): ServiceBuilder;
|
||||
@@ -583,6 +597,16 @@ export interface StatusCheckHandlerOptions {
|
||||
statusCheck?: StatusCheck;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface TokenManager {
|
||||
// (undocumented)
|
||||
authenticate: (token: string) => Promise<void>;
|
||||
// (undocumented)
|
||||
getToken: () => Promise<{
|
||||
token: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type UrlReader = {
|
||||
read(url: string): Promise<Buffer>;
|
||||
|
||||
Vendored
+14
@@ -20,6 +20,20 @@ export interface Config {
|
||||
};
|
||||
|
||||
backend: {
|
||||
/** Backend configuration for when request authentication is enabled */
|
||||
auth?: {
|
||||
/** Keys shared by all backends for signing and validating backend tokens. */
|
||||
keys: {
|
||||
/**
|
||||
* Secret for generating tokens. Should be a base64 string, recommended
|
||||
* length is 24 bytes.
|
||||
*
|
||||
* @visibility secret
|
||||
*/
|
||||
secret: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
baseUrl: string; // defined in core, but repeated here without doc
|
||||
|
||||
/** Address that the backend should listen to. */
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/backend-common",
|
||||
"description": "Common functionality library for Backstage backends",
|
||||
"version": "0.9.10",
|
||||
"version": "0.9.11",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -32,7 +32,7 @@
|
||||
"@backstage/cli-common": "^0.1.6",
|
||||
"@backstage/config": "^0.1.11",
|
||||
"@backstage/config-loader": "^0.8.0",
|
||||
"@backstage/errors": "^0.1.4",
|
||||
"@backstage/errors": "^0.1.5",
|
||||
"@backstage/integration": "^0.6.9",
|
||||
"@backstage/types": "^0.1.1",
|
||||
"@google-cloud/storage": "^5.8.0",
|
||||
@@ -54,6 +54,7 @@
|
||||
"git-url-parse": "^11.6.0",
|
||||
"helmet": "^4.0.0",
|
||||
"isomorphic-git": "^1.8.0",
|
||||
"jose": "^1.27.1",
|
||||
"keyv": "^4.0.3",
|
||||
"keyv-memcache": "^1.2.5",
|
||||
"knex": "^0.95.1",
|
||||
@@ -80,8 +81,8 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.9.0",
|
||||
"@backstage/test-utils": "^0.1.22",
|
||||
"@backstage/cli": "^0.9.1",
|
||||
"@backstage/test-utils": "^0.1.23",
|
||||
"@types/archiver": "^5.1.0",
|
||||
"@types/compression": "^1.7.0",
|
||||
"@types/concat-stream": "^1.6.0",
|
||||
|
||||
@@ -31,4 +31,5 @@ export * from './paths';
|
||||
export * from './reading';
|
||||
export * from './scm';
|
||||
export * from './service';
|
||||
export * from './tokens';
|
||||
export * from './util';
|
||||
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* 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 { ConfigReader } from '@backstage/config';
|
||||
import { TokenManager } from './types';
|
||||
import { ServerTokenManager } from './ServerTokenManager';
|
||||
|
||||
const emptyConfig = new ConfigReader({});
|
||||
const configWithSecret = new ConfigReader({
|
||||
backend: { auth: { keys: [{ secret: 'a-secret-key' }] } },
|
||||
});
|
||||
|
||||
describe('ServerTokenManager', () => {
|
||||
it('should throw if secret in config does not exist', () => {
|
||||
expect(() => ServerTokenManager.fromConfig(emptyConfig)).toThrowError();
|
||||
});
|
||||
|
||||
describe('getToken', () => {
|
||||
it('should return a token if secret in config exists', async () => {
|
||||
const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
|
||||
expect((await tokenManager.getToken()).token).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return a token string if using a noop TokenManager', async () => {
|
||||
const tokenManager = ServerTokenManager.noop();
|
||||
expect((await tokenManager.getToken()).token).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('authenticate', () => {
|
||||
it('should not throw if token is valid', async () => {
|
||||
const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
|
||||
const { token } = await tokenManager.getToken();
|
||||
await expect(tokenManager.authenticate(token)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw if token is invalid', async () => {
|
||||
const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
|
||||
await expect(
|
||||
tokenManager.authenticate('random-string'),
|
||||
).rejects.toThrowError(/invalid server token/i);
|
||||
});
|
||||
|
||||
it('should validate server tokens created by a different instance using the same secret', async () => {
|
||||
const tokenManager1 = ServerTokenManager.fromConfig(configWithSecret);
|
||||
const tokenManager2 = ServerTokenManager.fromConfig(configWithSecret);
|
||||
|
||||
const { token } = await tokenManager1.getToken();
|
||||
|
||||
await expect(tokenManager2.authenticate(token)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should validate server tokens created using any of the secrets', async () => {
|
||||
const tokenManager1 = ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: { auth: { keys: [{ secret: 'a1b2c3' }] } },
|
||||
}),
|
||||
);
|
||||
const tokenManager2 = ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: { auth: { keys: [{ secret: 'd4e5f6' }] } },
|
||||
}),
|
||||
);
|
||||
const tokenManager3 = ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: {
|
||||
auth: { keys: [{ secret: 'a1b2c3' }, { secret: 'd4e5f6' }] },
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
const { token: token1 } = await tokenManager1.getToken();
|
||||
await expect(tokenManager3.authenticate(token1)).resolves.not.toThrow();
|
||||
|
||||
const { token: token2 } = await tokenManager2.getToken();
|
||||
await expect(tokenManager3.authenticate(token2)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should throw for server tokens created using a different secret', async () => {
|
||||
const tokenManager1 = ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: { auth: { keys: [{ secret: 'a1b2c3' }] } },
|
||||
}),
|
||||
);
|
||||
const tokenManager2 = ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: { auth: { keys: [{ secret: 'd4e5f6' }] } },
|
||||
}),
|
||||
);
|
||||
|
||||
const { token } = await tokenManager1.getToken();
|
||||
|
||||
await expect(tokenManager2.authenticate(token)).rejects.toThrowError(
|
||||
/invalid server token/i,
|
||||
);
|
||||
});
|
||||
|
||||
it('should throw for server tokens created using a noop TokenManager', async () => {
|
||||
const noopTokenManager = ServerTokenManager.noop();
|
||||
const tokenManager = ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: { auth: { keys: [{ secret: 'a1b2c3' }] } },
|
||||
}),
|
||||
);
|
||||
|
||||
const { token } = await noopTokenManager.getToken();
|
||||
|
||||
await expect(tokenManager.authenticate(token)).rejects.toThrowError(
|
||||
/invalid server token/i,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ServerTokenManager.fromConfig', () => {
|
||||
it('should throw if backend auth configuration is missing', () => {
|
||||
expect(() =>
|
||||
ServerTokenManager.fromConfig(new ConfigReader({})),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('should throw if no keys are included in the configuration', () => {
|
||||
expect(() =>
|
||||
ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: { auth: { keys: [] } },
|
||||
}),
|
||||
),
|
||||
).toThrow();
|
||||
});
|
||||
|
||||
it('should throw if any key is missing a secret property', () => {
|
||||
expect(() =>
|
||||
ServerTokenManager.fromConfig(
|
||||
new ConfigReader({
|
||||
backend: {
|
||||
auth: {
|
||||
keys: [{ secret: '123' }, {}, { secret: '789' }],
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('ServerTokenManager.noop', () => {
|
||||
let noopTokenManager: TokenManager;
|
||||
|
||||
beforeEach(() => {
|
||||
noopTokenManager = ServerTokenManager.noop();
|
||||
});
|
||||
|
||||
it('should accept tokens it generates', async () => {
|
||||
const { token } = await noopTokenManager.getToken();
|
||||
|
||||
await expect(noopTokenManager.authenticate(token)).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should accept tokens generated by other noop token managers', async () => {
|
||||
const noopTokenManager2 = ServerTokenManager.noop();
|
||||
await expect(
|
||||
noopTokenManager.authenticate(
|
||||
(
|
||||
await noopTokenManager2.getToken()
|
||||
).token,
|
||||
),
|
||||
).resolves.not.toThrow();
|
||||
});
|
||||
|
||||
it('should accept signed tokens', async () => {
|
||||
const tokenManager = ServerTokenManager.fromConfig(configWithSecret);
|
||||
await expect(
|
||||
noopTokenManager.authenticate((await tokenManager.getToken()).token),
|
||||
).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 { JWKS, JWK, JWT } from 'jose';
|
||||
import { Config } from '@backstage/config';
|
||||
import { AuthenticationError } from '@backstage/errors';
|
||||
import { TokenManager } from './types';
|
||||
|
||||
class NoopTokenManager implements TokenManager {
|
||||
async getToken() {
|
||||
return { token: '' };
|
||||
}
|
||||
|
||||
async authenticate() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and validates tokens for use during backend-to-backend
|
||||
* authentication.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export class ServerTokenManager implements TokenManager {
|
||||
private readonly verificationKeys: JWKS.KeyStore;
|
||||
private readonly signingKey: JWK.Key;
|
||||
|
||||
static noop(): TokenManager {
|
||||
return new NoopTokenManager();
|
||||
}
|
||||
|
||||
static fromConfig(config: Config) {
|
||||
return new ServerTokenManager(
|
||||
config
|
||||
.getConfigArray('backend.auth.keys')
|
||||
.map(key => key.getString('secret')),
|
||||
);
|
||||
}
|
||||
|
||||
private constructor(secrets?: string[]) {
|
||||
if (!secrets?.length) {
|
||||
throw new Error(
|
||||
'No secrets provided when constructing ServerTokenManager',
|
||||
);
|
||||
}
|
||||
|
||||
this.verificationKeys = new JWKS.KeyStore(
|
||||
secrets.map(k => JWK.asKey({ kty: 'oct', k })),
|
||||
);
|
||||
this.signingKey = this.verificationKeys.all()[0];
|
||||
}
|
||||
|
||||
async getToken(): Promise<{ token: string }> {
|
||||
const jwt = JWT.sign({ sub: 'backstage-server' }, this.signingKey, {
|
||||
algorithm: 'HS256',
|
||||
});
|
||||
|
||||
return { token: jwt };
|
||||
}
|
||||
|
||||
async authenticate(token: string): Promise<void> {
|
||||
try {
|
||||
JWT.verify(token, this.verificationKeys);
|
||||
} catch (e) {
|
||||
throw new AuthenticationError('Invalid server token');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { ServerTokenManager } from './ServerTokenManager';
|
||||
export type { TokenManager } from './types';
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface for creating and validating tokens.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface TokenManager {
|
||||
getToken: () => Promise<{ token: string }>;
|
||||
authenticate: (token: string) => Promise<void>;
|
||||
}
|
||||
@@ -1,5 +1,27 @@
|
||||
# example-backend
|
||||
|
||||
## 0.2.54
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-kubernetes-backend@0.3.19
|
||||
- @backstage/plugin-tech-insights-backend@0.1.2
|
||||
- @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.1
|
||||
- @backstage/plugin-auth-backend@0.4.9
|
||||
- @backstage/plugin-scaffolder-backend@0.15.14
|
||||
- @backstage/plugin-catalog-backend@0.18.0
|
||||
- @backstage/plugin-kafka-backend@0.2.12
|
||||
- @backstage/backend-common@0.9.11
|
||||
- @backstage/plugin-azure-devops-backend@0.2.2
|
||||
- @backstage/plugin-badges-backend@0.1.12
|
||||
- @backstage/plugin-code-coverage-backend@0.1.15
|
||||
- @backstage/plugin-jenkins-backend@0.1.8
|
||||
- @backstage/plugin-proxy-backend@0.2.14
|
||||
- @backstage/plugin-rollbar-backend@0.1.16
|
||||
- @backstage/plugin-search-backend@0.2.7
|
||||
- @backstage/plugin-techdocs-backend@0.10.9
|
||||
|
||||
## 0.2.52
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.2.52",
|
||||
"version": "0.2.54",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -24,35 +24,35 @@
|
||||
"migrate:create": "knex migrate:make -x ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.9.10",
|
||||
"@backstage/backend-common": "^0.9.11",
|
||||
"@backstage/catalog-client": "^0.5.2",
|
||||
"@backstage/catalog-model": "^0.9.7",
|
||||
"@backstage/config": "^0.1.10",
|
||||
"@backstage/integration": "^0.6.8",
|
||||
"@backstage/plugin-app-backend": "^0.3.19",
|
||||
"@backstage/plugin-auth-backend": "^0.4.8",
|
||||
"@backstage/plugin-azure-devops-backend": "^0.2.1",
|
||||
"@backstage/plugin-badges-backend": "^0.1.11",
|
||||
"@backstage/plugin-catalog-backend": "^0.17.4",
|
||||
"@backstage/plugin-code-coverage-backend": "^0.1.14",
|
||||
"@backstage/plugin-auth-backend": "^0.4.9",
|
||||
"@backstage/plugin-azure-devops-backend": "^0.2.2",
|
||||
"@backstage/plugin-badges-backend": "^0.1.12",
|
||||
"@backstage/plugin-catalog-backend": "^0.18.0",
|
||||
"@backstage/plugin-code-coverage-backend": "^0.1.15",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.9",
|
||||
"@backstage/plugin-jenkins-backend": "^0.1.7",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.3.18",
|
||||
"@backstage/plugin-kafka-backend": "^0.2.11",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.13",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.15",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.15.13",
|
||||
"@backstage/plugin-jenkins-backend": "^0.1.8",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.3.19",
|
||||
"@backstage/plugin-kafka-backend": "^0.2.12",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.14",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.16",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.15.14",
|
||||
"@backstage/plugin-scaffolder-backend-module-rails": "^0.1.7",
|
||||
"@backstage/plugin-search-backend": "^0.2.6",
|
||||
"@backstage/plugin-search-backend": "^0.2.7",
|
||||
"@backstage/plugin-search-backend-node": "^0.4.2",
|
||||
"@backstage/plugin-search-backend-module-elasticsearch": "^0.0.5",
|
||||
"@backstage/plugin-search-backend-module-pg": "^0.2.1",
|
||||
"@backstage/plugin-techdocs-backend": "^0.10.8",
|
||||
"@backstage/plugin-tech-insights-backend": "^0.1.1",
|
||||
"@backstage/plugin-techdocs-backend": "^0.10.9",
|
||||
"@backstage/plugin-tech-insights-backend": "^0.1.2",
|
||||
"@backstage/plugin-tech-insights-node": "^0.1.0",
|
||||
"@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.0",
|
||||
"@backstage/plugin-tech-insights-backend-module-jsonfc": "^0.1.1",
|
||||
"@backstage/plugin-todo-backend": "^0.1.13",
|
||||
"@gitbeaker/node": "^30.2.0",
|
||||
"@gitbeaker/node": "^34.6.0",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"azure-devops-node-api": "^11.0.1",
|
||||
"dockerode": "^3.3.1",
|
||||
@@ -68,7 +68,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.9.0",
|
||||
"@backstage/cli": "^0.9.1",
|
||||
"@types/dockerode": "^3.3.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5"
|
||||
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
SingleHostDiscovery,
|
||||
UrlReaders,
|
||||
useHotMemoize,
|
||||
ServerTokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import healthcheck from './plugins/healthcheck';
|
||||
@@ -60,6 +61,7 @@ function makeCreateEnv(config: Config) {
|
||||
const root = getRootLogger();
|
||||
const reader = UrlReaders.default({ logger: root, config });
|
||||
const discovery = SingleHostDiscovery.fromConfig(config);
|
||||
const tokenManager = ServerTokenManager.noop();
|
||||
|
||||
root.info(`Created UrlReader ${reader}`);
|
||||
|
||||
@@ -70,7 +72,7 @@ function makeCreateEnv(config: Config) {
|
||||
const logger = root.child({ type: 'plugin', plugin });
|
||||
const database = databaseManager.forPlugin(plugin);
|
||||
const cache = cacheManager.forPlugin(plugin);
|
||||
return { logger, cache, database, config, reader, discovery };
|
||||
return { logger, cache, database, config, reader, discovery, tokenManager };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,7 @@ export default async function createPlugin({
|
||||
discovery,
|
||||
config,
|
||||
database,
|
||||
tokenManager,
|
||||
}: PluginEnvironment) {
|
||||
// Initialize a connection to a search engine.
|
||||
const searchEngine = await createSearchEngine({ config, logger, database });
|
||||
@@ -68,7 +69,10 @@ export default async function createPlugin({
|
||||
// particular collator gathers entities from the software catalog.
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: DefaultCatalogCollator.fromConfig(config, { discovery }),
|
||||
collator: DefaultCatalogCollator.fromConfig(config, {
|
||||
discovery,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
indexBuilder.addCollator({
|
||||
@@ -76,6 +80,7 @@ export default async function createPlugin({
|
||||
collator: DefaultTechDocsCollator.fromConfig(config, {
|
||||
discovery,
|
||||
logger,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
PluginCacheManager,
|
||||
PluginDatabaseManager,
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
|
||||
@@ -30,4 +31,5 @@ export type PluginEnvironment = {
|
||||
config: Config;
|
||||
reader: UrlReader;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# @backstage/cli
|
||||
|
||||
## 0.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- dde216acf4: Switch the default test coverage provider from the jest default one to `'v8'`, which provides much better coverage information when using the default Backstage test setup. This is considered a bug fix as the current coverage information is often very inaccurate.
|
||||
- 719cc87d2f: Disable ES transforms in tests transformed by the `jestSucraseTransform.js`. This is not considered a breaking change since all code is already transpiled this way in the development setup.
|
||||
- bab752e2b3: Change default port of backend from 7000 to 7007.
|
||||
|
||||
This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
|
||||
|
||||
You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
|
||||
|
||||
```
|
||||
backend:
|
||||
listen: 0.0.0.0:7123
|
||||
baseUrl: http://localhost:7123
|
||||
```
|
||||
|
||||
More information can be found here: https://backstage.io/docs/conf/writing
|
||||
|
||||
- ee055cf6db: Update the default routes to use id instead of title
|
||||
- Updated dependencies
|
||||
- @backstage/errors@0.1.5
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -31,7 +31,7 @@
|
||||
"@backstage/cli-common": "^0.1.6",
|
||||
"@backstage/config": "^0.1.11",
|
||||
"@backstage/config-loader": "^0.8.0",
|
||||
"@backstage/errors": "^0.1.4",
|
||||
"@backstage/errors": "^0.1.5",
|
||||
"@backstage/types": "^0.1.1",
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@lerna/package-graph": "^4.0.0",
|
||||
@@ -90,7 +90,7 @@
|
||||
"postcss": "^8.1.0",
|
||||
"process": "^0.11.10",
|
||||
"react": "^16.0.0",
|
||||
"react-dev-utils": "^11.0.4",
|
||||
"react-dev-utils": "^12.0.0-next.47",
|
||||
"react-hot-loader": "^4.12.21",
|
||||
"recursive-readdir": "^2.2.2",
|
||||
"replace-in-file": "^6.0.0",
|
||||
@@ -117,13 +117,13 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.9.10",
|
||||
"@backstage/backend-common": "^0.9.11",
|
||||
"@backstage/config": "^0.1.11",
|
||||
"@backstage/core-components": "^0.7.4",
|
||||
"@backstage/core-plugin-api": "^0.2.0",
|
||||
"@backstage/core-app-api": "^0.1.21",
|
||||
"@backstage/core-components": "^0.7.5",
|
||||
"@backstage/core-plugin-api": "^0.2.1",
|
||||
"@backstage/core-app-api": "^0.1.23",
|
||||
"@backstage/dev-utils": "^0.2.13",
|
||||
"@backstage/test-utils": "^0.1.22",
|
||||
"@backstage/test-utils": "^0.1.23",
|
||||
"@backstage/theme": "^0.2.13",
|
||||
"@types/diff": "^5.0.0",
|
||||
"@types/express": "^4.17.6",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/codemods
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/core-app-api@0.1.23
|
||||
- @backstage/core-plugin-api@0.2.1
|
||||
- @backstage/core-components@0.7.5
|
||||
|
||||
## 0.1.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/codemods",
|
||||
"description": "A collection of codemods for Backstage projects",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
"json-schema": "^0.4.0",
|
||||
"json-schema-merge-allof": "^0.8.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"node-fetch": "^2.6.1",
|
||||
"typescript-json-schema": "^0.51.0",
|
||||
"yaml": "^1.9.2",
|
||||
"yup": "^0.32.9",
|
||||
"node-fetch": "2.6.5"
|
||||
"yup": "^0.32.9"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^26.0.7",
|
||||
|
||||
@@ -1,5 +1,86 @@
|
||||
# @backstage/core-app-api
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bab752e2b3: Change default port of backend from 7000 to 7007.
|
||||
|
||||
This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
|
||||
|
||||
You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
|
||||
|
||||
```
|
||||
backend:
|
||||
listen: 0.0.0.0:7123
|
||||
baseUrl: http://localhost:7123
|
||||
```
|
||||
|
||||
More information can be found here: https://backstage.io/docs/conf/writing
|
||||
|
||||
- 000190de69: The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`.
|
||||
|
||||
These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs.
|
||||
|
||||
When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code:
|
||||
|
||||
```tsx
|
||||
render(
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[identityApiRef, mockIdentityApi as unknown as IdentityApi]
|
||||
])}
|
||||
>
|
||||
{...}
|
||||
</ApiProvider>
|
||||
)
|
||||
```
|
||||
|
||||
Would be migrated to this:
|
||||
|
||||
```tsx
|
||||
render(
|
||||
<TestApiProvider apis={[[identityApiRef, mockIdentityApi]]}>
|
||||
{...}
|
||||
</TestApiProvider>
|
||||
)
|
||||
```
|
||||
|
||||
In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array.
|
||||
|
||||
Usage that looks like this:
|
||||
|
||||
```ts
|
||||
const apis = ApiRegistry.with(
|
||||
identityApiRef,
|
||||
mockIdentityApi as unknown as IdentityApi,
|
||||
).with(configApiRef, new ConfigReader({}));
|
||||
```
|
||||
|
||||
OR like this:
|
||||
|
||||
```ts
|
||||
const apis = ApiRegistry.from([
|
||||
[identityApiRef, mockIdentityApi as unknown as IdentityApi],
|
||||
[configApiRef, new ConfigReader({})],
|
||||
]);
|
||||
```
|
||||
|
||||
Would be migrated to this:
|
||||
|
||||
```ts
|
||||
const apis = TestApiRegistry.from(
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[configApiRef, new ConfigReader({})],
|
||||
);
|
||||
```
|
||||
|
||||
If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`.
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/core-plugin-api@0.2.1
|
||||
- @backstage/core-components@0.7.5
|
||||
|
||||
## 0.1.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-app-api",
|
||||
"description": "Core app API used by Backstage apps",
|
||||
"version": "0.1.22",
|
||||
"version": "0.1.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,9 +30,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/app-defaults": "^0.1.1",
|
||||
"@backstage/core-components": "^0.7.4",
|
||||
"@backstage/core-components": "^0.7.5",
|
||||
"@backstage/config": "^0.1.11",
|
||||
"@backstage/core-plugin-api": "^0.2.0",
|
||||
"@backstage/core-plugin-api": "^0.2.1",
|
||||
"@backstage/theme": "^0.2.13",
|
||||
"@backstage/types": "^0.1.1",
|
||||
"@backstage/version-bridge": "^0.1.0",
|
||||
@@ -47,8 +47,8 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.9.0",
|
||||
"@backstage/test-utils": "^0.1.22",
|
||||
"@backstage/cli": "^0.9.1",
|
||||
"@backstage/test-utils": "^0.1.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
|
||||
@@ -349,6 +349,78 @@ describe('Integration Test', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('getFeatureFlags should return feature flags', async () => {
|
||||
const storageFlags = new LocalStorageFeatureFlags();
|
||||
jest.spyOn(storageFlags, 'registerFlag');
|
||||
|
||||
const apis = [
|
||||
noOpAnalyticsApi,
|
||||
createApiFactory({
|
||||
api: featureFlagsApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory() {
|
||||
return storageFlags;
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
const app = new AppManager({
|
||||
apis,
|
||||
defaultApis: [],
|
||||
themes: [
|
||||
{
|
||||
id: 'light',
|
||||
title: 'Light Theme',
|
||||
variant: 'light',
|
||||
Provider: ({ children }) => <>{children}</>,
|
||||
},
|
||||
],
|
||||
icons,
|
||||
plugins: [
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
featureFlags: [
|
||||
{
|
||||
name: 'foo',
|
||||
},
|
||||
],
|
||||
register: p => p.featureFlags.register('name'),
|
||||
}),
|
||||
],
|
||||
components,
|
||||
configLoader: async () => [],
|
||||
bindRoutes: ({ bind }) => {
|
||||
bind(plugin1.externalRoutes, {
|
||||
extRouteRef1: plugin1RouteRef,
|
||||
extRouteRef2: plugin2RouteRef,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const Provider = app.getProvider();
|
||||
const Router = app.getRouter();
|
||||
|
||||
await renderWithEffects(
|
||||
<Provider>
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route path="/" element={<ExposedComponent />} />
|
||||
<Route path="/foo" element={<HiddenComponent />} />
|
||||
</Routes>
|
||||
</Router>
|
||||
</Provider>,
|
||||
);
|
||||
|
||||
expect(storageFlags.registerFlag).toHaveBeenCalledWith({
|
||||
name: 'name',
|
||||
pluginId: 'test',
|
||||
});
|
||||
expect(storageFlags.registerFlag).toHaveBeenCalledWith({
|
||||
name: 'foo',
|
||||
pluginId: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('should track route changes via analytics api', async () => {
|
||||
const mockAnalyticsApi = new MockAnalyticsApi();
|
||||
const apis = [createApiFactory(analyticsApiRef, mockAnalyticsApi)];
|
||||
|
||||
@@ -272,17 +272,26 @@ export class AppManager implements BackstageApp {
|
||||
const featureFlagsApi = this.getApiHolder().get(featureFlagsApiRef)!;
|
||||
|
||||
for (const plugin of this.plugins.values()) {
|
||||
for (const output of plugin.output()) {
|
||||
switch (output.type) {
|
||||
case 'feature-flag': {
|
||||
featureFlagsApi.registerFlag({
|
||||
name: output.name,
|
||||
pluginId: plugin.getId(),
|
||||
});
|
||||
break;
|
||||
if ('getFeatureFlags' in plugin) {
|
||||
for (const flag of plugin.getFeatureFlags()) {
|
||||
featureFlagsApi.registerFlag({
|
||||
name: flag.name,
|
||||
pluginId: plugin.getId(),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const output of plugin.output()) {
|
||||
switch (output.type) {
|
||||
case 'feature-flag': {
|
||||
featureFlagsApi.registerFlag({
|
||||
name: output.name,
|
||||
pluginId: plugin.getId(),
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/core-components
|
||||
|
||||
## 0.7.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 157530187a: Pin sidebar by default for easier navigation
|
||||
- Updated dependencies
|
||||
- @backstage/errors@0.1.5
|
||||
- @backstage/core-plugin-api@0.2.1
|
||||
|
||||
## 0.7.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-components",
|
||||
"description": "Core components used by Backstage plugins and apps",
|
||||
"version": "0.7.4",
|
||||
"version": "0.7.5",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,8 +30,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.11",
|
||||
"@backstage/core-plugin-api": "^0.2.0",
|
||||
"@backstage/errors": "^0.1.4",
|
||||
"@backstage/core-plugin-api": "^0.2.1",
|
||||
"@backstage/errors": "^0.1.5",
|
||||
"@backstage/theme": "^0.2.13",
|
||||
"@material-table/core": "^3.1.0",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
@@ -67,9 +67,9 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/core-app-api": "^0.1.21",
|
||||
"@backstage/cli": "^0.9.0",
|
||||
"@backstage/test-utils": "^0.1.22",
|
||||
"@backstage/core-app-api": "^0.1.23",
|
||||
"@backstage/cli": "^0.9.1",
|
||||
"@backstage/test-utils": "^0.1.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @backstage/core-plugin-api
|
||||
|
||||
## 0.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 950b36393c: Deprecated `register` option of `createPlugin` and the `outputs` methods of the plugin instance.
|
||||
|
||||
Introduces the `featureFlags` property to define your feature flags instead.
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -258,6 +258,7 @@ export type BackstagePlugin<
|
||||
getId(): string;
|
||||
output(): PluginOutput[];
|
||||
getApis(): Iterable<AnyApiFactory>;
|
||||
getFeatureFlags(): Iterable<PluginFeatureFlagConfig>;
|
||||
provide<T>(extension: Extension<T>): T;
|
||||
routes: Routes;
|
||||
externalRoutes: ExternalRoutes;
|
||||
@@ -463,7 +464,7 @@ export type FeatureFlag = {
|
||||
pluginId: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export type FeatureFlagOutput = {
|
||||
type: 'feature-flag';
|
||||
name: string;
|
||||
@@ -682,14 +683,20 @@ export type PluginConfig<
|
||||
register?(hooks: PluginHooks): void;
|
||||
routes?: Routes;
|
||||
externalRoutes?: ExternalRoutes;
|
||||
featureFlags?: PluginFeatureFlagConfig[];
|
||||
};
|
||||
|
||||
// @public
|
||||
export type PluginFeatureFlagConfig = {
|
||||
name: string;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export type PluginHooks = {
|
||||
featureFlags: FeatureFlagsHooks;
|
||||
};
|
||||
|
||||
// @public
|
||||
// @public @deprecated
|
||||
export type PluginOutput = FeatureFlagOutput;
|
||||
|
||||
// @public
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-plugin-api",
|
||||
"description": "Core API used by Backstage plugins",
|
||||
"version": "0.2.0",
|
||||
"version": "0.2.1",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -43,9 +43,9 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.9.0",
|
||||
"@backstage/core-app-api": "^0.1.21",
|
||||
"@backstage/test-utils": "^0.1.22",
|
||||
"@backstage/cli": "^0.9.1",
|
||||
"@backstage/core-app-api": "^0.1.23",
|
||||
"@backstage/test-utils": "^0.1.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/react-hooks": "^7.0.2",
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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 { createPlugin } from './Plugin';
|
||||
|
||||
describe('Plugin Feature Flag', () => {
|
||||
it('should be able to register and receive feature flags', () => {
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([{ name: 'test' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([{ name: 'blob' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([{ name: 'test' }, { name: 'blob' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
}).getFeatureFlags(),
|
||||
).toEqual([]);
|
||||
|
||||
/* deprecated tests */
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).output(),
|
||||
).toEqual([{ name: 'test', type: 'feature-flag' }]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
}).output(),
|
||||
).toEqual([{ name: 'blob', type: 'feature-flag' }]);
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
register({ featureFlags }) {
|
||||
featureFlags.register('blob');
|
||||
},
|
||||
featureFlags: [{ name: 'test' }],
|
||||
}).output(),
|
||||
).toEqual([
|
||||
{ name: 'test', type: 'feature-flag' },
|
||||
{ name: 'blob', type: 'feature-flag' },
|
||||
]);
|
||||
|
||||
expect(
|
||||
createPlugin({
|
||||
id: 'test',
|
||||
}).output(),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
Extension,
|
||||
AnyRoutes,
|
||||
AnyExternalRoutes,
|
||||
PluginFeatureFlagConfig,
|
||||
} from './types';
|
||||
import { AnyApiFactory } from '../apis';
|
||||
|
||||
@@ -44,6 +45,14 @@ export class PluginImpl<
|
||||
return this.config.apis ?? [];
|
||||
}
|
||||
|
||||
getFeatureFlags(): Iterable<PluginFeatureFlagConfig> {
|
||||
const registeredFlags = this.output()
|
||||
.filter(({ type }) => type === 'feature-flag')
|
||||
.map(({ name }) => ({ name }));
|
||||
|
||||
return registeredFlags;
|
||||
}
|
||||
|
||||
get routes(): Routes {
|
||||
return this.config.routes ?? ({} as Routes);
|
||||
}
|
||||
@@ -56,11 +65,18 @@ export class PluginImpl<
|
||||
if (this.storedOutput) {
|
||||
return this.storedOutput;
|
||||
}
|
||||
if (!this.config.register) {
|
||||
return [];
|
||||
const outputs = new Array<PluginOutput>();
|
||||
this.storedOutput = outputs;
|
||||
|
||||
if (this.config.featureFlags) {
|
||||
for (const flag of this.config.featureFlags) {
|
||||
outputs.push({ type: 'feature-flag', name: flag.name });
|
||||
}
|
||||
}
|
||||
|
||||
const outputs = new Array<PluginOutput>();
|
||||
if (!this.config.register) {
|
||||
return outputs;
|
||||
}
|
||||
|
||||
this.config.register({
|
||||
featureFlags: {
|
||||
@@ -70,7 +86,6 @@ export class PluginImpl<
|
||||
},
|
||||
});
|
||||
|
||||
this.storedOutput = outputs;
|
||||
return this.storedOutput;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,4 +25,5 @@ export type {
|
||||
PluginConfig,
|
||||
PluginHooks,
|
||||
PluginOutput,
|
||||
PluginFeatureFlagConfig,
|
||||
} from './types';
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AnyApiFactory } from '../apis/system';
|
||||
|
||||
/**
|
||||
* Replace with using {@link RouteRef}s.
|
||||
*
|
||||
* @deprecated will be removed
|
||||
* @public
|
||||
*/
|
||||
export type FeatureFlagOutput = {
|
||||
@@ -31,6 +31,7 @@ export type FeatureFlagOutput = {
|
||||
* {@link FeatureFlagOutput} type.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Use {@link BackstagePlugin.getFeatureFlags} instead.
|
||||
*/
|
||||
export type PluginOutput = FeatureFlagOutput;
|
||||
|
||||
@@ -71,13 +72,30 @@ export type BackstagePlugin<
|
||||
ExternalRoutes extends AnyExternalRoutes = {},
|
||||
> = {
|
||||
getId(): string;
|
||||
/**
|
||||
* @deprecated use getFeatureFlags instead.
|
||||
* */
|
||||
output(): PluginOutput[];
|
||||
getApis(): Iterable<AnyApiFactory>;
|
||||
/**
|
||||
* Returns all registered feature flags for this plugin.
|
||||
*/
|
||||
getFeatureFlags(): Iterable<PluginFeatureFlagConfig>;
|
||||
provide<T>(extension: Extension<T>): T;
|
||||
routes: Routes;
|
||||
externalRoutes: ExternalRoutes;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin feature flag configuration.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PluginFeatureFlagConfig = {
|
||||
/** Feature flag name */
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin descriptor type.
|
||||
*
|
||||
@@ -89,14 +107,17 @@ export type PluginConfig<
|
||||
> = {
|
||||
id: string;
|
||||
apis?: Iterable<AnyApiFactory>;
|
||||
/** @deprecated use featureFlags property instead for defining feature flags */
|
||||
register?(hooks: PluginHooks): void;
|
||||
routes?: Routes;
|
||||
externalRoutes?: ExternalRoutes;
|
||||
featureFlags?: PluginFeatureFlagConfig[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Holds hooks registered by the plugin.
|
||||
*
|
||||
* @deprecated - feature flags are now registered in plugin config under featureFlags
|
||||
* @public
|
||||
*/
|
||||
export type PluginHooks = {
|
||||
|
||||
@@ -59,21 +59,21 @@ export class RouteRefImpl<Params extends AnyParams>
|
||||
if (config.path) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`[core-plugin-api] - routeRefs no longer decide their own path, please remove the path for ${this.toString()}. This will be removed in upcoming versions.`,
|
||||
`DEPRECATION WARNING: Passing a path to createRouteRef is deprecated, please remove the path for ${this}.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (config.icon) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`[core-plugin-api] - routeRefs no longer decide their own icon, please remove the icon for ${this.toString()}. This will be removed in upcoming versions.`,
|
||||
`DEPRECATION WARNING: Passing an icon to createRouteRef is deprecated, please remove the icon for ${this}.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (config.title) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn(
|
||||
`[core-plugin-api] - routeRefs no longer decide their own title, please remove the title for ${this.toString()}. This will be removed in upcoming versions.`,
|
||||
`DEPRECATION WARNING: Passing a title to createRouteRef is deprecated, please remove the title for ${this}.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @backstage/create-app
|
||||
|
||||
## 0.4.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- dcaeaac174: Cleaned out the `peerDependencies` in the published version of the package, making it much quicker to run `npx @backstage/create-app` as it no longer needs to install a long list of unnecessary.
|
||||
- a5a5d7e1f1: DefaultTechDocsCollator is now included in the search backend, and the Search Page updated with the SearchType component that includes the techdocs type
|
||||
- bab752e2b3: Change default port of backend from 7000 to 7007.
|
||||
|
||||
This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
|
||||
|
||||
You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
|
||||
|
||||
```
|
||||
backend:
|
||||
listen: 0.0.0.0:7123
|
||||
baseUrl: http://localhost:7123
|
||||
```
|
||||
|
||||
More information can be found here: https://backstage.io/docs/conf/writing
|
||||
|
||||
- 42ebbc18c0: Bump gitbeaker to the latest version
|
||||
|
||||
## 0.4.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/create-app",
|
||||
"description": "A CLI that helps you create your own Backstage app",
|
||||
"version": "0.4.4",
|
||||
"version": "0.4.5",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -25,6 +25,8 @@
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"clean": "backstage-cli clean",
|
||||
"prepack": "node scripts/prepack.js",
|
||||
"postpack": "node scripts/postpack.js",
|
||||
"start": "nodemon --"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
async function main() {
|
||||
const pkgPath = path.resolve(__dirname, '../package.json');
|
||||
const pkgBackupPath = path.resolve(__dirname, '../package.json-prepack');
|
||||
|
||||
try {
|
||||
await fs.move(pkgBackupPath, pkgPath, { overwrite: true });
|
||||
} catch (err) {
|
||||
console.error(`Failed to restore package.json during postpack, ${err}`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env node
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* eslint-disable no-restricted-syntax */
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
async function main() {
|
||||
const pkgPath = path.resolve(__dirname, '../package.json');
|
||||
const pkgBackupPath = path.resolve(__dirname, '../package.json-prepack');
|
||||
|
||||
const pkg = await fs.readJson(pkgPath);
|
||||
await fs.writeJson(pkgBackupPath, pkg, { encoding: 'utf8', spaces: 2 });
|
||||
delete pkg.peerDependencies;
|
||||
await fs.writeJson(pkgPath, pkg, { encoding: 'utf8', spaces: 2 });
|
||||
}
|
||||
|
||||
main().catch(err => {
|
||||
console.error(err.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -6,6 +6,11 @@ organization:
|
||||
name: My Company
|
||||
|
||||
backend:
|
||||
# Used for enabling authentication, secret is shared by all backend plugins
|
||||
# See backend-to-backend-auth.md in the docs for information on the format
|
||||
# auth:
|
||||
# keys:
|
||||
# - secret: ${BACKEND_SECRET}
|
||||
baseUrl: http://localhost:7007
|
||||
listen:
|
||||
port: 7007
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@backstage/plugin-search-backend": "^{{version '@backstage/plugin-search-backend'}}",
|
||||
"@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
|
||||
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
|
||||
"@gitbeaker/node": "^30.2.0",
|
||||
"@gitbeaker/node": "^34.6.0",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"dockerode": "^3.3.1",
|
||||
"express": "^4.17.1",
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
DatabaseManager,
|
||||
SingleHostDiscovery,
|
||||
UrlReaders,
|
||||
ServerTokenManager,
|
||||
} from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import app from './plugins/app';
|
||||
@@ -37,12 +38,13 @@ function makeCreateEnv(config: Config) {
|
||||
|
||||
const cacheManager = CacheManager.fromConfig(config);
|
||||
const databaseManager = DatabaseManager.fromConfig(config);
|
||||
const tokenManager = ServerTokenManager.noop();
|
||||
|
||||
return (plugin: string): PluginEnvironment => {
|
||||
const logger = root.child({ type: 'plugin', plugin });
|
||||
const database = databaseManager.forPlugin(plugin);
|
||||
const cache = cacheManager.forPlugin(plugin);
|
||||
return { logger, database, cache, config, reader, discovery };
|
||||
return { logger, database, cache, config, reader, discovery, tokenManager };
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ export default async function createPlugin({
|
||||
logger,
|
||||
discovery,
|
||||
config,
|
||||
tokenManager,
|
||||
}: PluginEnvironment) {
|
||||
// Initialize a connection to a search engine.
|
||||
const searchEngine = new LunrSearchEngine({ logger });
|
||||
@@ -21,13 +22,20 @@ export default async function createPlugin({
|
||||
// collator gathers entities from the software catalog.
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: DefaultCatalogCollator.fromConfig(config, { discovery }),
|
||||
collator: DefaultCatalogCollator.fromConfig(config, {
|
||||
discovery,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
// collator gathers entities from techdocs.
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: DefaultTechDocsCollator.fromConfig(config, { discovery, logger }),
|
||||
collator: DefaultTechDocsCollator.fromConfig(config, {
|
||||
discovery,
|
||||
logger,
|
||||
tokenManager,
|
||||
}),
|
||||
});
|
||||
|
||||
// The scheduler controls when documents are gathered from collators and sent
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
PluginCacheManager,
|
||||
PluginDatabaseManager,
|
||||
PluginEndpointDiscovery,
|
||||
TokenManager,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
|
||||
@@ -14,4 +15,5 @@ export type PluginEnvironment = {
|
||||
config: Config;
|
||||
reader: UrlReader;
|
||||
discovery: PluginEndpointDiscovery;
|
||||
tokenManager: TokenManager;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @backstage/errors
|
||||
|
||||
## 0.1.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 4d09c60256: Deprecate `parseErrorResponse` in favour of `parseErrorResponseBody`. Deprecate `data` field inside `ErrorResponse` in favour of `body`.
|
||||
Rename the error name for unknown errors from `unknown` to `error`.
|
||||
|
||||
## 0.1.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/errors",
|
||||
"description": "Common utilities for error handling within Backstage",
|
||||
"version": "0.1.4",
|
||||
"version": "0.1.5",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -35,7 +35,7 @@
|
||||
"serialize-error": "^8.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.9.0",
|
||||
"@backstage/cli": "^0.9.1",
|
||||
"@types/jest": "^26.0.7"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -112,7 +112,9 @@ export function getAzureFileFetchUrl(url: string): string;
|
||||
export function getAzureRequestOptions(
|
||||
config: AzureIntegrationConfig,
|
||||
additionalHeaders?: Record<string, string>,
|
||||
): RequestInit;
|
||||
): {
|
||||
headers: Record<string, string>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function getBitbucketDefaultBranch(
|
||||
@@ -135,7 +137,9 @@ export function getBitbucketFileFetchUrl(
|
||||
// @public
|
||||
export function getBitbucketRequestOptions(
|
||||
config: BitbucketIntegrationConfig,
|
||||
): RequestInit;
|
||||
): {
|
||||
headers: Record<string, string>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function getGitHubFileFetchUrl(
|
||||
@@ -148,7 +152,9 @@ export function getGitHubFileFetchUrl(
|
||||
export function getGitHubRequestOptions(
|
||||
config: GitHubIntegrationConfig,
|
||||
credentials: GithubCredentials,
|
||||
): RequestInit;
|
||||
): {
|
||||
headers: Record<string, string>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export function getGitLabFileFetchUrl(
|
||||
@@ -157,9 +163,9 @@ export function getGitLabFileFetchUrl(
|
||||
): Promise<string>;
|
||||
|
||||
// @public
|
||||
export function getGitLabRequestOptions(
|
||||
config: GitLabIntegrationConfig,
|
||||
): RequestInit;
|
||||
export function getGitLabRequestOptions(config: GitLabIntegrationConfig): {
|
||||
headers: Record<string, string>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type GithubAppConfig = {
|
||||
|
||||
@@ -64,8 +64,8 @@ export function getAzureCommitsUrl(url: string): string {
|
||||
export function getAzureRequestOptions(
|
||||
config: AzureIntegrationConfig,
|
||||
additionalHeaders?: Record<string, string>,
|
||||
): RequestInit {
|
||||
const headers: HeadersInit = additionalHeaders
|
||||
): { headers: Record<string, string> } {
|
||||
const headers: Record<string, string> = additionalHeaders
|
||||
? { ...additionalHeaders }
|
||||
: {};
|
||||
|
||||
|
||||
@@ -158,8 +158,8 @@ export function getBitbucketFileFetchUrl(
|
||||
*/
|
||||
export function getBitbucketRequestOptions(
|
||||
config: BitbucketIntegrationConfig,
|
||||
): RequestInit {
|
||||
const headers: HeadersInit = {};
|
||||
): { headers: Record<string, string> } {
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
if (config.token) {
|
||||
headers.Authorization = `Bearer ${config.token}`;
|
||||
|
||||
@@ -73,8 +73,8 @@ export function getGitHubFileFetchUrl(
|
||||
export function getGitHubRequestOptions(
|
||||
config: GitHubIntegrationConfig,
|
||||
credentials: GithubCredentials,
|
||||
): RequestInit {
|
||||
const headers: HeadersInit = {};
|
||||
): { headers: Record<string, string> } {
|
||||
const headers: Record<string, string> = {};
|
||||
|
||||
if (chooseEndpoint(config, credentials) === 'api') {
|
||||
headers.Accept = 'application/vnd.github.v3.raw';
|
||||
|
||||
@@ -54,9 +54,9 @@ export async function getGitLabFileFetchUrl(
|
||||
* @param config - The relevant provider config
|
||||
* @public
|
||||
*/
|
||||
export function getGitLabRequestOptions(
|
||||
config: GitLabIntegrationConfig,
|
||||
): RequestInit {
|
||||
export function getGitLabRequestOptions(config: GitLabIntegrationConfig): {
|
||||
headers: Record<string, string>;
|
||||
} {
|
||||
const { token = '' } = config;
|
||||
return {
|
||||
headers: {
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
"dockerode": "^3.3.1",
|
||||
"fs-extra": "^9.0.1",
|
||||
"http-proxy": "^1.18.1",
|
||||
"react-dev-utils": "^11.0.4",
|
||||
"react-dev-utils": "^12.0.0-next.47",
|
||||
"serve-handler": "^6.1.3",
|
||||
"winston": "^3.2.1"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,27 @@
|
||||
# @backstage/techdocs-common
|
||||
|
||||
## 0.10.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- bab752e2b3: Change default port of backend from 7000 to 7007.
|
||||
|
||||
This is due to the AirPlay Receiver process occupying port 7000 and preventing local Backstage instances on MacOS to start.
|
||||
|
||||
You can change the port back to 7000 or any other value by providing an `app-config.yaml` with the following values:
|
||||
|
||||
```
|
||||
backend:
|
||||
listen: 0.0.0.0:7123
|
||||
baseUrl: http://localhost:7123
|
||||
```
|
||||
|
||||
More information can be found here: https://backstage.io/docs/conf/writing
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/errors@0.1.5
|
||||
- @backstage/backend-common@0.9.11
|
||||
|
||||
## 0.10.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/techdocs-common",
|
||||
"description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli",
|
||||
"version": "0.10.7",
|
||||
"version": "0.10.8",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -38,10 +38,10 @@
|
||||
"dependencies": {
|
||||
"@azure/identity": "^1.5.0",
|
||||
"@azure/storage-blob": "^12.5.0",
|
||||
"@backstage/backend-common": "^0.9.10",
|
||||
"@backstage/backend-common": "^0.9.11",
|
||||
"@backstage/catalog-model": "^0.9.7",
|
||||
"@backstage/config": "^0.1.11",
|
||||
"@backstage/errors": "^0.1.4",
|
||||
"@backstage/errors": "^0.1.5",
|
||||
"@backstage/search-common": "^0.2.1",
|
||||
"@backstage/integration": "^0.6.9",
|
||||
"@google-cloud/storage": "^5.6.0",
|
||||
@@ -60,7 +60,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.9.0",
|
||||
"@backstage/cli": "^0.9.1",
|
||||
"@types/fs-extra": "^9.0.5",
|
||||
"@types/js-yaml": "^4.0.0",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
|
||||
@@ -1,5 +1,72 @@
|
||||
# @backstage/test-utils
|
||||
|
||||
## 0.1.23
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 000190de69: The `ApiRegistry` from `@backstage/core-app-api` class has been deprecated and will be removed in a future release. To replace it, we have introduced two new helpers that are exported from `@backstage/test-utils`, namely `TestApiProvider` and `TestApiRegistry`.
|
||||
|
||||
These two new helpers are more tailored for writing tests and development setups, as they allow for partial implementations of each of the APIs.
|
||||
|
||||
When migrating existing code it is typically best to prefer usage of `TestApiProvider` when possible, so for example the following code:
|
||||
|
||||
```tsx
|
||||
render(
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[identityApiRef, mockIdentityApi as unknown as IdentityApi]
|
||||
])}
|
||||
>
|
||||
{...}
|
||||
</ApiProvider>
|
||||
)
|
||||
```
|
||||
|
||||
Would be migrated to this:
|
||||
|
||||
```tsx
|
||||
render(
|
||||
<TestApiProvider apis={[[identityApiRef, mockIdentityApi]]}>
|
||||
{...}
|
||||
</TestApiProvider>
|
||||
)
|
||||
```
|
||||
|
||||
In cases where the `ApiProvider` is used in a more standalone way, for example to reuse a set of APIs across multiple tests, the `TestApiRegistry` can be used instead. Note that the `TestApiRegistry` only has a single static factory method, `.from()`, and it is slightly different from the existing `.from()` method on `ApiRegistry` in that it doesn't require the API pairs to be wrapped in an outer array.
|
||||
|
||||
Usage that looks like this:
|
||||
|
||||
```ts
|
||||
const apis = ApiRegistry.with(
|
||||
identityApiRef,
|
||||
mockIdentityApi as unknown as IdentityApi,
|
||||
).with(configApiRef, new ConfigReader({}));
|
||||
```
|
||||
|
||||
OR like this:
|
||||
|
||||
```ts
|
||||
const apis = ApiRegistry.from([
|
||||
[identityApiRef, mockIdentityApi as unknown as IdentityApi],
|
||||
[configApiRef, new ConfigReader({})],
|
||||
]);
|
||||
```
|
||||
|
||||
Would be migrated to this:
|
||||
|
||||
```ts
|
||||
const apis = TestApiRegistry.from(
|
||||
[identityApiRef, mockIdentityApi],
|
||||
[configApiRef, new ConfigReader({})],
|
||||
);
|
||||
```
|
||||
|
||||
If your app is still using the `ApiRegistry` to construct the `apis` for `createApp`, we recommend that you move over to use the new method of supplying API factories instead, using `createApiFactory`.
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/core-app-api@0.1.23
|
||||
- @backstage/core-plugin-api@0.2.1
|
||||
|
||||
## 0.1.22
|
||||
|
||||
### Patch Changes
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user