Merge remote-tracking branch 'origin/master' into orkohunter/techdocs-publish-to-cloud-storage
This commit is contained in:
@@ -1,13 +0,0 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Optimized the `yarn install` step in the backend `Dockerfile`.
|
||||
|
||||
To apply these changes to an existing app, make the following changes to `packages/backend/Dockerfile`:
|
||||
|
||||
Replace the `RUN yarn install ...` line with the following:
|
||||
|
||||
```bash
|
||||
RUN yarn install --frozen-lockfile --production --network-timeout 300000 && rm -rf "$(yarn cache dir)"
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-api-docs': patch
|
||||
---
|
||||
|
||||
Update swagger-ui-react to 3.37.2
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
---
|
||||
|
||||
Add breakdown view to the Cost Overview panel
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
---
|
||||
|
||||
Update URL auth format for Gitlab clone
|
||||
@@ -0,0 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-circleci': patch
|
||||
'@backstage/plugin-jenkins': patch
|
||||
---
|
||||
|
||||
Refactor to support ADR004 module exporting.
|
||||
|
||||
For more information, see https://backstage.io/docs/architecture-decisions/adrs-adr004.
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-sentry': patch
|
||||
'@backstage/plugin-welcome': patch
|
||||
---
|
||||
|
||||
Refactor route registration to remove deprecating code
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
'@backstage/config-loader': minor
|
||||
---
|
||||
|
||||
Fix typo of "visibility" in config schema reference
|
||||
|
||||
If you have defined a config element named `visiblity`, you
|
||||
will need to fix the spelling to `visibility`. For more info,
|
||||
see https://backstage.io/docs/conf/defining#visibility.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Allow the `backend.listen.port` config to be both a number or a string.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Bump versions of `esbuild` and `rollup-plugin-esbuild`
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-proxy-backend': patch
|
||||
---
|
||||
|
||||
Filter the headers that are sent from the proxied-targed back to the frontend to not forwarded unwanted authentication or
|
||||
monitoring contexts from other origins (like `Set-Cookie` with e.g. a google analytics context). The implementation reuses
|
||||
the `allowedHeaders` configuration that now controls both directions `frontend->target` and `target->frontend`.
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Removed `"resolutions"` entry for `esbuild` in the root `package.json` in order to use the version specified by `@backstage/cli`.
|
||||
|
||||
To apply this change to an existing app, remove the following from your root `package.json`:
|
||||
|
||||
```json
|
||||
"resolutions": {
|
||||
"esbuild": "0.6.3"
|
||||
},
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Add [API docs plugin](https://github.com/backstage/backstage/tree/master/plugins/api-docs) to new apps being created through the CLI.
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
'@backstage/plugin-catalog-import': minor
|
||||
'@backstage/catalog-model': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Add Analyze location endpoint to catalog backend. Add catalog-import plugin and replace import-component with it. To start using Analyze location endpoint, you have add it to the `createRouter` function options in the `\backstage\packages\backend\src\plugins\catalog.ts` file:
|
||||
|
||||
```ts
|
||||
export default async function createPlugin(env: PluginEnvironment) {
|
||||
const builder = new CatalogBuilder(env);
|
||||
const {
|
||||
entitiesCatalog,
|
||||
locationsCatalog,
|
||||
higherOrderOperation,
|
||||
locationAnalyzer, //<--
|
||||
} = await builder.build();
|
||||
|
||||
return await createRouter({
|
||||
entitiesCatalog,
|
||||
locationsCatalog,
|
||||
higherOrderOperation,
|
||||
locationAnalyzer, //<--
|
||||
logger: env.logger,
|
||||
});
|
||||
}
|
||||
```
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Gracefully handle missing codeowners.
|
||||
|
||||
The CodeOwnersProcessor now also takes a logger as a parameter.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
'@backstage/cli': minor
|
||||
'@backstage/plugin-cost-insights': patch
|
||||
---
|
||||
|
||||
sort product panels and navigation menu by greatest cost
|
||||
update tsconfig.json to use ES2020 api
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Use type EntityName from catalog-model for entities
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Use the OWNED_BY relation and compare it to the users MEMBER_OF relation. The user entity is searched by name, based on the userId of the identity.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-proxy-backend': patch
|
||||
---
|
||||
|
||||
Add configuration schema for the commonly used properties
|
||||
@@ -294,6 +294,9 @@ costInsights:
|
||||
bigQuery:
|
||||
name: BigQuery
|
||||
icon: search
|
||||
events:
|
||||
name: Events
|
||||
icon: data
|
||||
metrics:
|
||||
DAU:
|
||||
name: Daily Active Users
|
||||
|
||||
+1
-1
@@ -93,6 +93,6 @@ sign-in methods.
|
||||
More details are provided in dedicated sections of the documentation.
|
||||
|
||||
- [OAuth](./oauth.md): Description of the generic OAuth flow implemented by the
|
||||
[auth-backend](../../plugins/auth-backend).
|
||||
[auth-backend](https://github.com/backstage/backstage/tree/master/plugins/auth-backend).
|
||||
- [Glossary](./glossary.md): Glossary of some common terms related to the auth
|
||||
flows.
|
||||
|
||||
@@ -31,6 +31,7 @@ we recommend that you name them `catalog-info.yaml`.
|
||||
- [Kind: Resource](#kind-resource)
|
||||
- [Kind: System](#kind-system)
|
||||
- [Kind: Domain](#kind-domain)
|
||||
- [Kind: Location](#kind-location)
|
||||
|
||||
## Overall Shape Of An Entity
|
||||
|
||||
@@ -884,3 +885,58 @@ This kind is not yet defined, but is reserved [for future use](system-model.md).
|
||||
## Kind: Domain
|
||||
|
||||
This kind is not yet defined, but is reserved [for future use](system-model.md).
|
||||
|
||||
## Kind: Location
|
||||
|
||||
Describes the following entity kind:
|
||||
|
||||
| Field | Value |
|
||||
| ------------ | ----------------------- |
|
||||
| `apiVersion` | `backstage.io/v1alpha1` |
|
||||
| `kind` | `Location` |
|
||||
|
||||
A location is a marker that references other places to look for catalog data.
|
||||
|
||||
Descriptor files for this kind may look as follows.
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: org-data
|
||||
spec:
|
||||
type: url
|
||||
targets:
|
||||
- http://github.com/myorg/myproject/org-data-dump/catalog-info-staff.yaml
|
||||
- http://github.com/myorg/myproject/org-data-dump/catalog-info-consultants.yaml
|
||||
```
|
||||
|
||||
In addition to the [common envelope metadata](#common-to-all-kinds-the-metadata)
|
||||
shape, this kind has the following structure.
|
||||
|
||||
### `apiVersion` and `kind` [required]
|
||||
|
||||
Exactly equal to `backstage.io/v1alpha1` and `Location`, respectively.
|
||||
|
||||
### `spec.type` [optional]
|
||||
|
||||
The single location type, that's common to the targets specified in the spec. If
|
||||
it is left out, it is inherited from the location type that originally read the
|
||||
entity data. For example, if you have a `url` type location, that when read
|
||||
results in a `Location` kind entity with no `spec.type`, then the referenced
|
||||
targets in the entity will implicitly also be of `url` type. This is useful
|
||||
because you can define a hierarchy of things in a directory structure using
|
||||
relative target paths (see below), and it will work out no matter if it's
|
||||
consumed locally on disk from a `file` location, or as uploaded on a VCS.
|
||||
|
||||
### `spec.target` [optional]
|
||||
|
||||
A single target as a string. Can be either an absolute path/URL (depending on
|
||||
the type), or a relative path such as `./details/catalog-info.yaml` which is
|
||||
resolved relative to the location of this Location entity itself.
|
||||
|
||||
### `spec.targets` [optional]
|
||||
|
||||
A list of targets as strings. They can all be either absolute paths/URLs
|
||||
(depending on the type), or relative paths such as `./details/catalog-info.yaml`
|
||||
which are resolved relative to the location of this Location entity itself.
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
# example-app
|
||||
|
||||
## 0.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [7eb8bfe4a]
|
||||
- Updated dependencies [fe7257ff0]
|
||||
- Updated dependencies [a2cfa311a]
|
||||
- Updated dependencies [69f38457f]
|
||||
- Updated dependencies [bec334b33]
|
||||
- Updated dependencies [303c5ea17]
|
||||
- Updated dependencies [b4488ddb0]
|
||||
- Updated dependencies [4a655c89d]
|
||||
- Updated dependencies [08835a61d]
|
||||
- Updated dependencies [a9fd599f7]
|
||||
- Updated dependencies [8a16e8af8]
|
||||
- Updated dependencies [bcc211a08]
|
||||
- Updated dependencies [00670a96e]
|
||||
- Updated dependencies [da2ad65cb]
|
||||
- Updated dependencies [ebf37bbae]
|
||||
- @backstage/plugin-api-docs@0.3.1
|
||||
- @backstage/plugin-cost-insights@0.4.2
|
||||
- @backstage/plugin-sentry@0.2.4
|
||||
- @backstage/plugin-welcome@0.2.2
|
||||
- @backstage/cli@0.4.0
|
||||
- @backstage/catalog-model@0.4.0
|
||||
- @backstage/plugin-catalog-import@0.3.0
|
||||
- @backstage/plugin-scaffolder@0.3.2
|
||||
- @backstage/plugin-kubernetes@0.3.1
|
||||
- @backstage/plugin-techdocs@0.3.1
|
||||
- @backstage/plugin-catalog@0.2.5
|
||||
- @backstage/test-utils@0.1.4
|
||||
- @backstage/plugin-circleci@0.2.3
|
||||
- @backstage/plugin-cloudbuild@0.2.3
|
||||
- @backstage/plugin-github-actions@0.2.3
|
||||
- @backstage/plugin-jenkins@0.3.2
|
||||
- @backstage/plugin-lighthouse@0.2.4
|
||||
- @backstage/plugin-register-component@0.2.3
|
||||
- @backstage/plugin-rollbar@0.2.5
|
||||
- @backstage/plugin-search@0.2.2
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+21
-21
@@ -1,37 +1,37 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.5",
|
||||
"private": true,
|
||||
"bundled": true,
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.3.1",
|
||||
"@backstage/cli": "^0.3.2",
|
||||
"@backstage/catalog-model": "^0.4.0",
|
||||
"@backstage/cli": "^0.4.0",
|
||||
"@backstage/core": "^0.3.2",
|
||||
"@backstage/plugin-api-docs": "^0.3.0",
|
||||
"@backstage/plugin-catalog": "^0.2.4",
|
||||
"@backstage/plugin-catalog-import": "^0.2.0",
|
||||
"@backstage/plugin-circleci": "^0.2.2",
|
||||
"@backstage/plugin-cloudbuild": "^0.2.2",
|
||||
"@backstage/plugin-cost-insights": "^0.4.1",
|
||||
"@backstage/plugin-api-docs": "^0.3.1",
|
||||
"@backstage/plugin-catalog": "^0.2.5",
|
||||
"@backstage/plugin-catalog-import": "^0.3.0",
|
||||
"@backstage/plugin-circleci": "^0.2.3",
|
||||
"@backstage/plugin-cloudbuild": "^0.2.3",
|
||||
"@backstage/plugin-cost-insights": "^0.4.2",
|
||||
"@backstage/plugin-explore": "^0.2.1",
|
||||
"@backstage/plugin-gcp-projects": "^0.2.1",
|
||||
"@backstage/plugin-github-actions": "^0.2.2",
|
||||
"@backstage/plugin-github-actions": "^0.2.3",
|
||||
"@backstage/plugin-gitops-profiles": "^0.2.1",
|
||||
"@backstage/plugin-graphiql": "^0.2.1",
|
||||
"@backstage/plugin-jenkins": "^0.3.1",
|
||||
"@backstage/plugin-kubernetes": "^0.3.0",
|
||||
"@backstage/plugin-lighthouse": "^0.2.3",
|
||||
"@backstage/plugin-jenkins": "^0.3.2",
|
||||
"@backstage/plugin-kubernetes": "^0.3.1",
|
||||
"@backstage/plugin-lighthouse": "^0.2.4",
|
||||
"@backstage/plugin-newrelic": "^0.2.1",
|
||||
"@backstage/plugin-register-component": "^0.2.2",
|
||||
"@backstage/plugin-rollbar": "^0.2.4",
|
||||
"@backstage/plugin-scaffolder": "^0.3.1",
|
||||
"@backstage/plugin-sentry": "^0.2.3",
|
||||
"@backstage/plugin-search": "^0.2.1",
|
||||
"@backstage/plugin-register-component": "^0.2.3",
|
||||
"@backstage/plugin-rollbar": "^0.2.5",
|
||||
"@backstage/plugin-scaffolder": "^0.3.2",
|
||||
"@backstage/plugin-sentry": "^0.2.4",
|
||||
"@backstage/plugin-search": "^0.2.2",
|
||||
"@backstage/plugin-tech-radar": "^0.3.0",
|
||||
"@backstage/plugin-techdocs": "^0.3.0",
|
||||
"@backstage/plugin-techdocs": "^0.3.1",
|
||||
"@backstage/plugin-user-settings": "^0.2.2",
|
||||
"@backstage/plugin-welcome": "^0.2.1",
|
||||
"@backstage/test-utils": "^0.1.3",
|
||||
"@backstage/plugin-welcome": "^0.2.2",
|
||||
"@backstage/test-utils": "^0.1.4",
|
||||
"@backstage/theme": "^0.2.1",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/backend-common
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 612368274: Allow the `backend.listen.port` config to be both a number or a string.
|
||||
- Updated dependencies [4e7091759]
|
||||
- Updated dependencies [b4488ddb0]
|
||||
- @backstage/config-loader@0.4.0
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/backend-common",
|
||||
"description": "Common functionality library for Backstage backends",
|
||||
"version": "0.3.2",
|
||||
"version": "0.3.3",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -31,7 +31,7 @@
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "^0.1.1",
|
||||
"@backstage/config": "^0.1.1",
|
||||
"@backstage/config-loader": "^0.3.0",
|
||||
"@backstage/config-loader": "^0.4.0",
|
||||
"@backstage/integration": "^0.1.2",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.6",
|
||||
@@ -67,8 +67,8 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.3.2",
|
||||
"@backstage/test-utils": "^0.1.3",
|
||||
"@backstage/cli": "^0.4.0",
|
||||
"@backstage/test-utils": "^0.1.4",
|
||||
"@types/archiver": "^3.1.1",
|
||||
"@types/compression": "^1.7.0",
|
||||
"@types/concat-stream": "^1.6.0",
|
||||
|
||||
@@ -235,6 +235,40 @@ describe('GithubUrlReader', () => {
|
||||
expect(indexMarkdownFile.toString()).toBe('# Test\n');
|
||||
});
|
||||
|
||||
it('includes the subdomain in the github url', async () => {
|
||||
worker.resetHandlers();
|
||||
worker.use(
|
||||
rest.get(
|
||||
'https://ghe.github.com/backstage/mock/archive/repo.tar.gz',
|
||||
(_, res, ctx) =>
|
||||
res(
|
||||
ctx.status(200),
|
||||
ctx.set('Content-Type', 'application/x-gzip'),
|
||||
ctx.body(repoBuffer),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
const processor = new GithubUrlReader(
|
||||
{
|
||||
host: 'ghe.github.com',
|
||||
apiBaseUrl: 'https://api.github.com',
|
||||
},
|
||||
{ treeResponseFactory },
|
||||
);
|
||||
|
||||
const response = await processor.readTree(
|
||||
'https://ghe.github.com/backstage/mock/tree/repo/docs',
|
||||
);
|
||||
|
||||
const files = await response.files();
|
||||
|
||||
expect(files.length).toBe(1);
|
||||
const indexMarkdownFile = await files[0].content();
|
||||
|
||||
expect(indexMarkdownFile.toString()).toBe('# Test\n');
|
||||
});
|
||||
|
||||
it('must specify a branch', async () => {
|
||||
const processor = new GithubUrlReader(
|
||||
{
|
||||
|
||||
@@ -179,7 +179,7 @@ export class GithubUrlReader implements UrlReader {
|
||||
name: repoName,
|
||||
ref,
|
||||
protocol,
|
||||
source,
|
||||
resource,
|
||||
full_name,
|
||||
filepath,
|
||||
} = parseGitUri(url);
|
||||
@@ -194,7 +194,7 @@ export class GithubUrlReader implements UrlReader {
|
||||
// TODO(Rugvip): use API to fetch URL instead
|
||||
const response = await fetch(
|
||||
new URL(
|
||||
`${protocol}://${source}/${full_name}/archive/${ref}.tar.gz`,
|
||||
`${protocol}://${resource}/${full_name}/archive/${ref}.tar.gz`,
|
||||
).toString(),
|
||||
);
|
||||
if (!response.ok) {
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
# example-backend
|
||||
|
||||
## 0.2.5
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [ae95c7ff3]
|
||||
- Updated dependencies [b4488ddb0]
|
||||
- Updated dependencies [612368274]
|
||||
- Updated dependencies [6a6c7c14e]
|
||||
- Updated dependencies [08835a61d]
|
||||
- Updated dependencies [a9fd599f7]
|
||||
- Updated dependencies [e42402b47]
|
||||
- Updated dependencies [bcc211a08]
|
||||
- Updated dependencies [3619ea4c4]
|
||||
- @backstage/plugin-techdocs-backend@0.3.1
|
||||
- @backstage/plugin-catalog-backend@0.3.0
|
||||
- @backstage/backend-common@0.3.3
|
||||
- @backstage/plugin-proxy-backend@0.2.2
|
||||
- @backstage/catalog-model@0.4.0
|
||||
- @backstage/plugin-kubernetes-backend@0.2.1
|
||||
- @backstage/plugin-app-backend@0.3.2
|
||||
- example-app@0.2.5
|
||||
- @backstage/plugin-auth-backend@0.2.5
|
||||
- @backstage/plugin-scaffolder-backend@0.3.3
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.2.4",
|
||||
"version": "0.2.5",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"private": true,
|
||||
@@ -18,24 +18,24 @@
|
||||
"migrate:create": "knex migrate:make -x ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.3.2",
|
||||
"@backstage/catalog-model": "^0.3.1",
|
||||
"@backstage/backend-common": "^0.3.3",
|
||||
"@backstage/catalog-model": "^0.4.0",
|
||||
"@backstage/config": "^0.1.1",
|
||||
"@backstage/plugin-app-backend": "^0.3.1",
|
||||
"@backstage/plugin-auth-backend": "^0.2.4",
|
||||
"@backstage/plugin-catalog-backend": "^0.2.3",
|
||||
"@backstage/plugin-app-backend": "^0.3.2",
|
||||
"@backstage/plugin-auth-backend": "^0.2.5",
|
||||
"@backstage/plugin-catalog-backend": "^0.3.0",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.3",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.2.0",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.1",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.2.1",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.2",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.4",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.3.2",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.3.3",
|
||||
"@backstage/plugin-sentry-backend": "^0.1.3",
|
||||
"@backstage/plugin-techdocs-backend": "^0.3.0",
|
||||
"@backstage/plugin-techdocs-backend": "^0.3.1",
|
||||
"@gitbeaker/node": "^25.2.0",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"azure-devops-node-api": "^10.1.1",
|
||||
"dockerode": "^3.2.0",
|
||||
"example-app": "^0.2.4",
|
||||
"example-app": "^0.2.5",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"knex": "^0.21.6",
|
||||
@@ -45,7 +45,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.3.2",
|
||||
"@backstage/cli": "^0.4.0",
|
||||
"@types/dockerode": "^2.5.32",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/catalog-client
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies [08835a61d]
|
||||
- Updated dependencies [a9fd599f7]
|
||||
- Updated dependencies [bcc211a08]
|
||||
- @backstage/catalog-model@0.4.0
|
||||
|
||||
## 0.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/catalog-client",
|
||||
"version": "0.3.1",
|
||||
"version": "0.3.2",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,12 +20,12 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.3.0",
|
||||
"@backstage/catalog-model": "^0.4.0",
|
||||
"@backstage/config": "^0.1.1",
|
||||
"cross-fetch": "^3.0.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.3.1",
|
||||
"@backstage/cli": "^0.4.0",
|
||||
"@types/jest": "^26.0.7",
|
||||
"msw": "^0.21.2"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,36 @@
|
||||
# @backstage/catalog-model
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- bcc211a08: k8s-plugin: refactor approach to use annotation based label-selector
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 08835a61d: Add support for relative targets and implicit types in Location entities.
|
||||
- a9fd599f7: Add Analyze location endpoint to catalog backend. Add catalog-import plugin and replace import-component with it. To start using Analyze location endpoint, you have add it to the `createRouter` function options in the `\backstage\packages\backend\src\plugins\catalog.ts` file:
|
||||
|
||||
```ts
|
||||
export default async function createPlugin(env: PluginEnvironment) {
|
||||
const builder = new CatalogBuilder(env);
|
||||
const {
|
||||
entitiesCatalog,
|
||||
locationsCatalog,
|
||||
higherOrderOperation,
|
||||
locationAnalyzer, //<--
|
||||
} = await builder.build();
|
||||
|
||||
return await createRouter({
|
||||
entitiesCatalog,
|
||||
locationsCatalog,
|
||||
higherOrderOperation,
|
||||
locationAnalyzer, //<--
|
||||
logger: env.logger,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## 0.3.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
# Examples with relative paths
|
||||
|
||||
These examples use Location entities with relative paths
|
||||
[see #3513](https://github.com/backstage/backstage/pull/3513).
|
||||
|
||||
The `examples` sibling directory will be replaced with these contents no sooner
|
||||
than December 16 2020, to give consumers time to adopt the above change.
|
||||
|
||||
The contents of this directory is here for consumers who are on the edge and
|
||||
want to use the new facility before December 16.
|
||||
@@ -0,0 +1,8 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: acme-corp
|
||||
description: A collection of all Backstage example Groups
|
||||
spec:
|
||||
targets:
|
||||
- ./acme/org.yaml
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: backstage
|
||||
description: The backstage sub-department
|
||||
spec:
|
||||
type: sub-department
|
||||
parent: infrastructure
|
||||
ancestors: [infrastructure, acme-corp]
|
||||
children: [team-a, team-b]
|
||||
descendants: [team-a, team-b]
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: boxoffice
|
||||
description: The boxoffice sub-department
|
||||
spec:
|
||||
type: sub-department
|
||||
parent: infrastructure
|
||||
ancestors: [infrastructure, acme-corp]
|
||||
children: [team-c, team-d]
|
||||
descendants: [team-c, team-d]
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: infrastructure
|
||||
description: The infra department
|
||||
spec:
|
||||
type: department
|
||||
parent: acme-corp
|
||||
ancestors: [acme-corp]
|
||||
children: [backstage, boxoffice]
|
||||
descendants: [backstage, boxoffice, team-a, team-b, team-c, team-d]
|
||||
@@ -0,0 +1,26 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: acme-corp
|
||||
description: The acme-corp organization
|
||||
spec:
|
||||
type: organization
|
||||
ancestors: []
|
||||
children: [infrastructure]
|
||||
descendants:
|
||||
[infrastructure, backstage, boxoffice, team-a, team-b, team-c, team-d]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: example-groups
|
||||
description: A collection of all Backstage example Groups
|
||||
spec:
|
||||
targets:
|
||||
- ./infrastructure-group.yaml
|
||||
- ./boxoffice-group.yaml
|
||||
- ./backstage-group.yaml
|
||||
- ./team-a-group.yaml
|
||||
- ./team-b-group.yaml
|
||||
- ./team-c-group.yaml
|
||||
- ./team-d-group.yaml
|
||||
@@ -0,0 +1,58 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: team-a
|
||||
description: Team A
|
||||
spec:
|
||||
type: team
|
||||
parent: backstage
|
||||
ancestors: [backstage, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: breanna.davison
|
||||
spec:
|
||||
profile:
|
||||
displayName: Breanna Davison
|
||||
email: breanna-davison@example.com
|
||||
picture: https://example.com/staff/breanna.jpeg
|
||||
memberOf: [team-a]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: janelle.dawe
|
||||
spec:
|
||||
profile:
|
||||
displayName: Janelle Dawe
|
||||
email: janelle-dawe@example.com
|
||||
picture: https://example.com/staff/janelle.jpeg
|
||||
memberOf: [team-a]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: nigel.manning
|
||||
spec:
|
||||
profile:
|
||||
displayName: Nigel Manning
|
||||
email: nigel-manning@example.com
|
||||
picture: https://example.com/staff/nigel.jpeg
|
||||
memberOf: [team-a]
|
||||
---
|
||||
# This user is added as an example, to make it more easy for the "Guest"
|
||||
# sign-in option to demonstrate some entities being owned. In a regular org,
|
||||
# a guest user would probably not be registered like this.
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: guest
|
||||
spec:
|
||||
profile:
|
||||
displayName: Guest User
|
||||
email: guest@example.com
|
||||
picture: https://example.com/staff/the-ceos-dog.jpeg
|
||||
memberOf: [team-a]
|
||||
@@ -0,0 +1,66 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: team-b
|
||||
description: Team B
|
||||
spec:
|
||||
type: team
|
||||
parent: backstage
|
||||
ancestors: [backstage, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: amelia.park
|
||||
spec:
|
||||
profile:
|
||||
displayName: Amelia Park
|
||||
email: amelia-park@example.com
|
||||
picture: https://example.com/staff/amelia.jpeg
|
||||
memberOf: [team-b]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: colette.brock
|
||||
spec:
|
||||
profile:
|
||||
displayName: Colette Brock
|
||||
email: colette-brock@example.com
|
||||
picture: https://example.com/staff/colette.jpeg
|
||||
memberOf: [team-b]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: jenny.doe
|
||||
spec:
|
||||
profile:
|
||||
displayName: Jenny Doe
|
||||
email: jenny-doe@example.com
|
||||
picture: https://example.com/staff/jenny.jpeg
|
||||
memberOf: [team-b]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: jonathon.page
|
||||
spec:
|
||||
profile:
|
||||
displayName: Jonathon Page
|
||||
email: jonathon-page@example.com
|
||||
picture: https://example.com/staff/jonathon.jpeg
|
||||
memberOf: [team-b]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: justine.barrow
|
||||
spec:
|
||||
profile:
|
||||
displayName: Justine Barrow
|
||||
email: justine-barrow@example.com
|
||||
picture: https://example.com/staff/justine.jpeg
|
||||
memberOf: [team-b]
|
||||
@@ -0,0 +1,66 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: team-c
|
||||
description: Team C
|
||||
spec:
|
||||
type: team
|
||||
parent: boxoffice
|
||||
ancestors: [boxoffice, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: calum.leavy
|
||||
spec:
|
||||
profile:
|
||||
displayName: Calum Leavy
|
||||
email: calum-leavy@example.com
|
||||
picture: https://example.com/staff/calum.jpeg
|
||||
memberOf: [team-c]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: frank.tiernan
|
||||
spec:
|
||||
profile:
|
||||
displayName: Frank Tiernan
|
||||
email: frank-tiernan@example.com
|
||||
picture: https://example.com/staff/frank.jpeg
|
||||
memberOf: [team-c]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: peadar.macmahon
|
||||
spec:
|
||||
profile:
|
||||
displayName: Peadar MacMahon
|
||||
email: peadar-macmahon@example.com
|
||||
picture: https://example.com/staff/peadar.jpeg
|
||||
memberOf: [team-c]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: sarah.gilroy
|
||||
spec:
|
||||
profile:
|
||||
displayName: Sarah Gilroy
|
||||
email: sarah-gilroy@example.com
|
||||
picture: https://example.com/staff/sarah.jpeg
|
||||
memberOf: [team-c]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: tara.macgovern
|
||||
spec:
|
||||
profile:
|
||||
displayName: Tara MacGovern
|
||||
email: tara-macgovern@example.com
|
||||
picture: https://example.com/staff/tara.jpeg
|
||||
memberOf: [team-c]
|
||||
@@ -0,0 +1,33 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: team-d
|
||||
description: Team D
|
||||
spec:
|
||||
type: team
|
||||
parent: boxoffice
|
||||
ancestors: [boxoffice, infrastructure, acme-corp]
|
||||
children: []
|
||||
descendants: []
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: eva.macdowell
|
||||
spec:
|
||||
profile:
|
||||
displayName: Eva MacDowell
|
||||
email: eva-macdowell@example.com
|
||||
picture: https://example.com/staff/eva.jpeg
|
||||
memberOf: [team-d]
|
||||
---
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: lucy.sheehan
|
||||
spec:
|
||||
profile:
|
||||
displayName: Lucy Sheehan
|
||||
email: lucy-sheehan@example.com
|
||||
picture: https://example.com/staff/lucy.jpeg
|
||||
memberOf: [team-d]
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: example-apis
|
||||
description: A collection of all Backstage example APIs
|
||||
spec:
|
||||
targets:
|
||||
- ./apis/hello-world-api.yaml
|
||||
- ./apis/petstore-api.yaml
|
||||
- ./apis/spotify-api.yaml
|
||||
- ./apis/streetlights-api.yaml
|
||||
- ./apis/swapi-graphql.yaml
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Location
|
||||
metadata:
|
||||
name: example-components
|
||||
description: A collection of all Backstage example components
|
||||
spec:
|
||||
targets:
|
||||
- ./components/artist-lookup-component.yaml
|
||||
- ./components/petstore-component.yaml
|
||||
- ./components/playback-order-component.yaml
|
||||
- ./components/podcast-api-component.yaml
|
||||
- ./components/queue-proxy-component.yaml
|
||||
- ./components/searcher-component.yaml
|
||||
- ./components/playback-lib-component.yaml
|
||||
- ./components/www-artist-component.yaml
|
||||
- ./components/shuffle-api-component.yaml
|
||||
@@ -0,0 +1,48 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: API
|
||||
metadata:
|
||||
name: hello-world
|
||||
description: Hello World example for gRPC
|
||||
spec:
|
||||
type: grpc
|
||||
lifecycle: deprecated
|
||||
owner: team-c
|
||||
definition: |
|
||||
// Copyright 2015 gRPC 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.
|
||||
|
||||
syntax = "proto3";
|
||||
|
||||
option java_multiple_files = true;
|
||||
option java_package = "io.grpc.examples.helloworld";
|
||||
option java_outer_classname = "HelloWorldProto";
|
||||
option objc_class_prefix = "HLW";
|
||||
|
||||
package helloworld;
|
||||
|
||||
// The greeting service definition.
|
||||
service Greeter {
|
||||
// Sends a greeting
|
||||
rpc SayHello (HelloRequest) returns (HelloReply) {}
|
||||
}
|
||||
|
||||
// The request message containing the user's name.
|
||||
message HelloRequest {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
// The response message containing the greetings
|
||||
message HelloReply {
|
||||
string message = 1;
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: API
|
||||
metadata:
|
||||
name: petstore
|
||||
description: The petstore API
|
||||
tags:
|
||||
- store
|
||||
- rest
|
||||
spec:
|
||||
type: openapi
|
||||
lifecycle: experimental
|
||||
owner: team-c
|
||||
definition: |
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
license:
|
||||
name: MIT
|
||||
servers:
|
||||
- url: http://petstore.swagger.io/v1
|
||||
paths:
|
||||
/pets:
|
||||
get:
|
||||
summary: List all pets
|
||||
operationId: listPets
|
||||
tags:
|
||||
- pets
|
||||
parameters:
|
||||
- name: limit
|
||||
in: query
|
||||
description: How many items to return at one time (max 100)
|
||||
required: false
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
responses:
|
||||
'200':
|
||||
description: A paged array of pets
|
||||
headers:
|
||||
x-next:
|
||||
description: A link to the next page of responses
|
||||
schema:
|
||||
type: string
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pets"
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
post:
|
||||
summary: Create a pet
|
||||
operationId: createPets
|
||||
tags:
|
||||
- pets
|
||||
responses:
|
||||
'201':
|
||||
description: Null response
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
/pets/{petId}:
|
||||
get:
|
||||
summary: Info for a specific pet
|
||||
operationId: showPetById
|
||||
tags:
|
||||
- pets
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
required: true
|
||||
description: The id of the pet to retrieve
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Expected response to a valid request
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
default:
|
||||
description: unexpected error
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Error"
|
||||
components:
|
||||
schemas:
|
||||
Pet:
|
||||
type: object
|
||||
required:
|
||||
- id
|
||||
- name
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
tag:
|
||||
type: string
|
||||
Pets:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
Error:
|
||||
type: object
|
||||
required:
|
||||
- code
|
||||
- message
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int32
|
||||
message:
|
||||
type: string
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: API
|
||||
metadata:
|
||||
name: spotify
|
||||
description: The Spotify web API
|
||||
tags:
|
||||
- spotify
|
||||
- rest
|
||||
annotations:
|
||||
# The annotation is deprecated, we use placeholders (see below) instead, remove it later.
|
||||
backstage.io/definition-at-location: 'url:https://raw.githubusercontent.com/APIs-guru/openapi-directory/master/APIs/spotify.com/v1/swagger.yaml'
|
||||
spec:
|
||||
type: openapi
|
||||
lifecycle: production
|
||||
owner: team-a
|
||||
definition:
|
||||
$text: https://github.com/APIs-guru/openapi-directory/blob/master/APIs/spotify.com/v1/swagger.yaml
|
||||
@@ -0,0 +1,221 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: API
|
||||
metadata:
|
||||
name: streetlights
|
||||
description: The Smartylighting Streetlights API allows you to remotely manage the city lights.
|
||||
tags:
|
||||
- mqtt
|
||||
spec:
|
||||
type: asyncapi
|
||||
lifecycle: production
|
||||
owner: team-c
|
||||
definition: |
|
||||
asyncapi: 2.0.0
|
||||
info:
|
||||
title: Streetlights API
|
||||
version: '1.0.0'
|
||||
description: |
|
||||
The Smartylighting Streetlights API allows you to remotely manage the city lights.
|
||||
|
||||
### Check out its awesome features:
|
||||
|
||||
* Turn a specific streetlight on/off 🌃
|
||||
* Dim a specific streetlight 😎
|
||||
* Receive real-time information about environmental lighting conditions 📈
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
servers:
|
||||
production:
|
||||
url: api.streetlights.smartylighting.com:{port}
|
||||
protocol: mqtt
|
||||
description: Test broker
|
||||
variables:
|
||||
port:
|
||||
description: Secure connection (TLS) is available through port 8883.
|
||||
default: '1883'
|
||||
enum:
|
||||
- '1883'
|
||||
- '8883'
|
||||
security:
|
||||
- apiKey: []
|
||||
- supportedOauthFlows:
|
||||
- streetlights:on
|
||||
- streetlights:off
|
||||
- streetlights:dim
|
||||
- openIdConnectWellKnown: []
|
||||
|
||||
defaultContentType: application/json
|
||||
|
||||
channels:
|
||||
smartylighting/streetlights/1/0/event/{streetlightId}/lighting/measured:
|
||||
description: The topic on which measured values may be produced and consumed.
|
||||
parameters:
|
||||
streetlightId:
|
||||
$ref: '#/components/parameters/streetlightId'
|
||||
subscribe:
|
||||
summary: Receive information about environmental lighting conditions of a particular streetlight.
|
||||
operationId: receiveLightMeasurement
|
||||
traits:
|
||||
- $ref: '#/components/operationTraits/kafka'
|
||||
message:
|
||||
$ref: '#/components/messages/lightMeasured'
|
||||
|
||||
smartylighting/streetlights/1/0/action/{streetlightId}/turn/on:
|
||||
parameters:
|
||||
streetlightId:
|
||||
$ref: '#/components/parameters/streetlightId'
|
||||
publish:
|
||||
operationId: turnOn
|
||||
traits:
|
||||
- $ref: '#/components/operationTraits/kafka'
|
||||
message:
|
||||
$ref: '#/components/messages/turnOnOff'
|
||||
|
||||
smartylighting/streetlights/1/0/action/{streetlightId}/turn/off:
|
||||
parameters:
|
||||
streetlightId:
|
||||
$ref: '#/components/parameters/streetlightId'
|
||||
publish:
|
||||
operationId: turnOff
|
||||
traits:
|
||||
- $ref: '#/components/operationTraits/kafka'
|
||||
message:
|
||||
$ref: '#/components/messages/turnOnOff'
|
||||
|
||||
smartylighting/streetlights/1/0/action/{streetlightId}/dim:
|
||||
parameters:
|
||||
streetlightId:
|
||||
$ref: '#/components/parameters/streetlightId'
|
||||
publish:
|
||||
operationId: dimLight
|
||||
traits:
|
||||
- $ref: '#/components/operationTraits/kafka'
|
||||
message:
|
||||
$ref: '#/components/messages/dimLight'
|
||||
|
||||
components:
|
||||
messages:
|
||||
lightMeasured:
|
||||
name: lightMeasured
|
||||
title: Light measured
|
||||
summary: Inform about environmental lighting conditions for a particular streetlight.
|
||||
contentType: application/json
|
||||
traits:
|
||||
- $ref: '#/components/messageTraits/commonHeaders'
|
||||
payload:
|
||||
$ref: "#/components/schemas/lightMeasuredPayload"
|
||||
turnOnOff:
|
||||
name: turnOnOff
|
||||
title: Turn on/off
|
||||
summary: Command a particular streetlight to turn the lights on or off.
|
||||
traits:
|
||||
- $ref: '#/components/messageTraits/commonHeaders'
|
||||
payload:
|
||||
$ref: "#/components/schemas/turnOnOffPayload"
|
||||
dimLight:
|
||||
name: dimLight
|
||||
title: Dim light
|
||||
summary: Command a particular streetlight to dim the lights.
|
||||
traits:
|
||||
- $ref: '#/components/messageTraits/commonHeaders'
|
||||
payload:
|
||||
$ref: "#/components/schemas/dimLightPayload"
|
||||
|
||||
schemas:
|
||||
lightMeasuredPayload:
|
||||
type: object
|
||||
properties:
|
||||
lumens:
|
||||
type: integer
|
||||
minimum: 0
|
||||
description: Light intensity measured in lumens.
|
||||
sentAt:
|
||||
$ref: "#/components/schemas/sentAt"
|
||||
turnOnOffPayload:
|
||||
type: object
|
||||
properties:
|
||||
command:
|
||||
type: string
|
||||
enum:
|
||||
- on
|
||||
- off
|
||||
description: Whether to turn on or off the light.
|
||||
sentAt:
|
||||
$ref: "#/components/schemas/sentAt"
|
||||
dimLightPayload:
|
||||
type: object
|
||||
properties:
|
||||
percentage:
|
||||
type: integer
|
||||
description: Percentage to which the light should be dimmed to.
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
sentAt:
|
||||
$ref: "#/components/schemas/sentAt"
|
||||
sentAt:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Date and time when the message was sent.
|
||||
|
||||
securitySchemes:
|
||||
apiKey:
|
||||
type: apiKey
|
||||
in: user
|
||||
description: Provide your API key as the user and leave the password empty.
|
||||
supportedOauthFlows:
|
||||
type: oauth2
|
||||
description: Flows to support OAuth 2.0
|
||||
flows:
|
||||
implicit:
|
||||
authorizationUrl: 'https://authserver.example/auth'
|
||||
scopes:
|
||||
'streetlights:on': Ability to switch lights on
|
||||
'streetlights:off': Ability to switch lights off
|
||||
'streetlights:dim': Ability to dim the lights
|
||||
password:
|
||||
tokenUrl: 'https://authserver.example/token'
|
||||
scopes:
|
||||
'streetlights:on': Ability to switch lights on
|
||||
'streetlights:off': Ability to switch lights off
|
||||
'streetlights:dim': Ability to dim the lights
|
||||
clientCredentials:
|
||||
tokenUrl: 'https://authserver.example/token'
|
||||
scopes:
|
||||
'streetlights:on': Ability to switch lights on
|
||||
'streetlights:off': Ability to switch lights off
|
||||
'streetlights:dim': Ability to dim the lights
|
||||
authorizationCode:
|
||||
authorizationUrl: 'https://authserver.example/auth'
|
||||
tokenUrl: 'https://authserver.example/token'
|
||||
refreshUrl: 'https://authserver.example/refresh'
|
||||
scopes:
|
||||
'streetlights:on': Ability to switch lights on
|
||||
'streetlights:off': Ability to switch lights off
|
||||
'streetlights:dim': Ability to dim the lights
|
||||
openIdConnectWellKnown:
|
||||
type: openIdConnect
|
||||
openIdConnectUrl: 'https://authserver.example/.well-known'
|
||||
|
||||
parameters:
|
||||
streetlightId:
|
||||
description: The ID of the streetlight.
|
||||
schema:
|
||||
type: string
|
||||
|
||||
messageTraits:
|
||||
commonHeaders:
|
||||
headers:
|
||||
type: object
|
||||
properties:
|
||||
my-app-header:
|
||||
type: integer
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
|
||||
operationTraits:
|
||||
kafka:
|
||||
bindings:
|
||||
kafka:
|
||||
clientId: my-app-id
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: artist-lookup
|
||||
description: Artist Lookup
|
||||
tags:
|
||||
- java
|
||||
- data
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: team-a
|
||||
@@ -0,0 +1,13 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: petstore
|
||||
description: Petstore
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: team-c
|
||||
implementsApis:
|
||||
- petstore
|
||||
- streetlights
|
||||
- hello-world
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: playback-sdk
|
||||
description: Audio and video playback SDK
|
||||
spec:
|
||||
type: library
|
||||
lifecycle: experimental
|
||||
owner: team-c
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: playback-order
|
||||
description: Playback Order
|
||||
tags:
|
||||
- java
|
||||
- playback
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: user:guest
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: podcast-api
|
||||
description: Podcast API
|
||||
tags:
|
||||
- java
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: team-b
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: queue-proxy
|
||||
description: Queue Proxy
|
||||
tags:
|
||||
- go
|
||||
- website
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: team-b
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: searcher
|
||||
description: Searcher
|
||||
tags:
|
||||
- go
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: user:guest
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: shuffle-api
|
||||
description: Shuffle API
|
||||
tags:
|
||||
- go
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: user:guest
|
||||
@@ -0,0 +1,9 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: www-artist
|
||||
description: Artist main website
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: team-a
|
||||
@@ -0,0 +1,7 @@
|
||||
# Example Entities
|
||||
|
||||
NOTE: These are being replaced with the contents of the `examples-relative`
|
||||
sibling directory, after December 16 2020. If you are using this example data
|
||||
you will need to be using a released version of the catalog backend which
|
||||
supports relative targets in Location entities
|
||||
[see #3513](https://github.com/backstage/backstage/pull/3513) after that time.
|
||||
@@ -42,3 +42,17 @@ spec:
|
||||
email: nigel-manning@example.com
|
||||
picture: https://example.com/staff/nigel.jpeg
|
||||
memberOf: [team-a]
|
||||
---
|
||||
# This user is added as an example, to make it more easy for the "Guest"
|
||||
# sign-in option to demonstrate some entities being owned. In a regular org,
|
||||
# a guest user would probably not be registered like this.
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: guest
|
||||
spec:
|
||||
profile:
|
||||
displayName: Guest User
|
||||
email: guest@example.com
|
||||
picture: https://example.com/staff/the-ceos-dog.jpeg
|
||||
memberOf: [team-a]
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: grpc
|
||||
lifecycle: deprecated
|
||||
owner: grpc@example.com
|
||||
owner: team-c
|
||||
definition: |
|
||||
// Copyright 2015 gRPC authors.
|
||||
//
|
||||
|
||||
@@ -9,7 +9,7 @@ metadata:
|
||||
spec:
|
||||
type: openapi
|
||||
lifecycle: experimental
|
||||
owner: pets@example.com
|
||||
owner: team-c
|
||||
definition: |
|
||||
openapi: "3.0.0"
|
||||
info:
|
||||
|
||||
@@ -12,6 +12,6 @@ metadata:
|
||||
spec:
|
||||
type: openapi
|
||||
lifecycle: production
|
||||
owner: spotify@example.com
|
||||
owner: team-a
|
||||
definition:
|
||||
$text: https://github.com/APIs-guru/openapi-directory/blob/master/APIs/spotify.com/v1/swagger.yaml
|
||||
|
||||
@@ -8,7 +8,7 @@ metadata:
|
||||
spec:
|
||||
type: asyncapi
|
||||
lifecycle: production
|
||||
owner: streetlights@example.com
|
||||
owner: team-c
|
||||
definition: |
|
||||
asyncapi: 2.0.0
|
||||
info:
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: graphql
|
||||
lifecycle: production
|
||||
owner: yoda@dagobah.space
|
||||
owner: team-b
|
||||
definition: |
|
||||
schema {
|
||||
query: Root
|
||||
|
||||
@@ -9,4 +9,4 @@ metadata:
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: artists@example.com
|
||||
owner: team-a
|
||||
|
||||
@@ -6,7 +6,7 @@ metadata:
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: pets@example.com
|
||||
owner: team-c
|
||||
implementsApis:
|
||||
- petstore
|
||||
- streetlights
|
||||
|
||||
@@ -6,4 +6,4 @@ metadata:
|
||||
spec:
|
||||
type: library
|
||||
lifecycle: experimental
|
||||
owner: players@example.com
|
||||
owner: team-c
|
||||
|
||||
@@ -9,4 +9,4 @@ metadata:
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: guest
|
||||
owner: user:guest
|
||||
|
||||
@@ -8,4 +8,4 @@ metadata:
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: players@example.com
|
||||
owner: team-b
|
||||
|
||||
@@ -9,4 +9,4 @@ metadata:
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: tools@example.com
|
||||
owner: team-b
|
||||
|
||||
@@ -8,4 +8,4 @@ metadata:
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: guest
|
||||
owner: user:guest
|
||||
|
||||
@@ -8,4 +8,4 @@ metadata:
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: production
|
||||
owner: guest
|
||||
owner: user:guest
|
||||
|
||||
@@ -6,4 +6,4 @@ metadata:
|
||||
spec:
|
||||
type: website
|
||||
lifecycle: production
|
||||
owner: artists@example.com
|
||||
owner: team-a
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/catalog-model",
|
||||
"version": "0.3.1",
|
||||
"version": "0.4.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -29,7 +29,7 @@
|
||||
"yup": "^0.29.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.3.2",
|
||||
"@backstage/cli": "^0.4.0",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/lodash": "^4.14.151",
|
||||
|
||||
@@ -32,15 +32,6 @@ const schema = yup.object<Partial<ComponentEntityV1alpha1>>({
|
||||
implementsApis: yup.array(yup.string().required()).notRequired(),
|
||||
providesApis: yup.array(yup.string().required()).notRequired(),
|
||||
consumesApis: yup.array(yup.string().required()).notRequired(),
|
||||
kubernetes: yup
|
||||
.object<any>({
|
||||
selector: yup
|
||||
.object<any>({
|
||||
matchLabels: yup.object<any>().required(),
|
||||
})
|
||||
.required(),
|
||||
})
|
||||
.notRequired(),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
@@ -60,13 +51,6 @@ export interface ComponentEntityV1alpha1 extends Entity {
|
||||
implementsApis?: string[];
|
||||
providesApis?: string[];
|
||||
consumesApis?: string[];
|
||||
kubernetes?: {
|
||||
selector: {
|
||||
matchLabels: {
|
||||
[key: string]: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ describe('LocationV1alpha1Validator', () => {
|
||||
await expect(validator.check(entity)).resolves.toBe(false);
|
||||
});
|
||||
|
||||
it('rejects missing type', async () => {
|
||||
it('accepts missing type', async () => {
|
||||
delete (entity as any).spec.type;
|
||||
await expect(validator.check(entity)).rejects.toThrow(/type/);
|
||||
await expect(validator.check(entity)).resolves.toBe(true);
|
||||
});
|
||||
|
||||
it('rejects wrong type', async () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ const schema = yup.object<Partial<LocationEntityV1alpha1>>({
|
||||
kind: yup.string().required().equals([KIND]),
|
||||
spec: yup
|
||||
.object({
|
||||
type: yup.string().required().min(1),
|
||||
type: yup.string().notRequired().min(1),
|
||||
target: yup.string().notRequired().min(1),
|
||||
targets: yup.array(yup.string().required()).notRequired(),
|
||||
})
|
||||
@@ -37,7 +37,7 @@ export interface LocationEntityV1alpha1 extends Entity {
|
||||
apiVersion: typeof API_VERSION[number];
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
type: string;
|
||||
type?: string;
|
||||
target?: string;
|
||||
targets?: string[];
|
||||
};
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# @backstage/cli
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 00670a96e: sort product panels and navigation menu by greatest cost
|
||||
update tsconfig.json to use ES2020 api
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError
|
||||
- 4a655c89d: Bump versions of `esbuild` and `rollup-plugin-esbuild`
|
||||
- 8a16e8af8: Support `.npmrc` when building with private NPM registries
|
||||
- Updated dependencies [4e7091759]
|
||||
- Updated dependencies [b4488ddb0]
|
||||
- @backstage/config-loader@0.4.0
|
||||
|
||||
## 0.3.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+12
@@ -97,3 +97,15 @@ declare module '*.module.sass' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
}
|
||||
|
||||
// NOTE(freben): Both the fix, and the placement of the fix, are not great.
|
||||
//
|
||||
// The fix is because the PositionError was renamed to
|
||||
// GeolocationPositionError outside of our control, and react-use is dependent
|
||||
// on the old name.
|
||||
//
|
||||
// The placement is because it's the one location we have at the moment, where
|
||||
// a central .d.ts file is imported by the frontend and can be amended.
|
||||
//
|
||||
// After both TS and react-use are bumped high enough, this should be removed.
|
||||
type PositionError = GeolocationPositionError;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.3.2",
|
||||
"version": "0.4.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -30,7 +30,7 @@
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "^0.1.1",
|
||||
"@backstage/config": "^0.1.1",
|
||||
"@backstage/config-loader": "^0.3.0",
|
||||
"@backstage/config-loader": "^0.4.0",
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@lerna/package-graph": "^3.18.5",
|
||||
"@lerna/project": "^3.18.0",
|
||||
@@ -111,11 +111,11 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.3.2",
|
||||
"@backstage/backend-common": "^0.3.3",
|
||||
"@backstage/config": "^0.1.1",
|
||||
"@backstage/core": "^0.3.2",
|
||||
"@backstage/dev-utils": "^0.1.4",
|
||||
"@backstage/test-utils": "^0.1.3",
|
||||
"@backstage/dev-utils": "^0.1.5",
|
||||
"@backstage/test-utils": "^0.1.4",
|
||||
"@backstage/theme": "^0.2.1",
|
||||
"@types/diff": "^4.0.2",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
|
||||
@@ -34,11 +34,15 @@ export default async (cmd: Command) => {
|
||||
const pkgPath = paths.resolveTarget(PKG_PATH);
|
||||
const pkg = await fs.readJson(pkgPath);
|
||||
const appConfigs = await findAppConfigs();
|
||||
const npmrc = (await fs.pathExists(paths.resolveTargetRoot('.npmrc')))
|
||||
? ['.npmrc']
|
||||
: [];
|
||||
const tempDistWorkspace = await createDistWorkspace([pkg.name], {
|
||||
buildDependencies: Boolean(cmd.build),
|
||||
files: [
|
||||
'package.json',
|
||||
'yarn.lock',
|
||||
...npmrc,
|
||||
...appConfigs,
|
||||
{ src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' },
|
||||
],
|
||||
|
||||
@@ -47,6 +47,7 @@ export function registerCommands(program: CommanderStatic) {
|
||||
program
|
||||
.command('backend:__experimental__bundle__', { hidden: true })
|
||||
.description('Bundle all backend packages into dist-workspace')
|
||||
.option('--build', 'Build packages before packing them into the image')
|
||||
.action(lazy(() => import('./backend/bundle').then(m => m.default)));
|
||||
|
||||
program
|
||||
@@ -55,7 +56,11 @@ export function registerCommands(program: CommanderStatic) {
|
||||
.helpOption(', --backstage-cli-help') // Let docker handle --help
|
||||
.option('--build', 'Build packages before packing them into the image')
|
||||
.description(
|
||||
'Bundles the package into a docker image. All extra args are forwarded to docker image build',
|
||||
// TODO: Add example use cases in Backstage documentation.
|
||||
// For example, if a $NPM_TOKEN needs to be exposed, run `backend:build-image --secret
|
||||
// id=NPM_TOKEN,src=/NPM_TOKEN.txt`.
|
||||
'Bundles the package into a docker image. All extra args are forwarded to ' +
|
||||
'`docker image build`.',
|
||||
)
|
||||
.action(lazy(() => import('./backend/buildImage').then(m => m.default)));
|
||||
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @backstage/config-loader
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 4e7091759: Fix typo of "visibility" in config schema reference
|
||||
|
||||
If you have defined a config element named `visiblity`, you
|
||||
will need to fix the spelling to `visibility`. For more info,
|
||||
see https://backstage.io/docs/conf/defining#visibility.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError
|
||||
|
||||
## 0.3.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/config-loader",
|
||||
"description": "Config loading functionality used by Backstage backend, and CLI",
|
||||
"version": "0.3.0",
|
||||
"version": "0.4.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -36,7 +36,7 @@
|
||||
"fs-extra": "^9.0.0",
|
||||
"json-schema": "^0.2.5",
|
||||
"json-schema-merge-allof": "^0.7.0",
|
||||
"typescript-json-schema": "^0.43.0",
|
||||
"typescript-json-schema": "^0.45.0",
|
||||
"yaml": "^1.9.2",
|
||||
"yup": "^0.29.3"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @backstage/core-api
|
||||
|
||||
## 0.2.4
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- b4488ddb0: Added a type alias for PositionError = GeolocationPositionError
|
||||
- @backstage/test-utils@0.1.4
|
||||
|
||||
## 0.2.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-api",
|
||||
"description": "Internal Core API used by Backstage plugins and apps",
|
||||
"version": "0.2.3",
|
||||
"version": "0.2.4",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,7 +30,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1",
|
||||
"@backstage/test-utils": "^0.1.3",
|
||||
"@backstage/test-utils": "^0.1.4",
|
||||
"@backstage/theme": "^0.2.1",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
@@ -42,7 +42,7 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.3.2",
|
||||
"@backstage/cli": "^0.4.0",
|
||||
"@backstage/test-utils-core": "^0.1.1",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
|
||||
@@ -19,7 +19,7 @@ import { Observable } from '../../../types';
|
||||
|
||||
type RequestQueueEntry<ResultType> = {
|
||||
scopes: Set<string>;
|
||||
resolve: (value?: ResultType | PromiseLike<ResultType> | undefined) => void;
|
||||
resolve: (value: ResultType | PromiseLike<ResultType>) => void;
|
||||
reject: (reason: Error) => void;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import { attachComponentData, getComponentData } from './componentData';
|
||||
|
||||
describe('elementData', () => {
|
||||
it('should attach a single piece of data', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const Component = () => null;
|
||||
attachComponentData(Component, 'my-data', data);
|
||||
|
||||
const element = <Component />;
|
||||
expect(getComponentData(element, 'my-data')).toBe(data);
|
||||
});
|
||||
|
||||
it('should attach several distinct pieces of data', () => {
|
||||
const data1 = { foo: 'bar' };
|
||||
const data2 = { test: 'value' };
|
||||
const Component = () => null;
|
||||
attachComponentData(Component, 'my-data', data1);
|
||||
attachComponentData(Component, 'second', data2);
|
||||
|
||||
const element = <Component />;
|
||||
expect(getComponentData(element, 'my-data')).toBe(data1);
|
||||
expect(getComponentData(element, 'second')).toBe(data2);
|
||||
});
|
||||
|
||||
it('returns undefined for missing data', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const Component1 = () => null;
|
||||
const Component2 = () => null;
|
||||
attachComponentData(Component2, 'my-data', data);
|
||||
|
||||
const element1 = <Component1 />;
|
||||
const element2 = <Component2 />;
|
||||
expect(getComponentData(element1, 'missing')).toBeUndefined();
|
||||
expect(getComponentData(element2, 'missing')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should throw when attempting to overwrite data', () => {
|
||||
const data = { foo: 'bar' };
|
||||
const MyComponent = () => null;
|
||||
attachComponentData(MyComponent, 'my-data', data);
|
||||
expect(() => attachComponentData(MyComponent, 'my-data', data)).toThrow(
|
||||
'Attempted to attach duplicate data "my-data" to component "MyComponent"',
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { ComponentType, ReactNode } from 'react';
|
||||
|
||||
const DATA_KEY = Symbol('backstage-component-data');
|
||||
|
||||
type DataContainer = {
|
||||
map: Map<string, unknown>;
|
||||
};
|
||||
|
||||
type ComponentWithData<P> = ComponentType<P> & {
|
||||
[DATA_KEY]?: DataContainer;
|
||||
};
|
||||
|
||||
type ReactNodeWithData = ReactNode & {
|
||||
type?: { [DATA_KEY]?: DataContainer };
|
||||
};
|
||||
|
||||
export function attachComponentData<P>(
|
||||
component: ComponentType<P>,
|
||||
type: string,
|
||||
data: unknown,
|
||||
) {
|
||||
const dataComponent = component as ComponentWithData<P>;
|
||||
|
||||
let container = dataComponent[DATA_KEY];
|
||||
if (!container) {
|
||||
container = dataComponent[DATA_KEY] = { map: new Map() };
|
||||
}
|
||||
|
||||
if (container.map.has(type)) {
|
||||
const name = component.displayName || component.name;
|
||||
throw new Error(
|
||||
`Attempted to attach duplicate data "${type}" to component "${name}"`,
|
||||
);
|
||||
}
|
||||
|
||||
container.map.set(type, data);
|
||||
}
|
||||
|
||||
export function getComponentData<T>(
|
||||
node: ReactNode,
|
||||
type: string,
|
||||
): T | undefined {
|
||||
if (!node) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const container = (node as ReactNodeWithData).type?.[DATA_KEY];
|
||||
if (!container) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return container.map.get(type) as T | undefined;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React from 'react';
|
||||
import { createPlugin } from '../plugin';
|
||||
import { createRouteRef } from '../routing';
|
||||
import { getComponentData } from './componentData';
|
||||
import {
|
||||
createComponentExtension,
|
||||
createReactExtension,
|
||||
createRoutableExtension,
|
||||
} from './extensions';
|
||||
|
||||
const plugin = createPlugin({
|
||||
id: 'my-plugin',
|
||||
});
|
||||
|
||||
describe('extensions', () => {
|
||||
it('should create a react extension with component data', () => {
|
||||
const Component = () => null;
|
||||
|
||||
const extension = createReactExtension({
|
||||
component: Component,
|
||||
data: {
|
||||
myData: { foo: 'bar' },
|
||||
},
|
||||
});
|
||||
|
||||
const ExtensionComponent = plugin.provide(extension);
|
||||
const element = <ExtensionComponent />;
|
||||
|
||||
expect(getComponentData(element, 'core.plugin')).toBe(plugin);
|
||||
expect(getComponentData(element, 'myData')).toEqual({ foo: 'bar' });
|
||||
});
|
||||
|
||||
it('should create react extensions of different types', () => {
|
||||
const Component = () => null;
|
||||
const routeRef = createRouteRef({ path: '/foo', title: 'Foo' });
|
||||
|
||||
const extension1 = createComponentExtension({
|
||||
component: Component,
|
||||
});
|
||||
|
||||
const extension2 = createRoutableExtension({
|
||||
component: Component,
|
||||
mountPoint: routeRef,
|
||||
});
|
||||
|
||||
const ExtensionComponent1 = plugin.provide(extension1);
|
||||
const ExtensionComponent2 = plugin.provide(extension2);
|
||||
|
||||
const element1 = <ExtensionComponent1 />;
|
||||
const element2 = <ExtensionComponent2 />;
|
||||
|
||||
expect(getComponentData(element1, 'core.plugin')).toBe(plugin);
|
||||
expect(getComponentData(element2, 'core.plugin')).toBe(plugin);
|
||||
expect(getComponentData(element2, 'core.mountPoint')).toBe(routeRef);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React, {
|
||||
NamedExoticComponent,
|
||||
ComponentType,
|
||||
PropsWithChildren,
|
||||
} from 'react';
|
||||
import { RouteRef } from '../routing';
|
||||
import { attachComponentData } from './componentData';
|
||||
import { Extension, BackstagePlugin } from '../plugin/types';
|
||||
|
||||
export function createRoutableExtension<Props extends {}>(options: {
|
||||
component: ComponentType<Props>;
|
||||
mountPoint: RouteRef;
|
||||
// TODO(Rugvip): We want to carry forward the exact props type from the inner component, with
|
||||
// or without children. ComponentType stops us from doing that though, as it always
|
||||
// adds children to the props internally. We may want to work around this with custom types.
|
||||
}): Extension<
|
||||
NamedExoticComponent<PropsWithChildren<Props & { path?: string }>>
|
||||
> {
|
||||
const { component, mountPoint } = options;
|
||||
return createReactExtension({
|
||||
component,
|
||||
data: {
|
||||
'core.mountPoint': mountPoint,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createComponentExtension<Props extends {}>(options: {
|
||||
component: ComponentType<Props>;
|
||||
}): Extension<NamedExoticComponent<Props>> {
|
||||
const { component } = options;
|
||||
return createReactExtension({ component });
|
||||
}
|
||||
|
||||
export function createReactExtension<Props extends {}>(options: {
|
||||
component: ComponentType<Props>;
|
||||
data?: Record<string, unknown>;
|
||||
}): Extension<NamedExoticComponent<Props>> {
|
||||
const { component: Component, data = {} } = options;
|
||||
return {
|
||||
expose(plugin: BackstagePlugin): NamedExoticComponent<Props> {
|
||||
const Result = (props: Props) => <Component {...props} />;
|
||||
|
||||
attachComponentData(Result, 'core.plugin', plugin);
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
attachComponentData(Result, key, value);
|
||||
}
|
||||
|
||||
const name = Component.displayName || Component.name || 'Component';
|
||||
if (name) {
|
||||
Result.displayName = `Extension(${name})`;
|
||||
}
|
||||
return Result as NamedExoticComponent<Props>;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { attachComponentData, getComponentData } from './componentData';
|
||||
export {
|
||||
createReactExtension,
|
||||
createRoutableExtension,
|
||||
createComponentExtension,
|
||||
} from './extensions';
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React, { Children, isValidElement } from 'react';
|
||||
import {
|
||||
childDiscoverer,
|
||||
createCollector,
|
||||
traverseElementTree,
|
||||
} from './traversal';
|
||||
|
||||
describe('discovery', () => {
|
||||
it('should collect element names', () => {
|
||||
const root = (
|
||||
<main>
|
||||
<div>
|
||||
<h1>Title</h1>
|
||||
<p>Text</p>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
<h2>Title</h2>
|
||||
<span>Text</span>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
const { names } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer],
|
||||
collectors: {
|
||||
names: createCollector(Array<string>(), (acc, el) => {
|
||||
if (typeof el.type === 'string') {
|
||||
acc.push(el.type);
|
||||
}
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
expect(names).toEqual([
|
||||
'main',
|
||||
'div',
|
||||
'hr',
|
||||
'div',
|
||||
'h1',
|
||||
'p',
|
||||
'h2',
|
||||
'span',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should collect element names while skipping one level of children', () => {
|
||||
const root = (
|
||||
<main>
|
||||
<div>
|
||||
<h1>Title</h1>
|
||||
<p>Text</p>
|
||||
</div>
|
||||
<hr />
|
||||
<div>
|
||||
<h2>Title</h2>
|
||||
<span>Text</span>
|
||||
</div>
|
||||
</main>
|
||||
);
|
||||
|
||||
const { names } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [
|
||||
el =>
|
||||
Children.toArray(el.props.children).flatMap(child =>
|
||||
isValidElement(child) ? child?.props?.children : [],
|
||||
),
|
||||
],
|
||||
collectors: {
|
||||
names: createCollector(Array<string>(), (acc, el) => {
|
||||
if (typeof el.type === 'string') {
|
||||
acc.push(el.type);
|
||||
}
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
expect(names).toEqual(['main', 'h1', 'p', 'h2', 'span']);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { isValidElement, ReactNode, ReactElement, Children } from 'react';
|
||||
|
||||
export type Discoverer = (element: ReactElement) => ReactNode;
|
||||
|
||||
export type Collector<Result, Context> = () => {
|
||||
accumulator: Result;
|
||||
visit(
|
||||
accumulator: Result,
|
||||
element: ReactElement,
|
||||
parent: ReactElement,
|
||||
context: Context,
|
||||
): Context;
|
||||
};
|
||||
|
||||
/**
|
||||
* A function that allows you to traverse a tree of React elements using
|
||||
* varying methods to discover child nodes and collect data along the way.
|
||||
*/
|
||||
export function traverseElementTree<Results>(options: {
|
||||
root: ReactElement;
|
||||
discoverers: Discoverer[];
|
||||
collectors: { [name in keyof Results]: Collector<Results[name], any> };
|
||||
}): Results {
|
||||
const visited = new Set();
|
||||
const collectors: {
|
||||
[name in string]: ReturnType<Collector<any, any>>;
|
||||
} = {};
|
||||
|
||||
// Bootstrap all collectors, initializing the accumulators and providing the visitor function
|
||||
for (const name in options.collectors) {
|
||||
if (options.collectors.hasOwnProperty(name)) {
|
||||
collectors[name] = options.collectors[name]();
|
||||
}
|
||||
}
|
||||
|
||||
// Internal representation of an element in the tree that we're iterating over
|
||||
type QueueItem = {
|
||||
node: ReactNode;
|
||||
parent: ReactElement;
|
||||
contexts: { [name in string]: unknown };
|
||||
};
|
||||
|
||||
const queue = [
|
||||
{
|
||||
node: Children.toArray(options.root),
|
||||
parent: options.root,
|
||||
contexts: {},
|
||||
} as QueueItem,
|
||||
];
|
||||
|
||||
while (queue.length !== 0) {
|
||||
const { node, parent, contexts } = queue.shift()!;
|
||||
|
||||
// While the parent and the element we pass on to collectors and discoverers
|
||||
// have been validated and are known to be React elements, the child nodes
|
||||
// emitted by the discoverers are not.
|
||||
Children.forEach(node, element => {
|
||||
if (!isValidElement(element)) {
|
||||
return;
|
||||
}
|
||||
if (visited.has(element)) {
|
||||
const anyType = element?.type as
|
||||
| { displayName?: string; name?: string }
|
||||
| undefined;
|
||||
const name = anyType?.displayName || anyType?.name || String(anyType);
|
||||
throw new Error(`Visited element ${name} twice`);
|
||||
}
|
||||
visited.add(element);
|
||||
|
||||
const nextContexts: QueueItem['contexts'] = {};
|
||||
|
||||
// Collectors populate their result data using the current node, and compute
|
||||
// context for the next iteration
|
||||
for (const name in collectors) {
|
||||
if (collectors.hasOwnProperty(name)) {
|
||||
const collector = collectors[name];
|
||||
|
||||
nextContexts[name] = collector.visit(
|
||||
collector.accumulator,
|
||||
element,
|
||||
parent,
|
||||
contexts[name],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Discoverers provide ways to continue the traversal from the current element
|
||||
for (const discoverer of options.discoverers) {
|
||||
const children = discoverer(element);
|
||||
if (children) {
|
||||
queue.push({
|
||||
node: children,
|
||||
parent: element,
|
||||
contexts: nextContexts,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(collectors).map(([name, c]) => [name, c.accumulator]),
|
||||
) as Results;
|
||||
}
|
||||
|
||||
export function createCollector<Result, Context>(
|
||||
initialResult: Result,
|
||||
visit: ReturnType<Collector<Result, Context>>['visit'],
|
||||
): Collector<Result, Context> {
|
||||
return () => ({ accumulator: initialResult, visit });
|
||||
}
|
||||
|
||||
export function childDiscoverer(element: ReactElement): ReactNode {
|
||||
return element.props?.children;
|
||||
}
|
||||
|
||||
export function routeElementDiscoverer(element: ReactElement): ReactNode {
|
||||
if (element.props?.path && element.props?.element) {
|
||||
return element.props?.element;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
@@ -14,10 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { PluginConfig, PluginOutput, BackstagePlugin } from './types';
|
||||
import {
|
||||
PluginConfig,
|
||||
PluginOutput,
|
||||
BackstagePlugin,
|
||||
Extension,
|
||||
} from './types';
|
||||
import { AnyApiFactory } from '../apis';
|
||||
|
||||
export class PluginImpl {
|
||||
export class PluginImpl implements BackstagePlugin {
|
||||
private storedOutput?: PluginOutput[];
|
||||
|
||||
constructor(private readonly config: PluginConfig) {}
|
||||
@@ -65,6 +70,10 @@ export class PluginImpl {
|
||||
return this.storedOutput;
|
||||
}
|
||||
|
||||
provide<T>(extension: Extension<T>): T {
|
||||
return extension.expose(this);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `plugin{${this.config.id}}`;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React, { PropsWithChildren } from 'react';
|
||||
import { createRouteRef } from '../routing';
|
||||
import { createPlugin } from './Plugin';
|
||||
import {
|
||||
createRoutableExtension,
|
||||
createComponentExtension,
|
||||
} from '../extensions';
|
||||
import { MemoryRouter, Routes, Route } from 'react-router-dom';
|
||||
import {
|
||||
traverseElementTree,
|
||||
childDiscoverer,
|
||||
routeElementDiscoverer,
|
||||
} from '../extensions/traversal';
|
||||
import { pluginCollector } from './collectors';
|
||||
|
||||
const mockConfig = () => ({ path: '/foo', title: 'Foo' });
|
||||
const MockComponent = ({ children }: PropsWithChildren<{}>) => <>{children}</>;
|
||||
|
||||
const pluginA = createPlugin({ id: 'my-plugin-a' });
|
||||
const pluginB = createPlugin({ id: 'my-plugin-b' });
|
||||
const pluginC = createPlugin({ id: 'my-plugin-c' });
|
||||
|
||||
const ref1 = createRouteRef(mockConfig());
|
||||
const ref2 = createRouteRef(mockConfig());
|
||||
|
||||
const Extension1 = pluginA.provide(
|
||||
createRoutableExtension({ component: MockComponent, mountPoint: ref1 }),
|
||||
);
|
||||
const Extension2 = pluginB.provide(
|
||||
createRoutableExtension({ component: MockComponent, mountPoint: ref2 }),
|
||||
);
|
||||
const Extension3 = pluginA.provide(
|
||||
createComponentExtension({ component: MockComponent }),
|
||||
);
|
||||
const Extension4 = pluginB.provide(
|
||||
createComponentExtension({ component: MockComponent }),
|
||||
);
|
||||
const Extension5 = pluginC.provide(
|
||||
createComponentExtension({ component: MockComponent }),
|
||||
);
|
||||
|
||||
describe('collection', () => {
|
||||
it('should collect the plugins', () => {
|
||||
const root = (
|
||||
<MemoryRouter>
|
||||
<Routes>
|
||||
<Extension1 path="/foo">
|
||||
<div>
|
||||
<Extension2 path="/bar/:id">
|
||||
<div>
|
||||
<div />
|
||||
{[<Extension4 key={0} />]}
|
||||
Some text here shouldn't be a problem
|
||||
<div />
|
||||
{null}
|
||||
<div />
|
||||
<Extension3 />
|
||||
</div>
|
||||
</Extension2>
|
||||
{false}
|
||||
{true}
|
||||
{0}
|
||||
</div>
|
||||
</Extension1>
|
||||
<div>
|
||||
<Route path="/divsoup" element={<Extension5 />} />
|
||||
</div>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const { plugins } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
plugins: pluginCollector,
|
||||
},
|
||||
});
|
||||
|
||||
expect(plugins).toEqual(new Set([pluginA, pluginB, pluginC]));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 { BackstagePlugin } from './types';
|
||||
import { getComponentData } from '../extensions';
|
||||
import { createCollector } from '../extensions/traversal';
|
||||
|
||||
export const pluginCollector = createCollector(
|
||||
new Set<BackstagePlugin>(),
|
||||
(acc, node) => {
|
||||
const plugin = getComponentData<BackstagePlugin>(node, 'core.plugin');
|
||||
if (plugin) {
|
||||
acc.add(plugin);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -66,10 +66,15 @@ export type PluginOutput =
|
||||
| RedirectRouteOutput
|
||||
| FeatureFlagOutput;
|
||||
|
||||
export type Extension<T> = {
|
||||
expose(plugin: BackstagePlugin): T;
|
||||
};
|
||||
|
||||
export type BackstagePlugin = {
|
||||
getId(): string;
|
||||
output(): PluginOutput[];
|
||||
getApis(): Iterable<AnyApiFactory>;
|
||||
provide<T>(extension: Extension<T>): T;
|
||||
};
|
||||
|
||||
export type PluginConfig = {
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* 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 React, { PropsWithChildren } from 'react';
|
||||
import { routeCollector, routeParentCollector } from './collectors';
|
||||
|
||||
import {
|
||||
traverseElementTree,
|
||||
childDiscoverer,
|
||||
routeElementDiscoverer,
|
||||
} from '../extensions/traversal';
|
||||
import { createRouteRef } from './RouteRef';
|
||||
import { createPlugin } from '../plugin';
|
||||
import { createRoutableExtension } from '../extensions';
|
||||
import { MemoryRouter, Routes, Route } from 'react-router-dom';
|
||||
|
||||
const mockConfig = () => ({ path: '/foo', title: 'Foo' });
|
||||
const MockComponent = ({ children }: PropsWithChildren<{}>) => <>{children}</>;
|
||||
|
||||
const plugin = createPlugin({ id: 'my-plugin' });
|
||||
|
||||
const ref1 = createRouteRef(mockConfig());
|
||||
const ref2 = createRouteRef(mockConfig());
|
||||
const ref3 = createRouteRef(mockConfig());
|
||||
const ref4 = createRouteRef(mockConfig());
|
||||
const ref5 = createRouteRef(mockConfig());
|
||||
|
||||
const Extension1 = plugin.provide(
|
||||
createRoutableExtension({ component: MockComponent, mountPoint: ref1 }),
|
||||
);
|
||||
const Extension2 = plugin.provide(
|
||||
createRoutableExtension({ component: MockComponent, mountPoint: ref2 }),
|
||||
);
|
||||
const Extension3 = plugin.provide(
|
||||
createRoutableExtension({ component: MockComponent, mountPoint: ref3 }),
|
||||
);
|
||||
const Extension4 = plugin.provide(
|
||||
createRoutableExtension({ component: MockComponent, mountPoint: ref4 }),
|
||||
);
|
||||
const Extension5 = plugin.provide(
|
||||
createRoutableExtension({ component: MockComponent, mountPoint: ref5 }),
|
||||
);
|
||||
|
||||
describe('discovery', () => {
|
||||
it('should collect routes', () => {
|
||||
const list = [
|
||||
<div key={0} />,
|
||||
<div key={1} />,
|
||||
<div key={3}>
|
||||
<Extension5 path="/blop" />
|
||||
</div>,
|
||||
];
|
||||
|
||||
const root = (
|
||||
<MemoryRouter>
|
||||
<Routes>
|
||||
<Extension1 path="/foo">
|
||||
<div>
|
||||
<Extension2 path="/bar/:id">
|
||||
<div>
|
||||
<div />
|
||||
Some text here shouldn't be a problem
|
||||
<div />
|
||||
{null}
|
||||
<div />
|
||||
<Extension3 path="/baz" />
|
||||
</div>
|
||||
</Extension2>
|
||||
{false}
|
||||
{list}
|
||||
{true}
|
||||
{0}
|
||||
</div>
|
||||
</Extension1>
|
||||
<div>
|
||||
<Route path="/divsoup" element={<Extension4 />} />
|
||||
</div>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const { routes, routeParents } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routes: routeCollector,
|
||||
routeParents: routeParentCollector,
|
||||
},
|
||||
});
|
||||
expect(routes).toEqual(
|
||||
new Map([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/bar/:id'],
|
||||
[ref3, '/baz'],
|
||||
[ref4, '/divsoup'],
|
||||
[ref5, '/blop'],
|
||||
]),
|
||||
);
|
||||
|
||||
expect(routeParents).toEqual(
|
||||
new Map([
|
||||
[ref1, undefined],
|
||||
[ref2, ref1],
|
||||
[ref3, ref2],
|
||||
[ref4, undefined],
|
||||
[ref5, ref1],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle all react router Route patterns', () => {
|
||||
const root = (
|
||||
<MemoryRouter>
|
||||
<Routes>
|
||||
<Route
|
||||
path="/foo"
|
||||
element={
|
||||
<Extension1>
|
||||
<Routes>
|
||||
<Extension2 path="/bar/:id" />
|
||||
</Routes>
|
||||
</Extension1>
|
||||
}
|
||||
/>
|
||||
<Route path="/baz" element={<Extension3 path="/not-used" />}>
|
||||
<Route path="/divsoup" element={<Extension4 />} />
|
||||
<Extension5 path="/blop" />
|
||||
</Route>
|
||||
</Routes>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
const { routes, routeParents } = traverseElementTree({
|
||||
root,
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routes: routeCollector,
|
||||
routeParents: routeParentCollector,
|
||||
},
|
||||
});
|
||||
expect(routes).toEqual(
|
||||
new Map([
|
||||
[ref1, '/foo'],
|
||||
[ref2, '/bar/:id'],
|
||||
[ref3, '/baz'],
|
||||
[ref4, '/divsoup'],
|
||||
[ref5, '/blop'],
|
||||
]),
|
||||
);
|
||||
expect(routeParents).toEqual(
|
||||
new Map([
|
||||
[ref1, undefined],
|
||||
[ref2, ref1],
|
||||
[ref3, undefined],
|
||||
[ref4, ref3],
|
||||
[ref5, ref3],
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not visit the same element twice', () => {
|
||||
const element = <Extension3 path="/baz" />;
|
||||
|
||||
expect(() =>
|
||||
traverseElementTree({
|
||||
root: (
|
||||
<MemoryRouter>
|
||||
<Extension1 path="/foo">{element}</Extension1>
|
||||
<Extension2 path="/bar">{element}</Extension2>
|
||||
</MemoryRouter>
|
||||
),
|
||||
discoverers: [childDiscoverer, routeElementDiscoverer],
|
||||
collectors: {
|
||||
routes: routeCollector,
|
||||
routeParents: routeParentCollector,
|
||||
},
|
||||
}),
|
||||
).toThrow(`Visited element Extension(MockComponent) twice`);
|
||||
});
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user