Merge branch 'master' into rugvip/nouser
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Builtin support for cookiecutter based templates has been removed from `@backstage/plugin-scaffolder-backend`. Due to this, the `containerRunner` argument to its `createRouter` has also been removed.
|
||||
|
||||
If you do not use cookiecutter templates and are fine with removing support from it in your own installation, update your `packages/backend/src/plugins/scaffolder.ts` file as follows:
|
||||
|
||||
```diff
|
||||
-import { DockerContainerRunner } from '@backstage/backend-common';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { createRouter } from '@backstage/plugin-scaffolder-backend';
|
||||
-import Docker from 'dockerode';
|
||||
import { Router } from 'express';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({
|
||||
reader,
|
||||
discovery,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
- const dockerClient = new Docker();
|
||||
- const containerRunner = new DockerContainerRunner({ dockerClient });
|
||||
-
|
||||
const catalogClient = new CatalogClient({ discoveryApi: discovery });
|
||||
-
|
||||
return await createRouter({
|
||||
- containerRunner,
|
||||
logger,
|
||||
config,
|
||||
// ...
|
||||
```
|
||||
|
||||
If you want to retain cookiecutter support, please use the `@backstage/plugin-scaffolder-backend-module-cookiecutter` package explicitly (see [its README](https://github.com/backstage/backstage/tree/master/plugins/scaffolder-backend-module-cookiecutter) for installation instructions).
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `useEntityFromUrl`.
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING**:
|
||||
|
||||
- Removed the `createFetchCookiecutterAction` export, please use the `@backstage/plugin-scaffolder-backend-module-cookiecutter` package explicitly (see [its README](https://github.com/backstage/backstage/tree/master/plugins/scaffolder-backend-module-cookiecutter) for installation instructions).
|
||||
- Removed the `containerRunner` argument from the types `RouterOptions` (as used by `createRouter`) and `CreateBuiltInActionsOptions` (as used by `createBuiltinActions`).
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/catalog-model': minor
|
||||
---
|
||||
|
||||
**BREAKING**:
|
||||
|
||||
- Removed the previously deprecated type `EntityRef`. Please use `string` for stringified entity refs, `CompoundEntityRef` for compound kind-namespace-name triplet objects, or custom objects like `{ kind?: string; namespace?: string; name: string }` and similar if you have need for partial types.
|
||||
- Removed the previously deprecated type `LocationSpec` type, which has been moved to `@backstage/plugin-catalog-backend`.
|
||||
- Removed the previously deprecated function `parseEntityName`. Please use `parseEntityRef` instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Fixed display of the location in the log message that is printed when entity envelope validation fails.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed the previously deprecated `OctokitProvider` class.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Postgres-based search is now installed when PG is chosen as the desired database for Backstage.
|
||||
|
||||
There is no need to make this change in an existing Backstage backend. See [supported search engines](https://backstage.io/docs/features/search/search-engines) for details about production-ready search engines.
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': minor
|
||||
---
|
||||
|
||||
**BREAKING**: Removed `useOwnedEntities` and moved its usage internally to the scaffolder-backend where it's used.
|
||||
|
||||
**BREAKING**: Removed `EntityTypeReturn` type which is now inlined.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Changed the logic for how modules are marked as external in the Rollup build of packages. Rather than only marking dependencies and build-in Node.js modules as external, all non-relative imports are now considered external.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Internalized usage of `useOwnedEntities` hook.
|
||||
@@ -56,18 +56,7 @@ jobs:
|
||||
github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }}
|
||||
# TODO(Rugvip): Remove the create-app dispatch once we've been on the release version for a while
|
||||
script: |
|
||||
console.log('Dispatching upgrade helper sync - release version');
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'backstage',
|
||||
repo: 'upgrade-helper-diff',
|
||||
workflow_id: 'release.yml',
|
||||
ref: 'master',
|
||||
inputs: {
|
||||
version: require('./backstage/package.json').version,
|
||||
},
|
||||
});
|
||||
|
||||
console.log('Dispatching upgrade helper sync - create-app version');
|
||||
console.log('Dispatching upgrade helper sync');
|
||||
await github.rest.actions.createWorkflowDispatch({
|
||||
owner: 'backstage',
|
||||
repo: 'upgrade-helper-diff',
|
||||
@@ -75,5 +64,6 @@ jobs:
|
||||
ref: 'master',
|
||||
inputs: {
|
||||
version: require('./backstage/packages/create-app/package.json').version,
|
||||
releaseVersion: require('./backstage/package.json').version
|
||||
},
|
||||
});
|
||||
|
||||
@@ -24,12 +24,12 @@ Backstage ecosystem.
|
||||
|
||||
## Project roadmap
|
||||
|
||||
| Version | Description |
|
||||
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Backstage Search Pre-Alpha ✅ | Search Frontend letting you search through the entities of the software catalog. [See Pre-Alpha Use Cases.](#backstage-search-pre-alpha) |
|
||||
| Backstage Search Alpha ✅ | Basic “out-of-the-box” in-memory indexing process of entities, and their metadata, registered to the Software Catalog. [See Alpha Use Cases](#backstage-search-alpha). |
|
||||
| [Backstage Search Beta ⌛][beta] | At least one production-ready search engine that supports the same use-cases as in the alpha. [See Beta Use Cases](#backstage-search-beta). |
|
||||
| [Backstage Search GA ⌛][ga] | A stable Search API for plugin developers to add search to their plugins, and app integrators to expose that to their users. [See GA Use Cases](#backstage-search-ga). |
|
||||
| Version | Description |
|
||||
| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| Backstage Search Pre-Alpha ✅ | Search Frontend letting you search through the entities of the software catalog. [See Pre-Alpha Use Cases.](#backstage-search-pre-alpha) |
|
||||
| Backstage Search Alpha ✅ | Basic “out-of-the-box” in-memory indexing process of entities, and their metadata, registered to the Software Catalog. [See Alpha Use Cases](#backstage-search-alpha). |
|
||||
| Backstage Search Beta ✅ | At least one production-ready search engine that supports the same use-cases as in the alpha. [See Beta Use Cases](#backstage-search-beta). |
|
||||
| [Backstage Search GA ⌛][ga] | A stable Search API for plugin developers to add search to their plugins, and app integrators to expose that to their users. [See GA Use Cases](#backstage-search-ga). |
|
||||
|
||||
[beta]: https://github.com/backstage/backstage/milestone/27
|
||||
[ga]: https://github.com/backstage/backstage/milestone/28
|
||||
|
||||
@@ -137,9 +137,10 @@ entities are indexed by the search engine.
|
||||
|
||||
For the purposes of this guide, Search Beta version is defined as:
|
||||
|
||||
- **Search Plugin**: At least `v0.x.y`
|
||||
- **Search Backend Plugin**: At least `v0.x.y`
|
||||
- **Search Backend Node**: At least `v0.x.y`
|
||||
- **Search Plugin**: At least `v0.7.2`
|
||||
- **Search Backend Plugin**: At least `v0.4.6`
|
||||
- **Search Backend Node**: At least `v0.5.0`
|
||||
- **Search Common**: At least `v0.3.0`
|
||||
|
||||
In the Beta version, the Search Platform's indexing process has been rewritten
|
||||
as a stream pipeline in order to improve efficiency and performance on large
|
||||
@@ -149,11 +150,14 @@ If you've not yet extended the Search Platform with custom code, and have
|
||||
instead taken advantage of default collators, decorators, and search engines
|
||||
provided by existing plugins, the migration process is fairly straightforward:
|
||||
|
||||
1. Upgrade to at least version `0.x.y` of
|
||||
1. Upgrade to at least version `0.5.0` of
|
||||
`@backstage/plugin-search-backend-node`, as well as any backend plugins whose
|
||||
collators you are using (e.g. at least version `0.x.y` of
|
||||
`@backstage/plugin-catalog-backend` and/or version `0.x.y` of
|
||||
`@backstage/plugin-techdocs-backend`).
|
||||
collators you are using (e.g. at least version `0.23.0` of
|
||||
`@backstage/plugin-catalog-backend` and/or version `0.14.1` of
|
||||
`@backstage/plugin-techdocs-backend`), as well as any search-engine specific
|
||||
plugin you are using (e.g. at least version `0.3.0` of
|
||||
`@backstage/plugin-search-backend-module-pg` or version `0.1.0` of
|
||||
`@backstage/plugin-search-backend-module-elasticsearch`).
|
||||
2. Then, make the following changes to your
|
||||
`/packages/backend/src/plugins/search.ts` file:
|
||||
|
||||
|
||||
@@ -181,12 +181,10 @@ files like stylesheets or images. For more details on what syntax and file
|
||||
formats are supported by the build process, see the [loaders section](#loaders).
|
||||
|
||||
When building CommonJS or ESM output, the build commands will always use
|
||||
`src/index.ts` as the entrypoint. All dependencies of the package will be marked
|
||||
as external, meaning that in general it is only the contents of the `src` folder
|
||||
that ends up being compiled and output to `dist`. All import statements of
|
||||
external dependencies, even within the same monorepo, will stay intact. The
|
||||
externalized dependencies are based on dependency information in `package.json`,
|
||||
which means it's important to keep it up to date.
|
||||
`src/index.ts` as the entrypoint. All non-relative modules imports are considered
|
||||
external, meaning the Rollup build will only compile the source code of the package
|
||||
itself. All import statements of external dependencies, even within the same
|
||||
monorepo, will stay intact.
|
||||
|
||||
The build of the type definitions works quite differently. The entrypoint of the
|
||||
type definition build is the relative location of the package within the
|
||||
|
||||
@@ -14,10 +14,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DockerContainerRunner } from '@backstage/backend-common';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { createRouter } from '@backstage/plugin-scaffolder-backend';
|
||||
import Docker from 'dockerode';
|
||||
import { Router } from 'express';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
|
||||
@@ -28,13 +26,8 @@ export default async function createPlugin({
|
||||
reader,
|
||||
discovery,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
const dockerClient = new Docker();
|
||||
const containerRunner = new DockerContainerRunner({ dockerClient });
|
||||
|
||||
const catalogClient = new CatalogClient({ discoveryApi: discovery });
|
||||
|
||||
return await createRouter({
|
||||
containerRunner,
|
||||
logger,
|
||||
config,
|
||||
database,
|
||||
|
||||
@@ -190,15 +190,6 @@ export type EntityPolicy = {
|
||||
enforce(entity: Entity): Promise<Entity | undefined>;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export type EntityRef =
|
||||
| string
|
||||
| {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type EntityRelation = {
|
||||
type: string;
|
||||
@@ -316,13 +307,6 @@ export { LocationEntityV1alpha1 };
|
||||
// @public
|
||||
export const locationEntityV1alpha1Validator: KindValidator;
|
||||
|
||||
// @public @deprecated
|
||||
export type LocationSpec = {
|
||||
type: string;
|
||||
target: string;
|
||||
presence?: 'optional' | 'required';
|
||||
};
|
||||
|
||||
// @public
|
||||
export function makeValidator(overrides?: Partial<Validators>): Validators;
|
||||
|
||||
@@ -333,21 +317,6 @@ export class NoForeignRootFieldsEntityPolicy implements EntityPolicy {
|
||||
enforce(entity: Entity): Promise<Entity>;
|
||||
}
|
||||
|
||||
// @public @deprecated
|
||||
export function parseEntityName(
|
||||
ref:
|
||||
| string
|
||||
| {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
},
|
||||
context?: {
|
||||
defaultKind?: string;
|
||||
defaultNamespace?: string;
|
||||
},
|
||||
): CompoundEntityRef;
|
||||
|
||||
// @public
|
||||
export function parseEntityRef(
|
||||
ref:
|
||||
|
||||
@@ -36,7 +36,6 @@ export * from './policies';
|
||||
export {
|
||||
getCompoundEntityRef,
|
||||
getEntityName,
|
||||
parseEntityName,
|
||||
parseEntityRef,
|
||||
stringifyEntityRef,
|
||||
} from './ref';
|
||||
|
||||
@@ -14,171 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DEFAULT_NAMESPACE } from './constants';
|
||||
import { parseEntityName, parseEntityRef } from './ref';
|
||||
import { parseEntityRef } from './ref';
|
||||
|
||||
describe('ref', () => {
|
||||
describe('parseEntityName', () => {
|
||||
it('handles some omissions', () => {
|
||||
expect(parseEntityName('a:b/c')).toEqual({
|
||||
kind: 'a',
|
||||
namespace: 'b',
|
||||
name: 'c',
|
||||
});
|
||||
expect(() => parseEntityName('b/c')).toThrow(/kind/);
|
||||
expect(parseEntityName('a:c')).toEqual({
|
||||
kind: 'a',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: 'c',
|
||||
});
|
||||
expect(() => parseEntityName('c')).toThrow(/kind/);
|
||||
});
|
||||
|
||||
it('rejects bad inputs', () => {
|
||||
expect(() => parseEntityName(null as any)).toThrow();
|
||||
expect(() => parseEntityName(7 as any)).toThrow();
|
||||
expect(() => parseEntityName('a:b:c')).toThrow();
|
||||
expect(() => parseEntityName('a/b/c')).toThrow();
|
||||
expect(() => parseEntityName('a/b:c')).toThrow();
|
||||
expect(() => parseEntityName('a:b/c/d')).toThrow();
|
||||
expect(() => parseEntityName('a:b/c:d')).toThrow();
|
||||
});
|
||||
|
||||
it('rejects empty parts in strings', () => {
|
||||
// one is empty
|
||||
expect(() => parseEntityName(':b/c')).toThrow();
|
||||
expect(() => parseEntityName('a:/c')).toThrow();
|
||||
expect(() => parseEntityName('a:b/')).toThrow();
|
||||
// two are empty
|
||||
expect(() => parseEntityName('a:/')).toThrow();
|
||||
expect(() => parseEntityName(':b/')).toThrow();
|
||||
expect(() => parseEntityName(':/c')).toThrow();
|
||||
// three are empty
|
||||
expect(() => parseEntityName(':/')).toThrow();
|
||||
// one is left out, one empty
|
||||
expect(() => parseEntityName('/c')).toThrow();
|
||||
expect(() => parseEntityName('b/')).toThrow();
|
||||
expect(() => parseEntityName(':c')).toThrow();
|
||||
expect(() => parseEntityName('a:')).toThrow();
|
||||
// nothing at all
|
||||
expect(() => parseEntityName('')).toThrow();
|
||||
});
|
||||
|
||||
it('rejects empty parts in compounds', () => {
|
||||
// one is empty
|
||||
expect(() =>
|
||||
parseEntityName({ kind: '', namespace: 'b', name: 'c' }),
|
||||
).toThrow();
|
||||
expect(() => parseEntityName({ namespace: 'b', name: 'c' })).toThrow();
|
||||
expect(() =>
|
||||
parseEntityName({ kind: 'a', namespace: '', name: 'c' }),
|
||||
).toThrow();
|
||||
expect(() => parseEntityName({ kind: 'a', name: 'c' })).not.toThrow();
|
||||
expect(() =>
|
||||
parseEntityName({ kind: 'a', namespace: 'b', name: '' }),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
parseEntityName({ kind: 'a', namespace: 'b' } as any),
|
||||
).toThrow();
|
||||
// two are empty
|
||||
expect(() =>
|
||||
parseEntityName({ kind: '', namespace: '', name: 'c' }),
|
||||
).toThrow();
|
||||
expect(() => parseEntityName({ name: 'c' })).toThrow();
|
||||
expect(() =>
|
||||
parseEntityName({ kind: '', namespace: 'b', name: '' }),
|
||||
).toThrow();
|
||||
expect(() => parseEntityName({ namespace: 'b' } as any)).toThrow();
|
||||
expect(() =>
|
||||
parseEntityName({ kind: 'a', namespace: '', name: '' }),
|
||||
).toThrow();
|
||||
expect(() => parseEntityName({ kind: 'a' } as any)).toThrow();
|
||||
// three are empty
|
||||
expect(() =>
|
||||
parseEntityName({ kind: '', namespace: '', name: '' }),
|
||||
).toThrow();
|
||||
expect(() => parseEntityName({} as any)).toThrow();
|
||||
// one is left out, one empty
|
||||
expect(() => parseEntityName({ namespace: '', name: 'c' })).toThrow();
|
||||
expect(() => parseEntityName({ namespace: 'b', name: '' })).toThrow();
|
||||
expect(() => parseEntityName({ kind: '', name: 'c' })).toThrow();
|
||||
expect(() => parseEntityName({ kind: 'a', name: '' })).toThrow();
|
||||
});
|
||||
|
||||
it('adds defaults where necessary to strings', () => {
|
||||
expect(
|
||||
parseEntityName('a:b/c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'a', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityName('b/c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'x', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityName('a:c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'a', namespace: 'y', name: 'c' });
|
||||
expect(parseEntityName('a:c', { defaultKind: 'x' })).toEqual({
|
||||
kind: 'a',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: 'c',
|
||||
});
|
||||
expect(
|
||||
parseEntityName('c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'x', namespace: 'y', name: 'c' });
|
||||
expect(parseEntityName('c', { defaultKind: 'x' })).toEqual({
|
||||
kind: 'x',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: 'c',
|
||||
});
|
||||
});
|
||||
|
||||
it('adds defaults where necessary to compounds', () => {
|
||||
expect(
|
||||
parseEntityName(
|
||||
{ kind: 'a', namespace: 'b', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'a', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityName(
|
||||
{ namespace: 'b', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'x', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityName(
|
||||
{ kind: 'a', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'a', namespace: 'y', name: 'c' });
|
||||
expect(
|
||||
parseEntityName({ kind: 'a', name: 'c' }, { defaultKind: 'x' }),
|
||||
).toEqual({ kind: 'a', namespace: DEFAULT_NAMESPACE, name: 'c' });
|
||||
expect(
|
||||
parseEntityName(
|
||||
{ name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'x', namespace: 'y', name: 'c' });
|
||||
expect(parseEntityName({ name: 'c' }, { defaultKind: 'x' })).toEqual({
|
||||
kind: 'x',
|
||||
namespace: DEFAULT_NAMESPACE,
|
||||
name: 'c',
|
||||
});
|
||||
// empty strings are errors, not defaults
|
||||
expect(() =>
|
||||
parseEntityName(
|
||||
{ kind: '', namespace: 'b', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toThrow(/kind/);
|
||||
expect(() =>
|
||||
parseEntityName(
|
||||
{ kind: 'a', namespace: '', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toThrow(/namespace/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseEntityRef', () => {
|
||||
it('handles some omissions', () => {
|
||||
expect(parseEntityRef('a:b/c')).toEqual({
|
||||
|
||||
@@ -64,45 +64,6 @@ export function getCompoundEntityRef(entity: Entity): CompoundEntityRef {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an entity reference, either on string or compound form, and always
|
||||
* returns a complete entity name including kind, namespace and name.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* This function automatically assumes the default namespace "default" unless
|
||||
* otherwise specified as part of the options, and will throw an error if no
|
||||
* kind was specified in the input reference and no default kind was given.
|
||||
*
|
||||
* @deprecated Please use parseEntityRef instead
|
||||
* @public
|
||||
* @param ref - The reference to parse
|
||||
* @param context - The context of defaults that the parsing happens within
|
||||
* @returns A complete entity name
|
||||
*/
|
||||
export function parseEntityName(
|
||||
ref: string | { kind?: string; namespace?: string; name: string },
|
||||
context: {
|
||||
/** The default kind, if none is given in the reference */
|
||||
defaultKind?: string;
|
||||
/** The default namespace, if none is given in the reference */
|
||||
defaultNamespace?: string;
|
||||
} = {},
|
||||
): CompoundEntityRef {
|
||||
const { kind, namespace, name } = parseEntityRef(ref, {
|
||||
defaultNamespace: DEFAULT_NAMESPACE,
|
||||
...context,
|
||||
});
|
||||
|
||||
if (!kind) {
|
||||
throw new Error(
|
||||
`Entity reference ${namespace}/${name} did not contain a kind`,
|
||||
);
|
||||
}
|
||||
|
||||
return { kind, namespace, name };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an entity reference, either on string or compound form, and returns
|
||||
* a structure with a name, and optional kind and namespace.
|
||||
|
||||
@@ -24,5 +24,5 @@ export * from './entity';
|
||||
export { EntityPolicies } from './EntityPolicies';
|
||||
export * from './kinds';
|
||||
export * from './location';
|
||||
export type { EntityName, EntityRef, CompoundEntityRef } from './types';
|
||||
export type { EntityName, CompoundEntityRef } from './types';
|
||||
export * from './validation';
|
||||
|
||||
@@ -15,19 +15,25 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Constant storing location annotation.
|
||||
* Entity annotation containing the location from which the entity is sourced.
|
||||
*
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export const ANNOTATION_LOCATION = 'backstage.io/managed-by-location';
|
||||
|
||||
/**
|
||||
* Constant storing origin location annotation
|
||||
* Entity annotation containing the originally sourced location which ultimately
|
||||
* led to this entity being ingested.
|
||||
*
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export const ANNOTATION_ORIGIN_LOCATION =
|
||||
'backstage.io/managed-by-origin-location';
|
||||
|
||||
/**
|
||||
* Contant storing source location annotation
|
||||
* Entity annotation pointing to the source (e.g. source code repository root or
|
||||
* similar) for this entity.
|
||||
*
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export const ANNOTATION_SOURCE_LOCATION = 'backstage.io/source-location';
|
||||
|
||||
@@ -24,4 +24,3 @@ export {
|
||||
parseLocationRef,
|
||||
stringifyLocationRef,
|
||||
} from './helpers';
|
||||
export type { LocationSpec } from './types';
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Holds the entity location information.
|
||||
*
|
||||
* @remarks
|
||||
*
|
||||
* `presence` flag: when using repo importer plugin, location is being created before the component yaml file is merged to the main branch.
|
||||
* This flag is then set to indicate that the file can be not present.
|
||||
* default value: 'required'.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Import from `@backstage/plugin-catalog-backend` instead.
|
||||
*/
|
||||
export type LocationSpec = {
|
||||
type: string;
|
||||
target: string;
|
||||
presence?: 'optional' | 'required';
|
||||
};
|
||||
@@ -33,25 +33,3 @@ export type CompoundEntityRef = {
|
||||
* @public
|
||||
*/
|
||||
export type EntityName = CompoundEntityRef;
|
||||
|
||||
/**
|
||||
* A reference by name to an entity, either as a compact string representation,
|
||||
* or as a compound reference structure.
|
||||
*
|
||||
* @deprecated Please use string directly, or EntityName (depending on what you actually need)
|
||||
* @remarks
|
||||
*
|
||||
* The string representation is on the form `[<kind>:][<namespace>/]<name>`.
|
||||
*
|
||||
* Left-out parts of the reference need to be handled by the application,
|
||||
* either by rejecting the reference or by falling back to default values.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type EntityRef =
|
||||
| string
|
||||
| {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
@@ -103,7 +103,6 @@
|
||||
"rollup": "^2.60.2",
|
||||
"rollup-plugin-dts": "^4.0.1",
|
||||
"rollup-plugin-esbuild": "^4.7.2",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.2",
|
||||
"rollup-plugin-postcss": "^4.0.0",
|
||||
"rollup-pluginutils": "^2.8.2",
|
||||
"run-script-webpack-plugin": "^0.0.11",
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright 2020 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 { ExternalOption } from 'rollup';
|
||||
import { makeRollupConfigs } from './config';
|
||||
import { Output } from './types';
|
||||
|
||||
describe('makeRollupConfigs', () => {
|
||||
it('should mark external modules correctly', async () => {
|
||||
const importerPath = '/some/path.ts'; // when specified we don't care about the path
|
||||
|
||||
const [config] = await makeRollupConfigs({
|
||||
outputs: new Set([Output.cjs]),
|
||||
});
|
||||
const external = config.external as Exclude<
|
||||
ExternalOption,
|
||||
string | RegExp | (string | RegExp)[]
|
||||
>;
|
||||
|
||||
expect(external('foo', importerPath, false)).toBe(true);
|
||||
expect(external('./foo', importerPath, false)).toBe(false);
|
||||
expect(external('/foo', importerPath, false)).toBe(false);
|
||||
expect(external('.\\foo', importerPath, false)).toBe(false);
|
||||
expect(external('c:\\foo', importerPath, false)).toBe(false);
|
||||
expect(external('@foo/bar', importerPath, false)).toBe(true);
|
||||
expect(external('../foo', importerPath, false)).toBe(false);
|
||||
|
||||
// Modules without an importer are entry points, i.e. not external
|
||||
expect(external('foo', undefined, false)).toBe(false);
|
||||
expect(external('./foo', undefined, false)).toBe(false);
|
||||
expect(external('/foo', undefined, false)).toBe(false);
|
||||
expect(external('.\\foo', undefined, false)).toBe(false);
|
||||
expect(external('c:\\foo', undefined, false)).toBe(false);
|
||||
expect(external('@foo/bar', undefined, false)).toBe(false);
|
||||
expect(external('../foo', undefined, false)).toBe(false);
|
||||
|
||||
// After modules have been resolved they're never marked as external
|
||||
expect(external('foo', importerPath, true)).toBe(false);
|
||||
expect(external('./foo', importerPath, true)).toBe(false);
|
||||
expect(external('/foo', importerPath, true)).toBe(false);
|
||||
expect(external('.\\foo', importerPath, true)).toBe(false);
|
||||
expect(external('c:\\foo', importerPath, true)).toBe(false);
|
||||
expect(external('@foo/bar', importerPath, true)).toBe(false);
|
||||
expect(external('../foo', importerPath, true)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -17,7 +17,6 @@
|
||||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import { relative as relativePath, resolve as resolvePath } from 'path';
|
||||
import peerDepsExternal from 'rollup-plugin-peer-deps-external';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
@@ -33,6 +32,19 @@ import { BuildOptions, Output } from './types';
|
||||
import { paths } from '../paths';
|
||||
import { svgrTemplate } from '../svgrTemplate';
|
||||
|
||||
function isFileImport(source: string) {
|
||||
if (source.startsWith('.')) {
|
||||
return true;
|
||||
}
|
||||
if (source.startsWith('/')) {
|
||||
return true;
|
||||
}
|
||||
if (source.match(/[a-z]:/i)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export async function makeRollupConfigs(
|
||||
options: BuildOptions,
|
||||
): Promise<RollupOptions[]> {
|
||||
@@ -81,12 +93,10 @@ export async function makeRollupConfigs(
|
||||
output,
|
||||
onwarn,
|
||||
preserveEntrySignatures: 'strict',
|
||||
external: require('module').builtinModules,
|
||||
// All module imports are always marked as external
|
||||
external: (source, importer, isResolved) =>
|
||||
Boolean(importer && !isResolved && !isFileImport(source)),
|
||||
plugins: [
|
||||
peerDepsExternal({
|
||||
packageJsonPath: resolvePath(targetDir, 'package.json'),
|
||||
includeDependencies: true,
|
||||
}),
|
||||
resolve({ mainFields }),
|
||||
commonjs({
|
||||
include: /node_modules/,
|
||||
|
||||
@@ -99,7 +99,7 @@ export interface DependencyGraphProps<NodeData, EdgeData>
|
||||
/**
|
||||
* Margin on top and bottom of whole graph
|
||||
*
|
||||
* @remarks
|
||||
* @remarks
|
||||
*
|
||||
* Default: 0
|
||||
*/
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
"@backstage/plugin-scaffolder-backend": "",
|
||||
"@backstage/plugin-search": "",
|
||||
"@backstage/plugin-search-backend": "",
|
||||
"@backstage/plugin-search-backend-module-pg": "",
|
||||
"@backstage/plugin-search-backend-node": "",
|
||||
"@backstage/plugin-tech-radar": "",
|
||||
"@backstage/plugin-techdocs": "",
|
||||
|
||||
@@ -70,6 +70,7 @@ import { version as pluginScaffolder } from '../../../../plugins/scaffolder/pack
|
||||
import { version as pluginScaffolderBackend } from '../../../../plugins/scaffolder-backend/package.json';
|
||||
import { version as pluginSearch } from '../../../../plugins/search/package.json';
|
||||
import { version as pluginSearchBackend } from '../../../../plugins/search-backend/package.json';
|
||||
import { version as pluginSearchBackendModulePg } from '../../../../plugins/search-backend-module-pg/package.json';
|
||||
import { version as pluginSearchBackendNode } from '../../../../plugins/search-backend-node/package.json';
|
||||
import { version as pluginTechRadar } from '../../../../plugins/tech-radar/package.json';
|
||||
import { version as pluginTechdocs } from '../../../../plugins/techdocs/package.json';
|
||||
@@ -113,6 +114,7 @@ export const packageVersions = {
|
||||
'@backstage/plugin-scaffolder-backend': pluginScaffolderBackend,
|
||||
'@backstage/plugin-search': pluginSearch,
|
||||
'@backstage/plugin-search-backend': pluginSearchBackend,
|
||||
'@backstage/plugin-search-backend-module-pg': pluginSearchBackendModulePg,
|
||||
'@backstage/plugin-search-backend-node': pluginSearchBackendNode,
|
||||
'@backstage/plugin-tech-radar': pluginTechRadar,
|
||||
'@backstage/plugin-techdocs': pluginTechdocs,
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
"@backstage/plugin-proxy-backend": "^{{version '@backstage/plugin-proxy-backend'}}",
|
||||
"@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}",
|
||||
"@backstage/plugin-search-backend": "^{{version '@backstage/plugin-search-backend'}}",
|
||||
{{#if dbTypePG}}
|
||||
"@backstage/plugin-search-backend-module-pg": "^{{version '@backstage/plugin-search-backend-module-pg'}}",
|
||||
{{/if}}
|
||||
"@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
|
||||
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
|
||||
"@gitbeaker/node": "^34.6.0",
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { DockerContainerRunner } from '@backstage/backend-common';
|
||||
import { CatalogClient } from '@backstage/catalog-client';
|
||||
import { createRouter } from '@backstage/plugin-scaffolder-backend';
|
||||
import Docker from 'dockerode';
|
||||
import { Router } from 'express';
|
||||
import type { PluginEnvironment } from '../types';
|
||||
|
||||
@@ -12,12 +10,8 @@ export default async function createPlugin({
|
||||
reader,
|
||||
discovery,
|
||||
}: PluginEnvironment): Promise<Router> {
|
||||
const dockerClient = new Docker();
|
||||
const containerRunner = new DockerContainerRunner({ dockerClient });
|
||||
const catalogClient = new CatalogClient({ discoveryApi: discovery });
|
||||
|
||||
return await createRouter({
|
||||
containerRunner,
|
||||
logger,
|
||||
config,
|
||||
database,
|
||||
|
||||
+10
@@ -4,6 +4,9 @@ import {
|
||||
IndexBuilder,
|
||||
LunrSearchEngine,
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
{{#if dbTypePG}}
|
||||
import { PgSearchEngine } from '@backstage/plugin-search-backend-module-pg';
|
||||
{{/if}}
|
||||
import { PluginEnvironment } from '../types';
|
||||
import { DefaultCatalogCollatorFactory } from '@backstage/plugin-catalog-backend';
|
||||
import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-techdocs-backend';
|
||||
@@ -16,7 +19,14 @@ export default async function createPlugin({
|
||||
tokenManager,
|
||||
}: PluginEnvironment) {
|
||||
// Initialize a connection to a search engine.
|
||||
{{#if dbTypeSqlite}}
|
||||
const searchEngine = new LunrSearchEngine({ logger });
|
||||
{{/if}}
|
||||
{{#if dbTypePG}}
|
||||
const searchEngine = (await PgSearchEngine.supported(database))
|
||||
? await PgSearchEngine.from({ database })
|
||||
: new LunrSearchEngine({ logger });
|
||||
{{/if}}
|
||||
const indexBuilder = new IndexBuilder({ logger, searchEngine });
|
||||
|
||||
// Collators are responsible for gathering documents known to plugins. This
|
||||
@@ -420,9 +420,15 @@ async function testBackendStart(appDir: string, isPostgres: boolean) {
|
||||
if (isPostgres) {
|
||||
print('Dropping old DBs');
|
||||
await Promise.all(
|
||||
['catalog', 'scaffolder', 'auth', 'identity', 'proxy', 'techdocs'].map(
|
||||
name => dropDB(`backstage_plugin_${name}`),
|
||||
),
|
||||
[
|
||||
'catalog',
|
||||
'scaffolder',
|
||||
'auth',
|
||||
'identity',
|
||||
'proxy',
|
||||
'techdocs',
|
||||
'search',
|
||||
].map(name => dropDB(`backstage_plugin_${name}`)),
|
||||
);
|
||||
print('Created DBs');
|
||||
}
|
||||
|
||||
@@ -18,37 +18,44 @@
|
||||
|
||||
/**
|
||||
* Severity levels of {@link CollectedLogs}
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export type LogFuncs = 'log' | 'warn' | 'error';
|
||||
/**
|
||||
* AsyncLogCollector type used in {@link (withLogCollector:1)} callback function.
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export type AsyncLogCollector = () => Promise<void>;
|
||||
/**
|
||||
* SyncLogCollector type used in {@link (withLogCollector:2)} callback function.
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export type SyncLogCollector = () => void;
|
||||
/**
|
||||
* Union type used in {@link (withLogCollector:3)} callback function.
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export type LogCollector = AsyncLogCollector | SyncLogCollector;
|
||||
/**
|
||||
* Map of severity level and corresponding log lines.
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export type CollectedLogs<T extends LogFuncs> = { [key in T]: string[] };
|
||||
|
||||
const allCategories = ['log', 'warn', 'error'];
|
||||
|
||||
/**
|
||||
* Asynchronous log collector with that collects all categories
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export function withLogCollector(
|
||||
callback: AsyncLogCollector,
|
||||
): Promise<CollectedLogs<LogFuncs>>;
|
||||
|
||||
/**
|
||||
* Synchronous log collector with that collects all categories
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export function withLogCollector(
|
||||
callback: SyncLogCollector,
|
||||
): CollectedLogs<LogFuncs>;
|
||||
@@ -64,7 +71,8 @@ export function withLogCollector<T extends LogFuncs>(
|
||||
|
||||
/**
|
||||
* Synchronous log collector with that only collects selected categories
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export function withLogCollector<T extends LogFuncs>(
|
||||
logsToCollect: T[],
|
||||
callback: SyncLogCollector,
|
||||
@@ -73,7 +81,7 @@ export function withLogCollector<T extends LogFuncs>(
|
||||
/**
|
||||
* Log collector that collect logs either from a sync or async collector.
|
||||
* @public
|
||||
* */
|
||||
*/
|
||||
export function withLogCollector(
|
||||
logsToCollect: LogFuncs[] | LogCollector,
|
||||
callback?: LogCollector,
|
||||
|
||||
@@ -62,7 +62,7 @@ export type AnalyzeLocationExistingEntity = {
|
||||
* enough info for the frontend to know what form data to show to the user
|
||||
* for overriding/completing the info.
|
||||
* @public
|
||||
* */
|
||||
*/
|
||||
export type AnalyzeLocationGenerateEntity = {
|
||||
// Some form of partial representation of the entity
|
||||
entity: RecursivePartial<Entity>;
|
||||
|
||||
@@ -72,17 +72,17 @@ export class ProcessorOutputCollector {
|
||||
|
||||
if (i.type === 'entity') {
|
||||
let entity: Entity;
|
||||
const location = stringifyLocationRef(i.location);
|
||||
|
||||
try {
|
||||
entity = validateEntityEnvelope(i.entity);
|
||||
} catch (e) {
|
||||
assertError(e);
|
||||
this.logger.debug(`Envelope validation failed at ${i.location}, ${e}`);
|
||||
this.logger.debug(`Envelope validation failed at ${location}, ${e}`);
|
||||
this.errors.push(e);
|
||||
return;
|
||||
}
|
||||
|
||||
const location = stringifyLocationRef(i.location);
|
||||
|
||||
// Note that at this point, we have only validated the envelope part of
|
||||
// the entity data. Annotations are not part of that, so we have to be
|
||||
// defensive. If the annotations were malformed (e.g. were not a valid
|
||||
|
||||
@@ -48,7 +48,7 @@ export type EntityProcessingResult =
|
||||
/**
|
||||
* Responsible for executing the individual processing steps in order to fully process an entity.
|
||||
* @public
|
||||
* */
|
||||
*/
|
||||
export interface CatalogProcessingOrchestrator {
|
||||
process(request: EntityProcessingRequest): Promise<EntityProcessingResult>;
|
||||
}
|
||||
@@ -56,7 +56,7 @@ export interface CatalogProcessingOrchestrator {
|
||||
/**
|
||||
* Entities that are not yet processed.
|
||||
* @public
|
||||
* */
|
||||
*/
|
||||
export type DeferredEntity = {
|
||||
entity: Entity;
|
||||
locationKey?: string;
|
||||
|
||||
@@ -77,7 +77,8 @@ export interface RefreshService {
|
||||
|
||||
/**
|
||||
* Interacts with the database to manage locations.
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export interface LocationStore {
|
||||
createLocation(location: LocationInput): Promise<Location>;
|
||||
listLocations(): Promise<Location[]>;
|
||||
|
||||
@@ -12,7 +12,6 @@ import { ComponentEntity } from '@backstage/catalog-model';
|
||||
import { ComponentProps } from 'react';
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { GetEntitiesResponse } from '@backstage/catalog-client';
|
||||
import { IconButton } from '@material-ui/core';
|
||||
import { LinkProps } from '@backstage/core-components';
|
||||
import { Observable } from '@backstage/types';
|
||||
@@ -386,15 +385,6 @@ export interface EntityTypePickerProps {
|
||||
initialFilter?: string;
|
||||
}
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export type EntityTypeReturn = {
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
availableTypes: string[];
|
||||
selectedTypes: string[];
|
||||
setSelectedTypes: (types: string[]) => void;
|
||||
};
|
||||
|
||||
// @public
|
||||
export const FavoriteEntity: (props: FavoriteEntityProps) => JSX.Element;
|
||||
|
||||
@@ -510,9 +500,6 @@ export function useEntity<TEntity extends Entity = Entity>(): {
|
||||
refresh?: VoidFunction;
|
||||
};
|
||||
|
||||
// @public @deprecated (undocumented)
|
||||
export const useEntityFromUrl: () => EntityLoadingStatus;
|
||||
|
||||
// @public @deprecated
|
||||
export function useEntityKinds(): {
|
||||
error: Error | undefined;
|
||||
@@ -547,12 +534,6 @@ export function useEntityTypeFilter(): {
|
||||
setSelectedTypes: (types: string[]) => void;
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export function useOwnedEntities(allowedKinds?: string[]): {
|
||||
loading: boolean;
|
||||
ownedEntities: GetEntitiesResponse | undefined;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export function useRelatedEntities(
|
||||
entity: Entity,
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
export {
|
||||
useEntity,
|
||||
useEntityFromUrl,
|
||||
EntityProvider,
|
||||
AsyncEntityProvider,
|
||||
useAsyncEntity,
|
||||
@@ -35,11 +34,9 @@ export type {
|
||||
EntityListContextProps,
|
||||
} from './useEntityListProvider';
|
||||
export { useEntityTypeFilter } from './useEntityTypeFilter';
|
||||
export type { EntityTypeReturn } from './useEntityTypeFilter';
|
||||
export { useEntityKinds } from './useEntityKinds';
|
||||
export { useRelatedEntities } from './useRelatedEntities';
|
||||
export { useStarredEntities } from './useStarredEntities';
|
||||
export { useStarredEntity } from './useStarredEntity';
|
||||
export { loadCatalogOwnerRefs, useEntityOwnership } from './useEntityOwnership';
|
||||
export { useOwnedEntities } from './useOwnedEntities';
|
||||
export { useEntityPermission } from './useEntityPermission';
|
||||
|
||||
@@ -14,21 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import {
|
||||
errorApiRef,
|
||||
useApi,
|
||||
useRouteRefParams,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
createVersionedContext,
|
||||
createVersionedValueMap,
|
||||
useVersionedContext,
|
||||
} from '@backstage/version-bridge';
|
||||
import React, { ReactNode, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router';
|
||||
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
|
||||
import { catalogApiRef } from '../api';
|
||||
import { entityRouteRef } from '../routes';
|
||||
import React, { ReactNode } from 'react';
|
||||
|
||||
/** @public */
|
||||
export type EntityLoadingStatus<TEntity extends Entity = Entity> = {
|
||||
@@ -104,35 +95,6 @@ export const EntityProvider = ({ entity, children }: EntityProviderProps) => (
|
||||
/>
|
||||
);
|
||||
|
||||
/** @public
|
||||
* @deprecated will be deleted shortly due to low external usage, re-implement if needed.
|
||||
*/
|
||||
export const useEntityFromUrl = (): EntityLoadingStatus => {
|
||||
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
|
||||
const navigate = useNavigate();
|
||||
const errorApi = useApi(errorApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
const {
|
||||
value: entity,
|
||||
error,
|
||||
loading,
|
||||
retry: refresh,
|
||||
} = useAsyncRetry(
|
||||
() => catalogApi.getEntityByRef({ kind, namespace, name }),
|
||||
[catalogApi, kind, namespace, name],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!name) {
|
||||
errorApi.post(new Error('No name provided!'));
|
||||
navigate('/');
|
||||
}
|
||||
}, [errorApi, navigate, error, loading, entity, name]);
|
||||
|
||||
return { entity, loading, error, refresh };
|
||||
};
|
||||
|
||||
/**
|
||||
* Grab the current entity from the context, throws if the entity has not yet been loaded
|
||||
* or is not available.
|
||||
|
||||
@@ -23,17 +23,6 @@ import { catalogApiRef } from '../api';
|
||||
import { useEntityList } from './useEntityListProvider';
|
||||
import { EntityTypeFilter } from '../filters';
|
||||
|
||||
/** @public
|
||||
* @deprecated type inlined with {@link useEntityTypeFilter}.
|
||||
*/
|
||||
export type EntityTypeReturn = {
|
||||
loading: boolean;
|
||||
error?: Error;
|
||||
availableTypes: string[];
|
||||
selectedTypes: string[];
|
||||
setSelectedTypes: (types: string[]) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook built on top of `useEntityList` for enabling selection of valid `spec.type` values
|
||||
* based on the selected EntityKindFilter.
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* 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 { catalogApiRef } from './../api';
|
||||
import { loadCatalogOwnerRefs } from './useEntityOwnership';
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import { GetEntitiesResponse } from '@backstage/catalog-client';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
/**
|
||||
* Takes the relevant parts of the Backstage identity, and translates them into
|
||||
* a list of entities which are owned by the user. Takes an optional parameter
|
||||
* to filter the entities based on allowedKinds
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @param allowedKinds - Array of allowed kinds to filter the entities
|
||||
* @deprecated Use `ownershipEntityRefs` from `identityApi.getBackstageIdentity()` instead.
|
||||
*/
|
||||
export function useOwnedEntities(allowedKinds?: string[]): {
|
||||
loading: boolean;
|
||||
ownedEntities: GetEntitiesResponse | undefined;
|
||||
} {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
const { loading, value: refs } = useAsync(async () => {
|
||||
const identity = await identityApi.getBackstageIdentity();
|
||||
const identityRefs = identity.ownershipEntityRefs;
|
||||
const catalogRefs = await loadCatalogOwnerRefs(catalogApi, identityRefs);
|
||||
const catalogs = await catalogApi.getEntities(
|
||||
allowedKinds
|
||||
? {
|
||||
filter: {
|
||||
kind: allowedKinds,
|
||||
[`relations.${RELATION_OWNED_BY}`]:
|
||||
[...identityRefs, ...catalogRefs] || [],
|
||||
},
|
||||
}
|
||||
: {
|
||||
filter: {
|
||||
[`relations.${RELATION_OWNED_BY}`]:
|
||||
[...identityRefs, ...catalogRefs] || [],
|
||||
},
|
||||
},
|
||||
);
|
||||
return catalogs;
|
||||
}, []);
|
||||
|
||||
const ownedEntities = useMemo(() => {
|
||||
return refs;
|
||||
}, [refs]);
|
||||
|
||||
return useMemo(() => ({ loading, ownedEntities }), [loading, ownedEntities]);
|
||||
}
|
||||
@@ -9,8 +9,6 @@ import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { CatalogProcessor } from '@backstage/plugin-catalog-backend';
|
||||
import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ContainerRunner } from '@backstage/backend-common';
|
||||
import { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter';
|
||||
import { createPullRequest } from 'octokit-plugin-create-pull-request';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import express from 'express';
|
||||
@@ -21,7 +19,6 @@ import { Knex } from 'knex';
|
||||
import { LocationSpec } from '@backstage/plugin-catalog-backend';
|
||||
import { Logger as Logger_2 } from 'winston';
|
||||
import { Observable } from '@backstage/types';
|
||||
import { Octokit } from 'octokit';
|
||||
import { PluginDatabaseManager } from '@backstage/backend-common';
|
||||
import { Schema } from 'jsonschema';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
@@ -63,8 +60,6 @@ export interface CreateBuiltInActionsOptions {
|
||||
catalogClient: CatalogApi;
|
||||
// (undocumented)
|
||||
config: Config;
|
||||
// @deprecated (undocumented)
|
||||
containerRunner?: ContainerRunner;
|
||||
// (undocumented)
|
||||
integrations: ScmIntegrations;
|
||||
// (undocumented)
|
||||
@@ -99,8 +94,6 @@ export function createDebugLogAction(): TemplateAction<{
|
||||
listWorkspace?: boolean | undefined;
|
||||
}>;
|
||||
|
||||
export { createFetchCookiecutterAction };
|
||||
|
||||
// @public
|
||||
export function createFetchPlainAction(options: {
|
||||
reader: UrlReader;
|
||||
@@ -388,25 +381,6 @@ export function fetchContents({
|
||||
outputPath: string;
|
||||
}): Promise<void>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "OctokitProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public @deprecated
|
||||
export class OctokitProvider {
|
||||
constructor(
|
||||
integrations: ScmIntegrationRegistry,
|
||||
githubCredentialsProvider?: GithubCredentialsProvider,
|
||||
);
|
||||
// Warning: (ae-forgotten-export) The symbol "OctokitIntegration" needs to be exported by the entry point index.d.ts
|
||||
//
|
||||
// @deprecated
|
||||
getOctokit(
|
||||
repoUrl: string,
|
||||
options?: {
|
||||
token?: string;
|
||||
},
|
||||
): Promise<OctokitIntegration>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface OctokitWithPullRequestPluginClient {
|
||||
// (undocumented)
|
||||
@@ -428,8 +402,6 @@ export interface RouterOptions {
|
||||
// (undocumented)
|
||||
config: Config;
|
||||
// (undocumented)
|
||||
containerRunner?: ContainerRunner;
|
||||
// (undocumented)
|
||||
database: PluginDatabaseManager;
|
||||
// (undocumented)
|
||||
logger: Logger_2;
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
"@backstage/integration": "^0.8.0",
|
||||
"@backstage/plugin-catalog-backend": "^0.23.0",
|
||||
"@backstage/plugin-scaffolder-common": "^0.2.3",
|
||||
"@backstage/plugin-scaffolder-backend-module-cookiecutter": "^0.2.3",
|
||||
"@backstage/types": "^0.1.3",
|
||||
"@gitbeaker/core": "^34.6.0",
|
||||
"@gitbeaker/node": "^35.1.0",
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ContainerRunner, UrlReader } from '@backstage/backend-common';
|
||||
import { UrlReader } from '@backstage/backend-common';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import {
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
|
||||
import { createDebugLogAction } from './debug';
|
||||
import { createFetchPlainAction, createFetchTemplateAction } from './fetch';
|
||||
import { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter';
|
||||
import {
|
||||
createFilesystemDeleteAction,
|
||||
createFilesystemRenameAction,
|
||||
@@ -49,7 +48,6 @@ import {
|
||||
} from './github';
|
||||
import { TemplateFilter } from '../../../lib';
|
||||
import { TemplateAction } from '../types';
|
||||
import { getRootLogger } from '@backstage/backend-common';
|
||||
|
||||
/**
|
||||
* The options passed to {@link createBuiltinActions}
|
||||
@@ -59,8 +57,6 @@ export interface CreateBuiltInActionsOptions {
|
||||
reader: UrlReader;
|
||||
integrations: ScmIntegrations;
|
||||
catalogClient: CatalogApi;
|
||||
/** @deprecated when the cookiecutter action is removed this won't be necessary */
|
||||
containerRunner?: ContainerRunner;
|
||||
config: Config;
|
||||
additionalTemplateFilters?: Record<string, TemplateFilter>;
|
||||
}
|
||||
@@ -78,7 +74,6 @@ export const createBuiltinActions = (
|
||||
const {
|
||||
reader,
|
||||
integrations,
|
||||
containerRunner,
|
||||
catalogClient,
|
||||
config,
|
||||
additionalTemplateFilters,
|
||||
@@ -135,20 +130,5 @@ export const createBuiltinActions = (
|
||||
}),
|
||||
];
|
||||
|
||||
if (containerRunner) {
|
||||
getRootLogger().warn(
|
||||
`[DEPRECATED] The fetch:cookiecutter action will be removed part of the default scaffolder actions in later versions.
|
||||
You can install the package seperately and remove the containerRunner from the createBuiltInActions to remove this warning,
|
||||
or you can migrate to using fetch:template https://backstage.io/docs/features/software-templates/builtin-actions#migrating-from-fetchcookiecutter-to-fetchtemplate`,
|
||||
);
|
||||
actions.push(
|
||||
createFetchCookiecutterAction({
|
||||
reader,
|
||||
integrations,
|
||||
containerRunner,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return actions as TemplateAction<JsonObject>[];
|
||||
};
|
||||
|
||||
-88
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* 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 { OctokitProvider } from './OctokitProvider';
|
||||
import {
|
||||
ScmIntegrations,
|
||||
DefaultGithubCredentialsProvider,
|
||||
} from '@backstage/integration';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
|
||||
describe('getOctokit', () => {
|
||||
const config = new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{ host: 'github.com', token: 'tokenlols' },
|
||||
{ host: 'ghe.github.com' },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
const integrations = ScmIntegrations.fromConfig(config);
|
||||
const githubCredentialsProvider =
|
||||
DefaultGithubCredentialsProvider.fromIntegrations(integrations);
|
||||
const octokitProvider = new OctokitProvider(
|
||||
integrations,
|
||||
githubCredentialsProvider,
|
||||
);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should throw an error when the repoUrl is not well formed', async () => {
|
||||
await expect(
|
||||
octokitProvider.getOctokit('github.com?repo=bob'),
|
||||
).rejects.toThrow(/missing owner/);
|
||||
|
||||
await expect(
|
||||
octokitProvider.getOctokit('github.com?owner=owner'),
|
||||
).rejects.toThrow(/missing repo/);
|
||||
});
|
||||
|
||||
it('should throw if there is no integration config provided', async () => {
|
||||
await expect(
|
||||
octokitProvider.getOctokit('missing.com?repo=bob&owner=owner'),
|
||||
).rejects.toThrow(/No matching integration configuration/);
|
||||
});
|
||||
|
||||
it('should throw if there is no token in the integration config that is returned', async () => {
|
||||
await expect(
|
||||
octokitProvider.getOctokit('ghe.github.com?repo=bob&owner=owner'),
|
||||
).rejects.toThrow(/No token available for host/);
|
||||
});
|
||||
|
||||
it('should return proper Octokit', async () => {
|
||||
const { client, token, owner, repo } = await octokitProvider.getOctokit(
|
||||
'github.com?repo=bob&owner=owner',
|
||||
);
|
||||
expect(client).toBeDefined();
|
||||
expect(token).toBe('tokenlols');
|
||||
expect(owner).toBe('owner');
|
||||
expect(repo).toBe('bob');
|
||||
});
|
||||
|
||||
it('should return an octokit client with the passed in token if it is provided', async () => {
|
||||
const { client, token, owner, repo } = await octokitProvider.getOctokit(
|
||||
'github.com?repo=bob&owner=owner',
|
||||
{ token: 'tokenlols2' },
|
||||
);
|
||||
expect(client).toBeDefined();
|
||||
expect(token).toBe('tokenlols2');
|
||||
expect(owner).toBe('owner');
|
||||
expect(repo).toBe('bob');
|
||||
});
|
||||
});
|
||||
@@ -1,111 +0,0 @@
|
||||
/*
|
||||
* 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 { InputError } from '@backstage/errors';
|
||||
import {
|
||||
DefaultGithubCredentialsProvider,
|
||||
GithubCredentialsProvider,
|
||||
ScmIntegrationRegistry,
|
||||
} from '@backstage/integration';
|
||||
import { Octokit } from 'octokit';
|
||||
import { parseRepoUrl } from '../publish/util';
|
||||
|
||||
export type OctokitIntegration = {
|
||||
client: Octokit;
|
||||
token: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
};
|
||||
/**
|
||||
* OctokitProvider provides Octokit client based on ScmIntegrationsRegistry configuration.
|
||||
* OctokitProvider supports GitHub credentials caching out of the box.
|
||||
*
|
||||
* @deprecated we are no longer providing a way from the scaffolder to generate octokit instances.
|
||||
* Implement your own if you're using this method from an external package, or use the internal `getOctokitOptions` function instead
|
||||
*/
|
||||
export class OctokitProvider {
|
||||
private readonly integrations: ScmIntegrationRegistry;
|
||||
private readonly githubCredentialsProvider: GithubCredentialsProvider;
|
||||
|
||||
constructor(
|
||||
integrations: ScmIntegrationRegistry,
|
||||
githubCredentialsProvider?: GithubCredentialsProvider,
|
||||
) {
|
||||
this.integrations = integrations;
|
||||
this.githubCredentialsProvider =
|
||||
githubCredentialsProvider ||
|
||||
DefaultGithubCredentialsProvider.fromIntegrations(this.integrations);
|
||||
}
|
||||
|
||||
/**
|
||||
* gets standard Octokit client based on repository URL.
|
||||
*
|
||||
* @param repoUrl - Repository URL
|
||||
*
|
||||
* @deprecated we are no longer providing a way from the scaffolder to generate octokit instances.
|
||||
* Implement your own if you're using this method from an external package, or use the internal `getOctokitOptions` function instead
|
||||
*/
|
||||
async getOctokit(
|
||||
repoUrl: string,
|
||||
options?: { token?: string },
|
||||
): Promise<OctokitIntegration> {
|
||||
const { owner, repo, host } = parseRepoUrl(repoUrl, this.integrations);
|
||||
|
||||
if (!owner) {
|
||||
throw new InputError(`No owner provided for repo ${repoUrl}`);
|
||||
}
|
||||
|
||||
const integrationConfig = this.integrations.github.byHost(host)?.config;
|
||||
|
||||
if (!integrationConfig) {
|
||||
throw new InputError(`No integration for host ${host}`);
|
||||
}
|
||||
|
||||
// Short circuit the internal Github Token provider the token provided
|
||||
// by the action or the caller.
|
||||
if (options?.token) {
|
||||
const client = new Octokit({
|
||||
auth: options.token,
|
||||
baseUrl: integrationConfig.apiBaseUrl,
|
||||
previews: ['nebula-preview'],
|
||||
});
|
||||
|
||||
return { client, token: options.token, owner, repo };
|
||||
}
|
||||
|
||||
// TODO(blam): Consider changing this API to have owner, repoo interface instead of URL as the it's
|
||||
// needless to create URL and then parse again the other side.
|
||||
const { token } = await this.githubCredentialsProvider.getCredentials({
|
||||
url: `https://${host}/${encodeURIComponent(owner)}/${encodeURIComponent(
|
||||
repo,
|
||||
)}`,
|
||||
});
|
||||
|
||||
if (!token) {
|
||||
throw new InputError(
|
||||
`No token available for host: ${host}, with owner ${owner}, and repo ${repo}`,
|
||||
);
|
||||
}
|
||||
|
||||
const client = new Octokit({
|
||||
auth: token,
|
||||
baseUrl: integrationConfig.apiBaseUrl,
|
||||
previews: ['nebula-preview'],
|
||||
});
|
||||
|
||||
return { client, token, owner, repo };
|
||||
}
|
||||
}
|
||||
@@ -16,4 +16,3 @@
|
||||
|
||||
export { createGithubActionsDispatchAction } from './githubActionsDispatch';
|
||||
export { createGithubWebhookAction } from './githubWebhook';
|
||||
export { OctokitProvider } from './OctokitProvider';
|
||||
|
||||
@@ -22,8 +22,5 @@ export * from './filesystem';
|
||||
export * from './publish';
|
||||
export * from './github';
|
||||
|
||||
/** @deprecated please add this package to your own installation manually */
|
||||
export { createFetchCookiecutterAction } from '@backstage/plugin-scaffolder-backend-module-cookiecutter';
|
||||
|
||||
export { runCommand, executeShellCommand } from './helpers';
|
||||
export type { RunCommandOptions } from './helpers';
|
||||
|
||||
@@ -26,7 +26,8 @@ import {
|
||||
|
||||
/**
|
||||
* ActionContext is passed into scaffolder actions.
|
||||
* @public */
|
||||
* @public
|
||||
*/
|
||||
export type ActionContext<Input extends JsonObject> = {
|
||||
/**
|
||||
* Base URL for the location of the task spec, typically the url of the source entity file.
|
||||
|
||||
@@ -30,7 +30,6 @@ jest.doMock('fs-extra', () => ({
|
||||
|
||||
import {
|
||||
DatabaseManager,
|
||||
DockerContainerRunner,
|
||||
getVoidLogger,
|
||||
PluginDatabaseManager,
|
||||
UrlReaders,
|
||||
@@ -122,7 +121,6 @@ describe('createRouter', () => {
|
||||
config: new ConfigReader({}),
|
||||
database: createDatabase(),
|
||||
catalogClient: createCatalogClient(template),
|
||||
containerRunner: new DockerContainerRunner({} as any),
|
||||
reader: mockUrlReader,
|
||||
taskBroker,
|
||||
});
|
||||
|
||||
@@ -14,11 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
ContainerRunner,
|
||||
PluginDatabaseManager,
|
||||
UrlReader,
|
||||
} from '@backstage/backend-common';
|
||||
import { PluginDatabaseManager, UrlReader } from '@backstage/backend-common';
|
||||
import { CatalogApi } from '@backstage/catalog-client';
|
||||
import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
@@ -61,7 +57,6 @@ export interface RouterOptions {
|
||||
catalogClient: CatalogApi;
|
||||
actions?: TemplateAction<any>[];
|
||||
taskWorkers?: number;
|
||||
containerRunner?: ContainerRunner;
|
||||
taskBroker?: TaskBroker;
|
||||
additionalTemplateFilters?: Record<string, TemplateFilter>;
|
||||
}
|
||||
@@ -89,7 +84,6 @@ export async function createRouter(
|
||||
database,
|
||||
catalogClient,
|
||||
actions,
|
||||
containerRunner,
|
||||
taskWorkers,
|
||||
additionalTemplateFilters,
|
||||
} = options;
|
||||
@@ -128,7 +122,6 @@ export async function createRouter(
|
||||
: createBuiltinActions({
|
||||
integrations,
|
||||
catalogClient,
|
||||
containerRunner,
|
||||
reader,
|
||||
config,
|
||||
additionalTemplateFilters,
|
||||
|
||||
@@ -13,14 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { GetEntitiesResponse } from '@backstage/catalog-client';
|
||||
import { RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import { identityApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
catalogApiRef,
|
||||
humanizeEntityRef,
|
||||
useOwnedEntities,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { TextField } from '@material-ui/core';
|
||||
import FormControl from '@material-ui/core/FormControl';
|
||||
import Autocomplete from '@material-ui/lab/Autocomplete';
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
import { FieldExtensionComponentProps } from '../../../extensions';
|
||||
|
||||
@@ -86,3 +90,45 @@ export const OwnedEntityPicker = (
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Takes the relevant parts of the Backstage identity, and translates them into
|
||||
* a list of entities which are owned by the user. Takes an optional parameter
|
||||
* to filter the entities based on allowedKinds
|
||||
*
|
||||
*
|
||||
* @param allowedKinds - Array of allowed kinds to filter the entities
|
||||
*/
|
||||
function useOwnedEntities(allowedKinds?: string[]): {
|
||||
loading: boolean;
|
||||
ownedEntities: GetEntitiesResponse | undefined;
|
||||
} {
|
||||
const identityApi = useApi(identityApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
const { loading, value: refs } = useAsync(async () => {
|
||||
const identity = await identityApi.getBackstageIdentity();
|
||||
const identityRefs = identity.ownershipEntityRefs;
|
||||
const catalogs = await catalogApi.getEntities(
|
||||
allowedKinds
|
||||
? {
|
||||
filter: {
|
||||
kind: allowedKinds,
|
||||
[`relations.${RELATION_OWNED_BY}`]: identityRefs || [],
|
||||
},
|
||||
}
|
||||
: {
|
||||
filter: {
|
||||
[`relations.${RELATION_OWNED_BY}`]: identityRefs || [],
|
||||
},
|
||||
},
|
||||
);
|
||||
return catalogs;
|
||||
}, []);
|
||||
|
||||
const ownedEntities = useMemo(() => {
|
||||
return refs;
|
||||
}, [refs]);
|
||||
|
||||
return useMemo(() => ({ loading, ownedEntities }), [loading, ownedEntities]);
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { DateTime, DurationLike } from 'luxon';
|
||||
import { Config } from '@backstage/config';
|
||||
import { PluginEndpointDiscovery } from '@backstage/backend-common';
|
||||
@@ -62,11 +63,10 @@ export type TechInsightFact = {
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
* Response type used when returning from database and API.
|
||||
* Adds a field for ref for easier usage
|
||||
*
|
||||
* @public
|
||||
* @public
|
||||
*/
|
||||
export type FlatTechInsightFact = TechInsightFact & {
|
||||
/**
|
||||
@@ -76,12 +76,12 @@ export type FlatTechInsightFact = TechInsightFact & {
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* A record type to specify individual fact shapes
|
||||
*
|
||||
* Used as part of a schema to validate, identify and generically construct usage implementations
|
||||
* of individual fact values in the system.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FactSchema = {
|
||||
/**
|
||||
@@ -142,11 +142,9 @@ export type FactRetrieverContext = {
|
||||
};
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* FactRetriever interface
|
||||
*
|
||||
* A component specifying
|
||||
* @public
|
||||
*/
|
||||
export interface FactRetriever {
|
||||
/**
|
||||
@@ -191,10 +189,9 @@ export interface FactRetriever {
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* A Luxon duration like object for time to live value
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
* \{ timeToLive: 1209600000 \}
|
||||
* \{ timeToLive: \{ weeks: 4 \} \}
|
||||
@@ -203,10 +200,9 @@ export interface FactRetriever {
|
||||
export type TTL = { timeToLive: DurationLike };
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* A maximum number for items to be kept in the database for each fact retriever/entity pair
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
* \{ maxItems: 10 \}
|
||||
*
|
||||
@@ -214,25 +210,25 @@ export type TTL = { timeToLive: DurationLike };
|
||||
export type MaxItems = { maxItems: number };
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* A fact lifecycle definition. Determines which strategy to use to purge expired facts from the database.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FactLifecycle = TTL | MaxItems;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* A flat serializable structure for Facts.
|
||||
* Containing information about fact schema, version, id, and entity filters
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FactSchemaDefinition = Omit<FactRetriever, 'handler'>;
|
||||
|
||||
/**
|
||||
* @public
|
||||
*
|
||||
* Registration of a fact retriever
|
||||
* Used to add and schedule individual fact retrievers to the fact retriever engine.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type FactRetrieverRegistration = {
|
||||
/**
|
||||
|
||||
@@ -347,8 +347,8 @@ export function createEventStream(
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated use event-stream implementation of the sync endpoint
|
||||
* */
|
||||
* @deprecated use event-stream implementation of the sync endpoint
|
||||
*/
|
||||
export function createHttpResponse(
|
||||
res: Response<any, any>,
|
||||
): DocsSynchronizerSyncOpts {
|
||||
|
||||
@@ -22048,11 +22048,6 @@ rollup-plugin-esbuild@^4.7.2:
|
||||
joycon "^3.0.1"
|
||||
jsonc-parser "^3.0.0"
|
||||
|
||||
rollup-plugin-peer-deps-external@^2.2.2:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.npmjs.org/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.4.tgz#8a420bbfd6dccc30aeb68c9bf57011f2f109570d"
|
||||
integrity sha512-AWdukIM1+k5JDdAqV/Cxd+nejvno2FVLVeZ74NKggm3Q5s9cbbcOgUPGdbxPi4BXu7xGaZ8HG12F+thImYu/0g==
|
||||
|
||||
rollup-plugin-postcss@*, rollup-plugin-postcss@^4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.npmjs.org/rollup-plugin-postcss/-/rollup-plugin-postcss-4.0.2.tgz#15e9462f39475059b368ce0e49c800fa4b1f7050"
|
||||
|
||||
Reference in New Issue
Block a user