merge master to samiram/fix-banner-pos
This commit is contained in:
@@ -13,6 +13,8 @@ flags:
|
||||
core:
|
||||
paths:
|
||||
- packages/core/
|
||||
carryforward: true
|
||||
core-api:
|
||||
paths:
|
||||
- packages/core-api/
|
||||
carryforward: true
|
||||
|
||||
@@ -12,6 +12,10 @@ If you encounter issues while upgrading to a newer version, don't hesitate to re
|
||||
|
||||
- Renamed `SessionStateApi` to `SessionApi` and `logout` to `signOut`. Custom implementations of the `SingInPage` app-component will need to rename their `logout` function. The different auth provider items for the `UserSettingsMenu` have been consolidated into a single `ProviderSettingsItem`, meaning you need to replace existing usages of `OAuthProviderSettings` and `OIDCProviderSettings`. [#2555](https://github.com/spotify/backstage/pull/2555).
|
||||
|
||||
### @backstage/auth-backend
|
||||
|
||||
- The default mount path of backend plugins was changed to `/api/:pluginId`, and as part of that it was needed to enable configuration of the base path of the auth backend, so that it can construct redirect URLs correctly. The default base path is `/api/auth`, but you need to set this to `/auth` if you want to keep using the old path. Note that you will also need to reconfigure any allowed redirect URLs to include `/api` if you switch to the new recommended pattern. [#2562](https://github.com/spotify/backstage/pull/2562)
|
||||
|
||||
## v0.1.1-alpha.22
|
||||
|
||||
### @backstage/core
|
||||
|
||||
@@ -32,6 +32,10 @@ If you start developing a plugin that you aim to release as open source, we sugg
|
||||
|
||||
You can also use this process if you have an idea for a good plugin but you hope that someone else will pick up the work.
|
||||
|
||||
## Adding Non-code Contributions
|
||||
|
||||
Since there is such a large landscape of possible development, build, and deployment environments, we welcome community contributions in these areas in the [`/contrib`](https://github.com/spotify/backstage/tree/master/contrib) folder of the project. This is an excellent place to put things that help out the community at large, but which may not fit within the scope of the core product to support natively. Here, you will find Helm charts, alternative Docker images, and much more.
|
||||
|
||||
## Write Documentation
|
||||
|
||||
The current documentation is very limited. Help us make the `/docs` folder come alive.
|
||||
|
||||
@@ -26,6 +26,8 @@ Out of the box, Backstage includes:
|
||||
|
||||
For more information go to [backstage.io](https://backstage.io) or join our [Discord chatroom](https://discord.gg/EBHEGzX).
|
||||
|
||||
🎉 Backstage is a CNCF Sandbox project. Read the announcement [here](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox).
|
||||
|
||||
## Project roadmap
|
||||
|
||||
A detailed project roadmap, including already delivered milestones, is available [here](https://backstage.io/docs/overview/roadmap).
|
||||
@@ -51,11 +53,9 @@ Check out [the documentation](https://backstage.io/docs/getting-started) on how
|
||||
- [Code of Conduct](CODE_OF_CONDUCT.md) - This is how we roll
|
||||
- [Adopters](ADOPTERS.md) - Companies already using Backstage
|
||||
- [Blog](https://backstage.io/blog/) - Announcements and updates
|
||||
- [Newsletter](https://mailchi.mp/spotify/backstage-community)
|
||||
- [Newsletter](https://mailchi.mp/spotify/backstage-community) - Subscribe to our email newsletter
|
||||
- Give us a star ⭐️ - If you are using Backstage or think it is an interesting project, we would love a star ❤️
|
||||
|
||||
Or, if you are an open source developer and are interested in joining our team, please reach out to [foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com)
|
||||
|
||||
## License
|
||||
|
||||
Copyright 2020 Spotify AB.
|
||||
|
||||
+6
-2
@@ -33,8 +33,8 @@ organization:
|
||||
name: Spotify
|
||||
|
||||
techdocs:
|
||||
storageUrl: http://localhost:7000/techdocs/static/docs
|
||||
requestUrl: http://localhost:7000/techdocs/docs
|
||||
storageUrl: http://localhost:7000/api/techdocs/static/docs
|
||||
requestUrl: http://localhost:7000/api/techdocs/docs
|
||||
generators:
|
||||
techdocs: 'docker'
|
||||
|
||||
@@ -55,6 +55,10 @@ newrelic:
|
||||
lighthouse:
|
||||
baseUrl: http://localhost:3003
|
||||
|
||||
kubernetes:
|
||||
clusterLocatorMethod: 'configMultiTenant'
|
||||
clusters: []
|
||||
|
||||
catalog:
|
||||
rules:
|
||||
- allow: [Component, API, Group, Template, Location]
|
||||
|
||||
@@ -7,7 +7,7 @@ description: Architecture Decision Record (ADR) log on Default Catalog File Name
|
||||
## Background
|
||||
|
||||
While the spec for the catalog file format is well described in
|
||||
[ADR002](./adr002-default-catalog-file-format.md), guidance was note provided as
|
||||
[ADR002](./adr002-default-catalog-file-format.md), guidance was not provided as
|
||||
to the name of the catalog file.
|
||||
|
||||
Following discussion in
|
||||
@@ -23,4 +23,4 @@ catalog-info.yaml
|
||||
```
|
||||
|
||||
This name is a default, **not a requirement**. The catalog file will work with
|
||||
Backstage irregardless of its name.
|
||||
Backstage regardless of its name.
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: adrs-adr009
|
||||
title: ADR009: Entity References
|
||||
description: Architecture Decision Record (ADR) log on Entity References
|
||||
---
|
||||
|
||||
## Background
|
||||
|
||||
While the spec for the catalog file format is well described in
|
||||
[ADR002](./adr002-default-catalog-file-format.md), guidance was not provided as
|
||||
to how one is expected to express references to other entities in the catalog.
|
||||
There was also some confusion on how to reference entities in URLs in the
|
||||
Backstage frontend.
|
||||
|
||||
Following discussion in
|
||||
[Issue 1947](https://github.com/spotify/backstage/issues/1947), a decision was
|
||||
made.
|
||||
|
||||
## Entity References in YAML files
|
||||
|
||||
The textual format, as written by humans, to reference entities by name is on
|
||||
the following form, where square brackets denote optionality:
|
||||
|
||||
```
|
||||
[<kind>:][<namespace>/]<name>
|
||||
```
|
||||
|
||||
That is, it is composed of between one and three parts in this specific order,
|
||||
without any additional encoding, with those exact separator characters.
|
||||
Optionality of `kind` and `namespace` are contextual, and they may or may not
|
||||
have default contextual fallback values.
|
||||
|
||||
When that format is insufficient or when machine made interchange formats wish
|
||||
to express such relations in a more expressive form, a nested structure on the
|
||||
following form can be used:
|
||||
|
||||
```yaml
|
||||
kind: <kind>
|
||||
namespace: <namespace>
|
||||
name: <name>
|
||||
```
|
||||
|
||||
Of these, only `name` is always required. Optionality of `kind` and `namespace`
|
||||
are contextual, and they may or may not have default contextual fallback values.
|
||||
All other possible key values in this structure are reserved for future use.
|
||||
|
||||
A system or user wanting to express a full entity name that is always valid,
|
||||
shall supply the entire triplet whether using the string form or the compound
|
||||
form.
|
||||
|
||||
A full description of the format can be found
|
||||
[in the documentation](https://backstage.io/docs/features/software-catalog/references).
|
||||
|
||||
## Entity References in URLs
|
||||
|
||||
Where entities are referenced by name in the Backstage frontend, the URL
|
||||
containing the reference shall take the following form:
|
||||
|
||||
```
|
||||
:namespace/:kind/:name
|
||||
```
|
||||
|
||||
All three parts are required under all circumstances. The default value for the
|
||||
`namespace` in the catalog is the string `"default"`, if the entity does not
|
||||
specify one explicitly in `metadata.namespace`.
|
||||
|
||||
This means that we do not encourage the string form of entity references to be
|
||||
used as a single URL segment, due to the use of URL-unsafe characters leading to
|
||||
possible risk, confusion, and uglier URLs.
|
||||
@@ -229,9 +229,10 @@ name.
|
||||
|
||||
### Test the new provider
|
||||
|
||||
You can `curl -i localhost:7000/auth/providerA/start` and which should provide a
|
||||
`302` redirect with a `Location` header. Paste the url from that header into a
|
||||
web browser and you should be able to trigger the authorization flow.
|
||||
You can `curl -i localhost:7000/api/auth/providerA/start` and which should
|
||||
provide a `302` redirect with a `Location` header. Paste the url from that
|
||||
header into a web browser and you should be able to trigger the authorization
|
||||
flow.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ humans. However, the structure and semantics is the same in both cases.
|
||||
- [Kind: Component](#kind-component)
|
||||
- [Kind: Template](#kind-template)
|
||||
- [Kind: API](#kind-api)
|
||||
- [Kind: Group](#kind-group)
|
||||
- [Kind: User](#kind-user)
|
||||
|
||||
## Overall Shape Of An Entity
|
||||
|
||||
@@ -571,3 +573,167 @@ group of people in an organizational structure.
|
||||
|
||||
The definition of the API, based on the format defined by `spec.type`. This
|
||||
field is required.
|
||||
|
||||
## Kind: Group
|
||||
|
||||
Describes the following entity kind:
|
||||
|
||||
| Field | Value |
|
||||
| ------------ | ----------------------- |
|
||||
| `apiVersion` | `backstage.io/v1alpha1` |
|
||||
| `kind` | `Group` |
|
||||
|
||||
A group describes an organizational entity, such as for example a team, a
|
||||
business unit, or a loose collection of people in an interest group. Members of
|
||||
these groups are modeled in the catalog as kind [`User`](#kind-user).
|
||||
|
||||
Descriptor files for this kind may look as follows.
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Group
|
||||
metadata:
|
||||
name: infrastructure
|
||||
description: The infra business unit
|
||||
spec:
|
||||
type: business-unit
|
||||
parent: ops
|
||||
ancestors: [ops, global-synergies, acme-corp]
|
||||
children: [backstage, other]
|
||||
descendants: [backstage, other, team-a, team-b, team-c, team-d]
|
||||
```
|
||||
|
||||
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 `Group`, respectively.
|
||||
|
||||
### `spec.type` [required]
|
||||
|
||||
The type of group as a string, e.g. `team`. There is currently no enforced set
|
||||
of values for this field, so it is left up to the adopting organization to
|
||||
choose a nomenclature that matches their org hierarchy.
|
||||
|
||||
Some common values for this field could be:
|
||||
|
||||
- `team`
|
||||
- `business-unit`
|
||||
- `product-area`
|
||||
- `root` - as a common virtual root of the hierarchy, if desired
|
||||
|
||||
### `spec.parent` [optional]
|
||||
|
||||
The immediate parent group in the hierarchy, if any. Not all groups must have a
|
||||
parent; the catalog supports multi-root hierarchies. Groups may however not have
|
||||
more than one parent.
|
||||
|
||||
This field is an
|
||||
[entity reference](https://backstage.io/docs/features/software-catalog/references),
|
||||
with the default kind `Group` and the default namespace equal to the same
|
||||
namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
this field points to a group in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of that group.
|
||||
|
||||
### `spec.ancestors` [required]
|
||||
|
||||
The recursive list of parents up the hierarchy, by stepping through parents one
|
||||
by one. The list must be present, but may be empty if `parent` is not present.
|
||||
The first entry in the list is equal to `parent`, and then the following ones
|
||||
are progressively farther up the hierarchy.
|
||||
|
||||
The entries of this array are
|
||||
[entity references](https://backstage.io/docs/features/software-catalog/references),
|
||||
with the default kind `Group` and the default namespace equal to the same
|
||||
namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
these entries point to groups in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of those groups.
|
||||
|
||||
### `spec.children` [required]
|
||||
|
||||
The immediate child groups of this group in the hierarchy (whose `parent` field
|
||||
points to this group). The list must be present, but may be empty if there are
|
||||
no child groups. The items are not guaranteed to be ordered in any particular
|
||||
way.
|
||||
|
||||
The entries of this array are
|
||||
[entity references](https://backstage.io/docs/features/software-catalog/references),
|
||||
with the default kind `Group` and the default namespace equal to the same
|
||||
namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
these entries point to groups in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of those groups.
|
||||
|
||||
### `spec.descendants` [required]
|
||||
|
||||
The immediate and recursive child groups of this group in the hierarchy
|
||||
(children, and children's children, etc.). The list must be present, but may be
|
||||
empty if there are no child groups. The items are not guaranteed to be ordered
|
||||
in any particular way.
|
||||
|
||||
The entries of this array are
|
||||
[entity references](https://backstage.io/docs/features/software-catalog/references),
|
||||
with the default kind `Group` and the default namespace equal to the same
|
||||
namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
these entries point to groups in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of those groups.
|
||||
|
||||
## Kind: User
|
||||
|
||||
Describes the following entity kind:
|
||||
|
||||
| Field | Value |
|
||||
| ------------ | ----------------------- |
|
||||
| `apiVersion` | `backstage.io/v1alpha1` |
|
||||
| `kind` | `User` |
|
||||
|
||||
A user describes a person, such as an employee, a contractor, or similar. Users
|
||||
belong to [`Group`](#kind-group) entities in the catalog.
|
||||
|
||||
These catalog user entries are connected to the way that authentication within
|
||||
the Backstage ecosystem works. See the [auth](https://backstage.io/docs/auth)
|
||||
section of the docs for a discussion of these concepts.
|
||||
|
||||
Descriptor files for this kind may look as follows.
|
||||
|
||||
```yaml
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: User
|
||||
metadata:
|
||||
name: jdoe
|
||||
spec:
|
||||
profile:
|
||||
displayName: Jenny Doe
|
||||
email: jenny-doe@example.com
|
||||
picture: https://example.com/staff/jenny-with-party-hat.jpeg
|
||||
memberOf: [team-b, employees]
|
||||
```
|
||||
|
||||
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 `User`, respectively.
|
||||
|
||||
### `spec.profile` [optional]
|
||||
|
||||
Optional profile information about the user, mainly for display purposes. All
|
||||
fields of this structure are also optional. The email would be a primary email
|
||||
of some form, that the user may wish to be used for contacting them. The picture
|
||||
is expected to be a URL pointing to an image that's representative of the user,
|
||||
and that a browser could fetch and render on a profile page or similar.
|
||||
|
||||
### `spec.memberOf` [required]
|
||||
|
||||
The list of groups that the user is a direct member of (i.e., no transitive
|
||||
memberships are listed here). The list must be present, but may be empty if the
|
||||
user is not member of any groups. The items are not guaranteed to be ordered in
|
||||
any particular way.
|
||||
|
||||
The entries of this array are
|
||||
[entity references](https://backstage.io/docs/features/software-catalog/references),
|
||||
with the default kind `Group` and the default namespace equal to the same
|
||||
namespace as the user. Only `Group` entities may be referenced. Most commonly,
|
||||
these entries point to groups in the same namespace, so in those cases it is
|
||||
sufficient to enter only the `metadata.name` field of those groups.
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: references
|
||||
title: Entity References
|
||||
description: How to express references between entities
|
||||
---
|
||||
|
||||
Entities commonly have a need to reference other entities. For example, a
|
||||
[Component](descriptor-format.md#kind-component) entity may want to declare who
|
||||
its owner is by mentioning a Group or User entity, and a User entity may want to
|
||||
declare what Group entities it is a member of. This article describes how to
|
||||
write those references in your yaml entity declaration files.
|
||||
|
||||
Each entity in the catalog is uniquely identified by the triplet of its
|
||||
[kind](descriptor-format.md#apiversion-and-kind-required),
|
||||
[namespace](descriptor-format.md#namespace-optional), and
|
||||
[name](descriptor-format.md#name-required). But that's a lot to type out
|
||||
manually, and in a lot of circumstances, both the kind and the namespace are
|
||||
fixed, or possible to deduce, or could have sane default values. So in order to
|
||||
help the writer, the catalog has a few tricks up its sleeve.
|
||||
|
||||
Each reference can be expressed in one of two ways: as a compact string, or as a
|
||||
compound reference structure.
|
||||
|
||||
## String References
|
||||
|
||||
This is the most common alternative, that should be used in almost all
|
||||
circumstances.
|
||||
|
||||
The string is on the form `[<kind>:][<namespace>/]<name>`, that is, it is
|
||||
composed of between one and three parts in this specific order, without any
|
||||
additional encoding:
|
||||
|
||||
- Optionally, the kind, followed by a colon
|
||||
- Optionally, the namespace, followed by a forward slash
|
||||
- The name
|
||||
|
||||
The name is always required. Depending on the context, you may be able to leave
|
||||
out the kind and/or namespace. If you do, it is contextual what values will be
|
||||
used, and the relevant documentation should specify which rule applies where.
|
||||
All strings are case insensitive.
|
||||
|
||||
```yaml
|
||||
# Example:
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: petstore
|
||||
namespace: external-systems
|
||||
description: Petstore
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: group:pet-managers
|
||||
implementsApis:
|
||||
- petstore
|
||||
- internal/streetlights
|
||||
- hello-world
|
||||
```
|
||||
|
||||
The field `spec.owner` is a reference. In this case, the string
|
||||
`group:pet-managers` was given by the user. That means that the kind is `Group`,
|
||||
the namespace is left out, and the name is `pet-managers`. In this context, the
|
||||
namespace was chosen to fall back to the value `default` by the code that parsed
|
||||
the reference, so the end result is that we expect to find another entity in the
|
||||
catalog that is of kind `Group`, namespace `default` (which, actually, also can
|
||||
be left out in its own yaml file because that's the default value there too),
|
||||
and name `pet-managers`.
|
||||
|
||||
The entries in `implementsApis` are also references. In this case, none of them
|
||||
need to specify a kind since we know from the context that that's the only kind
|
||||
that's supported here. The second entry specifies a namespace but the other ones
|
||||
don't, and in this context, the default is to refer to the same namespace as the
|
||||
originating entity (`external-systems` here). So the three references
|
||||
essentially expand to `api:external-systems/petstore`,
|
||||
`api:internal/streetlights`, and `api:external-systems/hello-world`. We expect
|
||||
there to exist three API kind entities in the catalog matching those references.
|
||||
|
||||
## Compound References
|
||||
|
||||
This is a more verbose version of a reference, where each part of the
|
||||
kind-namespace-name triplet is expressed as a field in a structure. This format
|
||||
can be used where necessary, such as if either of the three elements contain
|
||||
colons or forward slashes. Avoid using it where possible, since it is harder to
|
||||
read and write for humans.
|
||||
|
||||
```yaml
|
||||
# Example:
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: petstore
|
||||
description: Petstore
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner:
|
||||
kind: Group
|
||||
name: aegis-imports/pet-managers
|
||||
```
|
||||
|
||||
In this example, the `spec.owner` has been broken apart since the name was
|
||||
complex. The kind happened to be written with an uppercase letter G, which also
|
||||
works. The namespace was left out just like in the string version above, which
|
||||
is handled identically.
|
||||
@@ -88,7 +88,7 @@ running:
|
||||
```sh
|
||||
curl \
|
||||
--location \
|
||||
--request POST 'localhost:7000/catalog/locations' \
|
||||
--request POST 'localhost:7000/api/catalog/locations' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw "{\"type\": \"file\", \"target\": \"${YOUR PATH HERE}/template.yaml\"}"
|
||||
```
|
||||
@@ -98,7 +98,7 @@ If loading from a Git location, you can run the following
|
||||
```sh
|
||||
curl \
|
||||
--location \
|
||||
--request POST 'localhost:7000/catalog/locations' \
|
||||
--request POST 'localhost:7000/api/catalog/locations' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw "{\"type\": \"github\", \"target\": \"https://${YOUR GITHUB REPO}blob/master/${PATH TO FOLDER}/template.yaml\"}"
|
||||
```
|
||||
|
||||
@@ -57,8 +57,8 @@ The default storage and request URLs:
|
||||
|
||||
```yaml
|
||||
techdocs:
|
||||
storageUrl: http://localhost:7000/techdocs/static/docs
|
||||
requestUrl: http://localhost:7000/techdocs/docs
|
||||
storageUrl: http://localhost:7000/api/techdocs/static/docs
|
||||
requestUrl: http://localhost:7000/api/techdocs/docs
|
||||
```
|
||||
|
||||
If you want `techdocs-backend` to manage building and publishing, you want
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
id: troubleshooting
|
||||
title: Troubleshooting TechDocs
|
||||
sidebar_label: Troubleshooting
|
||||
description: Troubleshooting for TechDocs
|
||||
---
|
||||
|
||||
- TechDocs will fail to clone your docs if you have a git config which overrides
|
||||
the `https` protocol with `ssh` or something else. Make sure to remove your
|
||||
git config locally when you try TechDocs.
|
||||
@@ -6,10 +6,10 @@ info:
|
||||
|
||||
**Provided by `@backstage/auth-backend`.**
|
||||
|
||||
The purpose of the Auth APIs in Backstage are to identify the user, and to provide a way for plugins
|
||||
The purpose of the Auth APIs in Backstage are to identify the user, and to provide a way for plugins
|
||||
to request access to 3rd party services on behalf of that user.
|
||||
|
||||
The API is supplied with a list of providers - such as `Google` or `Github` - and will add the endpoints
|
||||
The API is supplied with a list of providers - such as `Google` or `Github` - and will add the endpoints
|
||||
described below to each of those providers.
|
||||
|
||||
Read more about [User Authentication and Authorization in Backstage](https://github.com/spotify/backstage/blob/master/docs/auth/overview.md).
|
||||
@@ -21,7 +21,7 @@ externalDocs:
|
||||
description: Backstage official documentation
|
||||
url: https://github.com/spotify/backstage/blob/master/docs/README.md
|
||||
servers:
|
||||
- url: http://localhost:7000/auth/
|
||||
- url: http://localhost:7000/api/auth/
|
||||
tags:
|
||||
- name: provider
|
||||
description: List of endpoints per provider
|
||||
|
||||
@@ -4,6 +4,23 @@ title: Architecture overview
|
||||
description: Documentation on Architecture overview
|
||||
---
|
||||
|
||||
## Terminology
|
||||
|
||||
Backstage is constructed out of three parts. We separate Backstage in this way
|
||||
because we see three groups of contributors that work with Backstage in three
|
||||
different ways.
|
||||
|
||||
- Core - Base functionality built by core developers in the open source project.
|
||||
- App - The app is an instance of a Backstage app that is deployed and tweaked.
|
||||
The app ties together core functionality with additional plugins. The app is
|
||||
built and maintained by app developers, usually a productivity team within a
|
||||
company.
|
||||
- Plugins - Additional functionality to make your Backstage app useful for your
|
||||
company. Plugins can be specific to a company or open sourced and reusable. At
|
||||
Spotify we have over 100 plugins built by over 50 different teams. It has been
|
||||
very powerful to get contributions from various infrastructure teams added
|
||||
into a single unified developer experience.
|
||||
|
||||
## Overview
|
||||
|
||||
The following diagram shows how Backstage might look when deployed inside a
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
---
|
||||
id: architecture-terminology
|
||||
title: Architecture terminology
|
||||
description: Documentation on Architecture terminology
|
||||
---
|
||||
|
||||
Backstage is constructed out of three parts. We separate Backstage in this way
|
||||
because we see three groups of contributors that work with Backstage in three
|
||||
different ways.
|
||||
|
||||
- Core - Base functionality built by core devs in the open source project.
|
||||
- App - The app is an instance of a Backstage app that is deployed and tweaked.
|
||||
The app ties together core functionality with additional plugins. The app is
|
||||
built and maintained by app developers, usually a productivity team within a
|
||||
company.
|
||||
- Plugins - Additional functionality to make your Backstage app useful for your
|
||||
company. Plugins can be specific to a company or open sourced and reusable. At
|
||||
Spotify we have over 100 plugins built by over 50 different teams. It has been
|
||||
very powerful to get contributions from various infrastructure teams added
|
||||
into a single unified developer experience.
|
||||
@@ -1,7 +1,8 @@
|
||||
---
|
||||
id: background
|
||||
title: The Spotify Story
|
||||
description: Documentation on Background and Story behind making of Backstage
|
||||
description: Backstage was born out of necessity at Spotify. We found that as we grew, our
|
||||
infrastructure was becoming more fragmented, our engineers less productive.
|
||||
---
|
||||
|
||||
Backstage was born out of necessity at Spotify. We found that as we grew, our
|
||||
@@ -29,3 +30,9 @@ building a new microservice using an automated template in Backstage. Create,
|
||||
maintain, and find the documentation for all that software in Backstage.
|
||||
|
||||
One place for everything. Accessible to everyone.
|
||||
|
||||
Backstage was originally built by Spotify and then donated to the CNCF.
|
||||
Backstage is currently in the Sandbox phase. Read the announcement
|
||||
[here](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox).
|
||||
|
||||
<img src="https://backstage.io/img/cncf-white.svg" width="400" />
|
||||
|
||||
@@ -13,10 +13,7 @@ description: Support and Community Details and Links
|
||||
- [FAQ](../FAQ.md) - Frequently Asked Questions
|
||||
- [Code of Conduct](../../CODE_OF_CONDUCT.md) - This is how we roll
|
||||
- [Blog](https://backstage.io/blog/) - Announcements and updates
|
||||
- [Newsletter](https://mailchi.mp/spotify/backstage-community)
|
||||
- [Newsletter](https://mailchi.mp/spotify/backstage-community) - Subscribe to
|
||||
our email newsletter
|
||||
- Give us a star ⭐️ - If you are using Backstage or think it is an interesting
|
||||
project, we would love a star ❤️
|
||||
|
||||
Or, if you are an open source developer and are interested in joining our team,
|
||||
please reach out to
|
||||
[foss-opportunities@spotify.com ](mailto:foss-opportunities@spotify.com)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: what-is-backstage
|
||||
title: What is Backstage?
|
||||
description: Backsatge is an open platform for building developer portals.
|
||||
description: Backstage is an open platform for building developer portals.
|
||||
Powered by a centralized service catalog, Backstage restores order to your microservices and infrastructure
|
||||
---
|
||||
|
||||
@@ -33,7 +33,14 @@ Out of the box, Backstage includes:
|
||||
[open source plugins](https://github.com/spotify/backstage/tree/master/plugins)
|
||||
that further expand Backstage’s customizability and functionality
|
||||
|
||||
### Benefits
|
||||
## Backstage and the CNCF
|
||||
|
||||
Backstage is a CNCF Sandbox project. Read the announcement
|
||||
[here](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox).
|
||||
|
||||
<img src="https://backstage.io/img/cncf-white.svg" width="400" />
|
||||
|
||||
## Benefits
|
||||
|
||||
- For _engineering managers_, it allows you to maintain standards and best
|
||||
practices across the organization, and can help you manage your whole tech
|
||||
|
||||
@@ -115,7 +115,7 @@ export AUTH_GITHUB_CLIENT_SECRET=xxx
|
||||
> Log into http://github.com
|
||||
> Navigate to (Settings > Developer Settings > OAuth Apps > New OAuth App)[https://github.com/settings/applications/new]
|
||||
> Set Homepage URL = http://localhost:3000
|
||||
> Set Callback URL = http://localhost:7000/auth/github
|
||||
> Set Callback URL = http://localhost:7000/api/auth/github
|
||||
> Click [Register application]
|
||||
> On the next page, copy and paste your new Client ID and Client Secret to the environment variables above, `AUTH_GITHUB_CLIENT_ID` & `AUTH_GITHUB_CLIENT_SECRET`
|
||||
> Don't forget to `source` that profile file again if necessary.
|
||||
|
||||
+1
-1
@@ -2,5 +2,5 @@
|
||||
"packages": ["packages/*", "plugins/*"],
|
||||
"npmClient": "yarn",
|
||||
"useWorkspaces": true,
|
||||
"version": "0.1.1-alpha.22"
|
||||
"version": "0.1.1-alpha.23"
|
||||
}
|
||||
|
||||
@@ -51,6 +51,7 @@ class Footer extends React.Component {
|
||||
<a href="https://mailchi.mp/spotify/backstage-community">
|
||||
Subscribe to our newsletter
|
||||
</a>
|
||||
<a href="https://www.cncf.io/sandbox-projects/">CNCF Sandbox</a>
|
||||
</div>
|
||||
<div>
|
||||
<h5>More</h5>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
"Overview": [
|
||||
"overview/what-is-backstage",
|
||||
"overview/architecture-overview",
|
||||
"overview/architecture-terminology",
|
||||
"overview/roadmap",
|
||||
"overview/vision",
|
||||
"overview/background",
|
||||
@@ -43,6 +42,7 @@
|
||||
"features/software-catalog/configuration",
|
||||
"features/software-catalog/system-model",
|
||||
"features/software-catalog/descriptor-format",
|
||||
"features/software-catalog/references",
|
||||
"features/software-catalog/well-known-annotations",
|
||||
"features/software-catalog/extending-the-model",
|
||||
"features/software-catalog/external-integrations",
|
||||
@@ -71,6 +71,7 @@
|
||||
"features/techdocs/concepts",
|
||||
"features/techdocs/architecture",
|
||||
"features/techdocs/creating-and-publishing",
|
||||
"features/techdocs/troubleshooting",
|
||||
"features/techdocs/faqs"
|
||||
]
|
||||
}
|
||||
@@ -158,7 +159,8 @@
|
||||
"architecture-decisions/adrs-adr005",
|
||||
"architecture-decisions/adrs-adr006",
|
||||
"architecture-decisions/adrs-adr007",
|
||||
"architecture-decisions/adrs-adr008"
|
||||
"architecture-decisions/adrs-adr008",
|
||||
"architecture-decisions/adrs-adr009"
|
||||
],
|
||||
"Contribute": ["../CONTRIBUTING"],
|
||||
"Support": ["overview/support"],
|
||||
|
||||
+3
-1
@@ -5,7 +5,6 @@ nav:
|
||||
- Overview:
|
||||
- What is Backstage?: 'overview/what-is-backstage.md'
|
||||
- Backstage architecture: 'overview/architecture-overview.md'
|
||||
- Architecture and terminology: 'overview/architecture-terminology.md'
|
||||
- Roadmap: 'overview/roadmap.md'
|
||||
- Vision: 'overview/vision.md'
|
||||
- The Spotify story: 'overview/background.md'
|
||||
@@ -32,6 +31,7 @@ nav:
|
||||
- Configuration: 'features/software-catalog/configuration.md'
|
||||
- System model: 'features/software-catalog/system-model.md'
|
||||
- YAML File Format: 'features/software-catalog/descriptor-format.md'
|
||||
- Entity References: 'features/software-catalog/references.md'
|
||||
- Well-known Annotations: 'features/software-catalog/well-known-annotations.md'
|
||||
- Extending the model: 'features/software-catalog/extending-the-model.md'
|
||||
- External integrations: 'features/software-catalog/external-integrations.md'
|
||||
@@ -51,6 +51,7 @@ nav:
|
||||
- Concepts: 'features/techdocs/concepts.md'
|
||||
- TechDocs Architecture: 'features/techdocs/architecture.md'
|
||||
- Creating and Publishing Documentation: 'features/techdocs/creating-and-publishing.md'
|
||||
- Troubleshooting: 'features/techdocs/troubleshooting.md'
|
||||
- FAQ: 'features/techdocs/FAQ.md'
|
||||
- Plugins:
|
||||
- Overview: 'plugins/index.md'
|
||||
@@ -104,6 +105,7 @@ nav:
|
||||
- ADR006 - Avoid React.FC and React.SFC: 'architecture-decisions/adr006-avoid-react-fc.md'
|
||||
- ADR007 - Use MSW for Network Request Mocking: 'architecture-decisions/adr007-use-msw-to-mock-service-requests.md'
|
||||
- ADR008 - Default Catalog File Name: 'architecture-decisions/adr008-default-catalog-file-name.md'
|
||||
- ADR009 - Entity References: 'architecture-decisions/adr009-entity-references.md'
|
||||
- Contribute: '../CONTRIBUTING.md'
|
||||
- Support: 'overview/support.md'
|
||||
- FAQ: FAQ.md
|
||||
|
||||
+25
-25
@@ -1,33 +1,33 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": true,
|
||||
"bundled": true,
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-api-docs": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-circleci": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-explore": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-gcp-projects": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-github-actions": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-graphiql": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-jenkins": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-kubernetes": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-newrelic": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-register-component": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-rollbar": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-tech-radar": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-api-docs": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-circleci": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-explore": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-gcp-projects": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-github-actions": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-gitops-profiles": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-graphiql": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-jenkins": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-kubernetes": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-lighthouse": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-newrelic": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-register-component": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-rollbar": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-sentry": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-tech-radar": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-welcome": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('App', () => {
|
||||
baseUrl: 'http://localhost:3003',
|
||||
},
|
||||
techdocs: {
|
||||
storageUrl: 'http://localhost:7000/techdocs/static/docs',
|
||||
storageUrl: 'http://localhost:7000/api/techdocs/static/docs',
|
||||
},
|
||||
},
|
||||
context: 'test',
|
||||
|
||||
@@ -16,11 +16,8 @@
|
||||
|
||||
import {
|
||||
errorApiRef,
|
||||
discoveryApiRef,
|
||||
UrlPatternDiscovery,
|
||||
githubAuthApiRef,
|
||||
createApiFactory,
|
||||
configApiRef,
|
||||
} from '@backstage/core';
|
||||
|
||||
import {
|
||||
@@ -38,15 +35,6 @@ import {
|
||||
} from '@roadiehq/backstage-plugin-github-pull-requests';
|
||||
|
||||
export const apis = [
|
||||
// TODO(Rugvip): migrate to use /api
|
||||
createApiFactory({
|
||||
api: discoveryApiRef,
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) =>
|
||||
UrlPatternDiscovery.compile(
|
||||
`${configApi.getString('backend.baseUrl')}/{{ pluginId }}`,
|
||||
),
|
||||
}),
|
||||
createApiFactory({
|
||||
api: graphQlBrowseApiRef,
|
||||
deps: { errorApi: errorApiRef, githubAuthApi: githubAuthApiRef },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/backend-common",
|
||||
"description": "Common functionality library for Backstage backends",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -29,9 +29,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.22",
|
||||
"@backstage/cli-common": "^0.1.1-alpha.23",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.23",
|
||||
"@types/cors": "^2.8.6",
|
||||
"@types/express": "^4.17.6",
|
||||
"compression": "^1.7.4",
|
||||
@@ -58,7 +58,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@types/compression": "^1.7.0",
|
||||
"@types/http-errors": "^1.6.3",
|
||||
"@types/morgan": "^1.9.0",
|
||||
|
||||
@@ -23,7 +23,7 @@ import { loadConfig } from '@backstage/config-loader';
|
||||
export async function loadBackendConfig() {
|
||||
const paths = findPaths(__dirname);
|
||||
const configs = await loadConfig({
|
||||
env: process.env.NODE_ENV,
|
||||
env: process.env.NODE_ENV ?? 'development',
|
||||
rootPaths: [paths.targetRoot, paths.targetDir],
|
||||
shouldReadSecrets: true,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"private": true,
|
||||
@@ -18,25 +18,26 @@
|
||||
"migrate:create": "knex migrate:make -x ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-app-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-auth-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-identity-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.23",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-app-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-auth-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-catalog-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-identity-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-kubernetes-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-proxy-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-sentry-backend": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-techdocs-backend": "^0.1.1-alpha.23",
|
||||
"@gitbeaker/node": "^23.5.0",
|
||||
"@octokit/rest": "^18.0.0",
|
||||
"dockerode": "^3.2.0",
|
||||
"example-app": "^0.1.1-alpha.22",
|
||||
"example-app": "^0.1.1-alpha.23",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"knex": "^0.21.1",
|
||||
"pg": "^8.3.0",
|
||||
"pg-connection-string": "^2.3.0",
|
||||
@@ -44,7 +45,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@types/dockerode": "^2.5.32",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5",
|
||||
|
||||
@@ -22,12 +22,14 @@
|
||||
* Happy hacking!
|
||||
*/
|
||||
|
||||
import Router from 'express-promise-router';
|
||||
import {
|
||||
createDatabaseClient,
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
getRootLogger,
|
||||
useHotMemoize,
|
||||
notFoundHandler,
|
||||
} from '@backstage/backend-common';
|
||||
import { ConfigReader, AppConfig } from '@backstage/config';
|
||||
import healthcheck from './plugins/healthcheck';
|
||||
@@ -78,19 +80,23 @@ async function main() {
|
||||
const graphqlEnv = useHotMemoize(module, () => createEnv('graphql'));
|
||||
const appEnv = useHotMemoize(module, () => createEnv('app'));
|
||||
|
||||
const apiRouter = Router();
|
||||
apiRouter.use('/catalog', await catalog(catalogEnv));
|
||||
apiRouter.use('/rollbar', await rollbar(rollbarEnv));
|
||||
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv));
|
||||
apiRouter.use('/sentry', await sentry(sentryEnv));
|
||||
apiRouter.use('/auth', await auth(authEnv, '/api/auth'));
|
||||
apiRouter.use('/identity', await identity(identityEnv));
|
||||
apiRouter.use('/techdocs', await techdocs(techdocsEnv));
|
||||
apiRouter.use('/kubernetes', await kubernetes(kubernetesEnv));
|
||||
apiRouter.use('/proxy', await proxy(proxyEnv, '/api/proxy'));
|
||||
apiRouter.use('/graphql', await graphql(graphqlEnv));
|
||||
apiRouter.use(notFoundHandler());
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
.loadConfig(configReader)
|
||||
.addRouter('', await healthcheck(healthcheckEnv))
|
||||
.addRouter('/catalog', await catalog(catalogEnv))
|
||||
.addRouter('/rollbar', await rollbar(rollbarEnv))
|
||||
.addRouter('/scaffolder', await scaffolder(scaffolderEnv))
|
||||
.addRouter('/sentry', await sentry(sentryEnv))
|
||||
.addRouter('/auth', await auth(authEnv))
|
||||
.addRouter('/identity', await identity(identityEnv))
|
||||
.addRouter('/techdocs', await techdocs(techdocsEnv))
|
||||
.addRouter('/kubernetes', await kubernetes(kubernetesEnv))
|
||||
.addRouter('/proxy', await proxy(proxyEnv, '/proxy'))
|
||||
.addRouter('/graphql', await graphql(graphqlEnv))
|
||||
.addRouter('/api', apiRouter)
|
||||
.addRouter('', await app(appEnv));
|
||||
|
||||
await service.start().catch(err => {
|
||||
|
||||
@@ -17,10 +17,9 @@
|
||||
import { createRouter } from '@backstage/plugin-auth-backend';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
database,
|
||||
config,
|
||||
}: PluginEnvironment) {
|
||||
return await createRouter({ logger, config, database });
|
||||
export default async function createPlugin(
|
||||
{ logger, database, config }: PluginEnvironment,
|
||||
basePath: string,
|
||||
) {
|
||||
return await createRouter({ logger, config, database, basePath });
|
||||
}
|
||||
|
||||
@@ -17,6 +17,9 @@
|
||||
import { createRouter } from '@backstage/plugin-kubernetes-backend';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
export default async function createPlugin({ logger }: PluginEnvironment) {
|
||||
return await createRouter({ logger });
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
config,
|
||||
}: PluginEnvironment) {
|
||||
return await createRouter({ logger, config });
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/catalog-model",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,7 +20,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@types/json-schema": "^7.0.5",
|
||||
"@types/yup": "^0.28.2",
|
||||
"json-schema": "^0.2.5",
|
||||
@@ -29,7 +29,7 @@
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/lodash": "^4.14.151",
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
GroupEntityV1alpha1Policy,
|
||||
LocationEntityV1alpha1Policy,
|
||||
TemplateEntityV1alpha1Policy,
|
||||
UserEntityV1alpha1Policy,
|
||||
} from './kinds';
|
||||
import { EntityPolicy } from './types';
|
||||
|
||||
@@ -77,6 +78,7 @@ export class EntityPolicies implements EntityPolicy {
|
||||
EntityPolicies.anyOf([
|
||||
new ComponentEntityV1alpha1Policy(),
|
||||
new GroupEntityV1alpha1Policy(),
|
||||
new UserEntityV1alpha1Policy(),
|
||||
new LocationEntityV1alpha1Policy(),
|
||||
new TemplateEntityV1alpha1Policy(),
|
||||
new ApiEntityV1alpha1Policy(),
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
export { entityMetaGeneratedFields } from './Entity';
|
||||
export type { Entity, EntityMeta } from './Entity';
|
||||
export * from './policies';
|
||||
export { parseEntityName, serializeEntityRef } from './ref';
|
||||
export {
|
||||
entityHasChanges,
|
||||
generateEntityEtag,
|
||||
|
||||
@@ -0,0 +1,362 @@
|
||||
/*
|
||||
* 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 { parseEntityName, parseEntityRef, serializeEntityRef } 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',
|
||||
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',
|
||||
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',
|
||||
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', 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',
|
||||
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({
|
||||
kind: 'a',
|
||||
namespace: 'b',
|
||||
name: 'c',
|
||||
});
|
||||
expect(parseEntityRef('b/c')).toEqual({
|
||||
kind: undefined,
|
||||
namespace: 'b',
|
||||
name: 'c',
|
||||
});
|
||||
expect(parseEntityRef('a:c')).toEqual({
|
||||
kind: 'a',
|
||||
namespace: undefined,
|
||||
name: 'c',
|
||||
});
|
||||
expect(parseEntityRef('c')).toEqual({
|
||||
kind: undefined,
|
||||
namespace: undefined,
|
||||
name: 'c',
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects bad inputs', () => {
|
||||
expect(() => parseEntityRef(null as any)).toThrow();
|
||||
expect(() => parseEntityRef(7 as any)).toThrow();
|
||||
expect(() => parseEntityRef('a:b:c')).toThrow();
|
||||
expect(() => parseEntityRef('a/b/c')).toThrow();
|
||||
expect(() => parseEntityRef('a/b:c')).toThrow();
|
||||
expect(() => parseEntityRef('a:b/c/d')).toThrow();
|
||||
expect(() => parseEntityRef('a:b/c:d')).toThrow();
|
||||
});
|
||||
|
||||
it('rejects empty parts in strings', () => {
|
||||
// one is empty
|
||||
expect(() => parseEntityRef(':b/c')).toThrow();
|
||||
expect(() => parseEntityRef('a:/c')).toThrow();
|
||||
expect(() => parseEntityRef('a:b/')).toThrow();
|
||||
// two are empty
|
||||
expect(() => parseEntityRef('a:/')).toThrow();
|
||||
expect(() => parseEntityRef(':b/')).toThrow();
|
||||
expect(() => parseEntityRef(':/c')).toThrow();
|
||||
// three are empty
|
||||
expect(() => parseEntityRef(':/')).toThrow();
|
||||
// one is left out, one empty
|
||||
expect(() => parseEntityRef('/c')).toThrow();
|
||||
expect(() => parseEntityRef('b/')).toThrow();
|
||||
expect(() => parseEntityRef(':c')).toThrow();
|
||||
expect(() => parseEntityRef('a:')).toThrow();
|
||||
// nothing at all
|
||||
expect(() => parseEntityRef('')).toThrow();
|
||||
});
|
||||
|
||||
it('rejects empty parts in compounds', () => {
|
||||
// one is empty
|
||||
expect(() =>
|
||||
parseEntityRef({ kind: '', namespace: 'b', name: 'c' }),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
parseEntityRef({ kind: 'a', namespace: '', name: 'c' }),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
parseEntityRef({ kind: 'a', namespace: 'b', name: '' }),
|
||||
).toThrow();
|
||||
// two are empty
|
||||
expect(() =>
|
||||
parseEntityRef({ kind: '', namespace: '', name: 'c' }),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
parseEntityRef({ kind: '', namespace: 'b', name: '' }),
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
parseEntityRef({ kind: 'a', namespace: '', name: '' }),
|
||||
).toThrow();
|
||||
// three are empty
|
||||
expect(() =>
|
||||
parseEntityRef({ kind: '', namespace: '', name: '' }),
|
||||
).toThrow();
|
||||
// one is left out, one empty
|
||||
expect(() => parseEntityRef({ namespace: '', name: 'c' })).toThrow();
|
||||
expect(() => parseEntityRef({ namespace: 'b', name: '' })).toThrow();
|
||||
expect(() => parseEntityRef({ kind: '', name: 'c' })).toThrow();
|
||||
expect(() => parseEntityRef({ kind: 'a', name: '' })).toThrow();
|
||||
// nothing at all
|
||||
expect(() => parseEntityRef({} as any)).toThrow();
|
||||
});
|
||||
|
||||
it('adds defaults where necessary to strings', () => {
|
||||
expect(
|
||||
parseEntityRef('a:b/c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'a', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityRef('b/c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'x', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityRef('a:c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'a', namespace: 'y', name: 'c' });
|
||||
expect(
|
||||
parseEntityRef('c', { defaultKind: 'x', defaultNamespace: 'y' }),
|
||||
).toEqual({ kind: 'x', namespace: 'y', name: 'c' });
|
||||
});
|
||||
|
||||
it('adds defaults where necessary to compounds', () => {
|
||||
expect(
|
||||
parseEntityRef(
|
||||
{ kind: 'a', namespace: 'b', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'a', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityRef(
|
||||
{ namespace: 'b', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'x', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
parseEntityRef(
|
||||
{ kind: 'a', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'a', namespace: 'y', name: 'c' });
|
||||
expect(
|
||||
parseEntityRef(
|
||||
{ name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toEqual({ kind: 'x', namespace: 'y', name: 'c' });
|
||||
// empty strings are errors, not defaults
|
||||
expect(() =>
|
||||
parseEntityRef(
|
||||
{ kind: '', namespace: 'b', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toThrow(/kind/);
|
||||
expect(() =>
|
||||
parseEntityRef(
|
||||
{ kind: 'a', namespace: '', name: 'c' },
|
||||
{ defaultKind: 'x', defaultNamespace: 'y' },
|
||||
),
|
||||
).toThrow(/namespace/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('serializeEntityRef', () => {
|
||||
it('handles partials', () => {
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a', namespace: 'b', name: 'c' }),
|
||||
).toEqual('a:b/c');
|
||||
expect(serializeEntityRef({ namespace: 'b', name: 'c' })).toEqual('b/c');
|
||||
expect(serializeEntityRef({ kind: 'a', name: 'c' })).toEqual('a:c');
|
||||
expect(serializeEntityRef({ name: 'c' })).toEqual('c');
|
||||
});
|
||||
|
||||
it('picks the least complex form', () => {
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a', namespace: 'b', name: 'c' }),
|
||||
).toEqual('a:b/c');
|
||||
expect(serializeEntityRef({ namespace: 'b', name: 'c' })).toEqual('b/c');
|
||||
expect(serializeEntityRef({ kind: 'a', name: 'c' })).toEqual('a:c');
|
||||
expect(serializeEntityRef({ name: 'c' })).toEqual('c');
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a:x', namespace: 'b', name: 'c' }),
|
||||
).toEqual({ kind: 'a:x', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a/x', namespace: 'b', name: 'c' }),
|
||||
).toEqual({ kind: 'a/x', namespace: 'b', name: 'c' });
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a', namespace: 'b:x', name: 'c' }),
|
||||
).toEqual({ kind: 'a', namespace: 'b:x', name: 'c' });
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a', namespace: 'b/x', name: 'c' }),
|
||||
).toEqual({ kind: 'a', namespace: 'b/x', name: 'c' });
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a', namespace: 'b', name: 'c:x' }),
|
||||
).toEqual({ kind: 'a', namespace: 'b', name: 'c:x' });
|
||||
expect(
|
||||
serializeEntityRef({ kind: 'a', namespace: 'b', name: 'c/x' }),
|
||||
).toEqual({ kind: 'a', namespace: 'b', name: 'c/x' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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 { EntityName, EntityRef } from '../types';
|
||||
|
||||
/**
|
||||
* The context of defaults that entity reference parsing happens within.
|
||||
*/
|
||||
type EntityRefContext = {
|
||||
/** The default kind, if none is given in the reference */
|
||||
defaultKind?: string;
|
||||
/** The default namespace, if none is given in the reference */
|
||||
defaultNamespace?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parses an entity reference, either on string or compound form, and always
|
||||
* returns a complete entity name including kind, namespace and name.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @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: EntityRef,
|
||||
context: EntityRefContext = {},
|
||||
): EntityName {
|
||||
const { kind, namespace, name } = parseEntityRef(ref, {
|
||||
defaultNamespace: 'default',
|
||||
...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.
|
||||
*
|
||||
* The options object can contain default values for the kind and namespace,
|
||||
* that will be used if the input reference did not specify any.
|
||||
*
|
||||
* @param ref The reference to parse
|
||||
* @param context The context of defaults that the parsing happens within
|
||||
* @returns The compound form of the reference
|
||||
*/
|
||||
export function parseEntityRef(
|
||||
ref: EntityRef,
|
||||
context?: { defaultKind: string },
|
||||
): {
|
||||
kind: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
};
|
||||
export function parseEntityRef(
|
||||
ref: EntityRef,
|
||||
context?: { defaultNamespace: string },
|
||||
): {
|
||||
kind?: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
};
|
||||
export function parseEntityRef(
|
||||
ref: EntityRef,
|
||||
context?: { defaultKind: string; defaultNamespace: string },
|
||||
): {
|
||||
kind: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
};
|
||||
export function parseEntityRef(
|
||||
ref: EntityRef,
|
||||
context: EntityRefContext = {},
|
||||
): {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
} {
|
||||
if (!ref) {
|
||||
throw new Error(`Entity reference must not be empty`);
|
||||
}
|
||||
|
||||
if (typeof ref === 'string') {
|
||||
const match = /^([^:/]+:)?([^:/]+\/)?([^:/]+)$/.exec(ref.trim());
|
||||
if (!match) {
|
||||
throw new Error(
|
||||
`Entity reference "${ref}" was not on the form [<kind>:][<namespace>/]<name>`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
kind: match[1]?.slice(0, -1) ?? context.defaultKind,
|
||||
namespace: match[2]?.slice(0, -1) ?? context.defaultNamespace,
|
||||
name: match[3],
|
||||
};
|
||||
}
|
||||
|
||||
const { kind, namespace, name } = ref;
|
||||
if (kind === '') {
|
||||
throw new Error('Entity reference kinds must not be empty');
|
||||
} else if (namespace === '') {
|
||||
throw new Error('Entity reference namespaces must not be empty');
|
||||
} else if (!name) {
|
||||
throw new Error('Entity references must contain a name');
|
||||
}
|
||||
|
||||
return {
|
||||
kind: kind ?? context.defaultKind,
|
||||
namespace: namespace ?? context.defaultNamespace,
|
||||
name,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an entity reference or name, and outputs an entity reference on the
|
||||
* most compact form possible. I.e. if the parts do not contain any
|
||||
* special/reserved characters, it outputs the string form, otherwise it
|
||||
* outputs the compound form.
|
||||
*
|
||||
* @param ref The reference to serialize
|
||||
* @returns The same reference on either string or compound form
|
||||
*/
|
||||
export function serializeEntityRef(ref: {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
}): EntityRef {
|
||||
const { kind, namespace, name } = ref;
|
||||
if (
|
||||
kind?.includes(':') ||
|
||||
kind?.includes('/') ||
|
||||
namespace?.includes(':') ||
|
||||
namespace?.includes('/') ||
|
||||
name.includes(':') ||
|
||||
name.includes('/')
|
||||
) {
|
||||
return { kind, namespace, name };
|
||||
}
|
||||
|
||||
return `${kind ? `${kind}:` : ''}${namespace ? `${namespace}/` : ''}${name}`;
|
||||
}
|
||||
@@ -18,5 +18,5 @@ export * from './entity';
|
||||
export { EntityPolicies } from './EntityPolicies';
|
||||
export * from './kinds';
|
||||
export * from './location';
|
||||
export type { EntityPolicy, JSONSchema } from './types';
|
||||
export type { EntityName, EntityPolicy, EntityRef, JSONSchema } from './types';
|
||||
export * from './validation';
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* 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 { EntityPolicy } from '../types';
|
||||
import {
|
||||
UserEntityV1alpha1,
|
||||
UserEntityV1alpha1Policy,
|
||||
} from './UserEntityV1alpha1';
|
||||
|
||||
describe('UserV1alpha1Policy', () => {
|
||||
let entity: UserEntityV1alpha1;
|
||||
let policy: EntityPolicy;
|
||||
|
||||
beforeEach(() => {
|
||||
entity = {
|
||||
apiVersion: 'backstage.io/v1alpha1',
|
||||
kind: 'User',
|
||||
metadata: {
|
||||
name: 'doe',
|
||||
},
|
||||
spec: {
|
||||
profile: {
|
||||
displayName: 'John Doe',
|
||||
email: 'john@doe.org',
|
||||
picture: 'https://doe.org/john.jpeg',
|
||||
},
|
||||
memberOf: ['team-a', 'developers'],
|
||||
},
|
||||
};
|
||||
policy = new UserEntityV1alpha1Policy();
|
||||
});
|
||||
|
||||
it('happy path: accepts valid data', async () => {
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
// root
|
||||
|
||||
it('silently accepts v1beta1 as well', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta1';
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('rejects unknown apiVersion', async () => {
|
||||
(entity as any).apiVersion = 'backstage.io/v1beta0';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/apiVersion/);
|
||||
});
|
||||
|
||||
it('rejects unknown kind', async () => {
|
||||
(entity as any).kind = 'Wizard';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/kind/);
|
||||
});
|
||||
|
||||
it('spec accepts unknown additional fields', async () => {
|
||||
(entity as any).spec.foo = 'data';
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
// profile
|
||||
|
||||
it('accepts missing profile', async () => {
|
||||
delete (entity as any).spec.profile;
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('rejects wrong profile', async () => {
|
||||
(entity as any).spec.profile = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/profile/);
|
||||
});
|
||||
|
||||
it('profile accepts missing displayName', async () => {
|
||||
delete (entity as any).spec.profile.displayName;
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('profile rejects wrong displayName', async () => {
|
||||
(entity as any).spec.profile.displayName = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/displayName/);
|
||||
});
|
||||
|
||||
it('profile rejects empty displayName', async () => {
|
||||
(entity as any).spec.profile.displayName = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/displayName/);
|
||||
});
|
||||
|
||||
it('profile accepts missing email', async () => {
|
||||
delete (entity as any).spec.profile.email;
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('profile rejects wrong email', async () => {
|
||||
(entity as any).spec.profile.email = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/email/);
|
||||
});
|
||||
|
||||
it('profile rejects empty email', async () => {
|
||||
(entity as any).spec.profile.email = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/email/);
|
||||
});
|
||||
|
||||
it('profile accepts missing picture', async () => {
|
||||
delete (entity as any).spec.profile.picture;
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
it('profile rejects wrong picture', async () => {
|
||||
(entity as any).spec.profile.picture = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/picture/);
|
||||
});
|
||||
|
||||
it('profile rejects empty picture', async () => {
|
||||
(entity as any).spec.profile.picture = '';
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/picture/);
|
||||
});
|
||||
|
||||
it('profile accepts unknown additional fields', async () => {
|
||||
(entity as any).spec.profile.foo = 'data';
|
||||
await expect(policy.enforce(entity)).resolves.toBe(entity);
|
||||
});
|
||||
|
||||
// memberOf
|
||||
|
||||
it('rejects missing memberOf', async () => {
|
||||
delete (entity as any).spec.memberOf;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/memberOf/);
|
||||
});
|
||||
|
||||
it('rejects wrong memberOf', async () => {
|
||||
(entity as any).spec.memberOf = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/memberOf/);
|
||||
});
|
||||
|
||||
it('rejects wrong memberOf item', async () => {
|
||||
(entity as any).spec.memberOf[0] = 7;
|
||||
await expect(policy.enforce(entity)).rejects.toThrow(/memberOf/);
|
||||
});
|
||||
});
|
||||
@@ -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 * as yup from 'yup';
|
||||
import type { Entity } from '../entity/Entity';
|
||||
import type { EntityPolicy } from '../types';
|
||||
|
||||
const API_VERSION = ['backstage.io/v1alpha1', 'backstage.io/v1beta1'] as const;
|
||||
const KIND = 'User' as const;
|
||||
|
||||
export interface UserEntityV1alpha1 extends Entity {
|
||||
apiVersion: typeof API_VERSION[number];
|
||||
kind: typeof KIND;
|
||||
spec: {
|
||||
profile?: {
|
||||
displayName?: string;
|
||||
email?: string;
|
||||
picture?: string;
|
||||
};
|
||||
memberOf: string[];
|
||||
};
|
||||
}
|
||||
|
||||
export class UserEntityV1alpha1Policy implements EntityPolicy {
|
||||
private schema: yup.Schema<any>;
|
||||
|
||||
constructor() {
|
||||
this.schema = yup.object<Partial<UserEntityV1alpha1>>({
|
||||
apiVersion: yup.string().required().oneOf(API_VERSION),
|
||||
kind: yup.string().required().equals([KIND]),
|
||||
spec: yup
|
||||
.object({
|
||||
profile: yup
|
||||
.object({
|
||||
displayName: yup.string().min(1).notRequired(),
|
||||
email: yup.string().min(1).notRequired(),
|
||||
picture: yup.string().min(1).notRequired(),
|
||||
})
|
||||
.notRequired(),
|
||||
memberOf: yup.array(yup.string()).required(),
|
||||
})
|
||||
.required(),
|
||||
});
|
||||
}
|
||||
|
||||
async enforce(envelope: Entity): Promise<Entity> {
|
||||
return await this.schema.validate(envelope, { strict: true });
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,11 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { ApiEntityV1alpha1Policy } from './ApiEntityV1alpha1';
|
||||
export type {
|
||||
ApiEntityV1alpha1 as ApiEntity,
|
||||
ApiEntityV1alpha1,
|
||||
} from './ApiEntityV1alpha1';
|
||||
export { ComponentEntityV1alpha1Policy } from './ComponentEntityV1alpha1';
|
||||
export type {
|
||||
ComponentEntityV1alpha1 as ComponentEntity,
|
||||
@@ -34,8 +39,8 @@ export type {
|
||||
TemplateEntityV1alpha1 as TemplateEntity,
|
||||
TemplateEntityV1alpha1,
|
||||
} from './TemplateEntityV1alpha1';
|
||||
export { ApiEntityV1alpha1Policy } from './ApiEntityV1alpha1';
|
||||
export { UserEntityV1alpha1Policy } from './UserEntityV1alpha1';
|
||||
export type {
|
||||
ApiEntityV1alpha1 as ApiEntity,
|
||||
ApiEntityV1alpha1,
|
||||
} from './ApiEntityV1alpha1';
|
||||
UserEntityV1alpha1 as UserEntity,
|
||||
UserEntityV1alpha1,
|
||||
} from './UserEntityV1alpha1';
|
||||
|
||||
@@ -34,3 +34,29 @@ export type EntityPolicy = {
|
||||
};
|
||||
|
||||
export type JSONSchema = JSONSchema7 & { [key in string]?: JsonValue };
|
||||
|
||||
/**
|
||||
* A complete entity name, with the full kind-namespace-name triplet.
|
||||
*/
|
||||
export type EntityName = {
|
||||
kind: string;
|
||||
namespace: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* A reference by name to an entity, either as a compact string representation,
|
||||
* or as a compound reference structure.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
export type EntityRef =
|
||||
| string
|
||||
| {
|
||||
kind?: string;
|
||||
namespace?: string;
|
||||
name: string;
|
||||
};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli-common",
|
||||
"description": "Common functionality used by cli, backend, and create-app",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -28,9 +28,9 @@
|
||||
"backstage-cli": "bin/backstage-cli"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.22",
|
||||
"@backstage/cli-common": "^0.1.1-alpha.23",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.23",
|
||||
"@hot-loader/react-dom": "^16.13.0",
|
||||
"@lerna/package-graph": "^3.18.5",
|
||||
"@lerna/project": "^3.18.0",
|
||||
|
||||
@@ -231,7 +231,11 @@ export default async (cmd: Command) => {
|
||||
const privatePackage = cmd.private === false ? false : true;
|
||||
const isMonoRepo = await fs.pathExists(paths.resolveTargetRoot('lerna.json'));
|
||||
const appPackage = paths.resolveTargetRoot('packages/app');
|
||||
const templateDir = paths.resolveOwn('templates/default-plugin');
|
||||
const templateDir = paths.resolveOwn(
|
||||
cmd.backend
|
||||
? 'templates/default-backend-plugin'
|
||||
: 'templates/default-plugin',
|
||||
);
|
||||
const tempDir = resolvePath(os.tmpdir(), answers.id);
|
||||
const pluginDir = isMonoRepo
|
||||
? paths.resolveTargetRoot('plugins', answers.id)
|
||||
@@ -252,6 +256,7 @@ export default async (cmd: Command) => {
|
||||
await createTemporaryPluginFolder(tempDir);
|
||||
|
||||
Task.section('Preparing files');
|
||||
|
||||
await templatingTask(templateDir, tempDir, {
|
||||
...answers,
|
||||
version,
|
||||
@@ -267,7 +272,7 @@ export default async (cmd: Command) => {
|
||||
Task.section('Building the plugin');
|
||||
await buildPlugin(pluginDir);
|
||||
|
||||
if (await fs.pathExists(appPackage)) {
|
||||
if ((await fs.pathExists(appPackage)) && !cmd.backend) {
|
||||
Task.section('Adding plugin as dependency in app');
|
||||
await addPluginDependencyToApp(paths.targetRoot, name, version);
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ export function registerCommands(program: CommanderStatic) {
|
||||
|
||||
program
|
||||
.command('create-plugin')
|
||||
.option(
|
||||
'--backend',
|
||||
'Create plugin with the backend dependencies as default',
|
||||
)
|
||||
.description('Creates a new plugin in the current repository')
|
||||
.option('--scope <scope>', 'NPM scope')
|
||||
.option('--npm-registry <URL>', 'NPM registry URL')
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
# {{id}}
|
||||
|
||||
Welcome to the {{id}} backend plugin!
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
|
||||
## Getting started
|
||||
|
||||
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn
|
||||
start` in the root directory, and then navigating to [/{{id}}](http://localhost:3000/{{id}}).
|
||||
|
||||
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
||||
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
||||
It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory.
|
||||
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"name": "{{name}}",
|
||||
"version": "{{version}}",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
{{#if privatePackage}} "private": {{privatePackage}},
|
||||
{{/if}}
|
||||
"publishConfig": {
|
||||
{{#if npmRegistry}} "registry": "{{npmRegistry}}",
|
||||
{{/if}}
|
||||
"access": "public",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "backstage-cli backend:dev",
|
||||
"build": "backstage-cli backend:build",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^{{version}}",
|
||||
"@backstage/config": "^{{version}}",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"winston": "^3.2.1",
|
||||
"node-fetch": "^2.6.1",
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^{{version}}",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"supertest": "^4.0.2",
|
||||
"msw": "^0.20.5"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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 * from './service/router';
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* 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 { getRootLogger } from '@backstage/backend-common';
|
||||
import yn from 'yn';
|
||||
import { startStandaloneServer } from './service/standaloneServer';
|
||||
|
||||
const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 7000;
|
||||
const enableCors = yn(process.env.PLUGIN_CORS, { default: false });
|
||||
const logger = getRootLogger();
|
||||
|
||||
startStandaloneServer({ port, enableCors, logger }).catch(err => {
|
||||
logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('SIGINT', () => {
|
||||
logger.info('CTRL+C pressed; exiting.');
|
||||
process.exit(0);
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 { getVoidLogger } from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
import request from 'supertest';
|
||||
|
||||
import { createRouter } from './router';
|
||||
|
||||
describe('createRouter', () => {
|
||||
let app: express.Express;
|
||||
|
||||
beforeAll(async () => {
|
||||
const router = await createRouter({
|
||||
logger: getVoidLogger(),
|
||||
});
|
||||
app = express().use(router);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
describe('GET /health', () => {
|
||||
it('returns ok', async () => {
|
||||
const response = await request(app).get('/health');
|
||||
|
||||
expect(response.status).toEqual(200);
|
||||
expect(response.body).toEqual({ status: 'ok' });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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 { errorHandler } from '@backstage/backend-common';
|
||||
import express from 'express';
|
||||
import Router from 'express-promise-router';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
export interface RouterOptions {
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { logger } = options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
|
||||
router.get('/health', (_, response) => {
|
||||
logger.info('PONG!');
|
||||
response.send({ status: 'ok' });
|
||||
});
|
||||
router.use(errorHandler());
|
||||
return router;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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 { createServiceBuilder } from '@backstage/backend-common';
|
||||
import { Server } from 'http';
|
||||
import { Logger } from 'winston';
|
||||
import { createRouter } from './router';
|
||||
|
||||
export interface ServerOptions {
|
||||
port: number;
|
||||
enableCors: boolean;
|
||||
logger: Logger;
|
||||
}
|
||||
|
||||
export async function startStandaloneServer(
|
||||
options: ServerOptions,
|
||||
): Promise<Server> {
|
||||
const logger = options.logger.child({ service: '{{id}}-backend' });
|
||||
logger.debug('Starting application server...');
|
||||
const router = await createRouter({
|
||||
logger,
|
||||
});
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
.enableCors({ origin: 'http://localhost:3000' })
|
||||
.addRouter('/{{id}}', router);
|
||||
|
||||
return await service.start().catch(err => {
|
||||
logger.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
|
||||
module.hot?.accept();
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 {};
|
||||
global.fetch = require('node-fetch');
|
||||
@@ -41,7 +41,8 @@
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
+21
-4
@@ -1,18 +1,35 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import mockFetch from 'jest-fetch-mock';
|
||||
import ExampleComponent from './ExampleComponent';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
|
||||
|
||||
describe('ExampleComponent', () => {
|
||||
const server = setupServer();
|
||||
// Enable API mocking before tests.
|
||||
beforeAll(() => server.listen())
|
||||
|
||||
// Reset any runtime request handlers we may add during the tests.
|
||||
afterEach(() => server.resetHandlers())
|
||||
|
||||
// Disable API mocking after the tests are done.
|
||||
afterAll(() => server.close())
|
||||
|
||||
// setup mock response
|
||||
beforeEach(() => {
|
||||
server.use(rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))))
|
||||
})
|
||||
|
||||
it('should render', () => {
|
||||
mockFetch.mockResponse(() => new Promise(() => {}));
|
||||
const rendered = render(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<ExampleComponent />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument();
|
||||
);
|
||||
expect(rendered.getByText('Welcome to {{ id }}!')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
+16
-2
@@ -1,11 +1,25 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import mockFetch from 'jest-fetch-mock';
|
||||
import ExampleFetchComponent from './ExampleFetchComponent';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
|
||||
describe('ExampleFetchComponent', () => {
|
||||
const server = setupServer();
|
||||
// Enable API mocking before tests.
|
||||
beforeAll(() => server.listen())
|
||||
|
||||
// Reset any runtime request handlers we may add during the tests.
|
||||
afterEach(() => server.resetHandlers())
|
||||
|
||||
// Disable API mocking after the tests are done.
|
||||
afterAll(() => server.close())
|
||||
|
||||
// setup mock response
|
||||
beforeEach(() => {
|
||||
server.use(rest.get('https://randomuser.me/*', (_, res, ctx) => res(ctx.status(200), ctx.delay(2000), ctx.json({}))))
|
||||
})
|
||||
it('should render', async () => {
|
||||
mockFetch.mockResponse(() => new Promise(() => {}));
|
||||
const rendered = render(<ExampleFetchComponent />);
|
||||
expect(await rendered.findByTestId('progress')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
require('jest-fetch-mock').enableMocks();
|
||||
global.fetch = require('node-fetch');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/config-loader",
|
||||
"description": "Config loading functionality used by Backstage backend, and CLI",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -30,7 +30,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"fs-extra": "^9.0.0",
|
||||
"yaml": "^1.9.2",
|
||||
"yup": "^0.29.1"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/config",
|
||||
"description": "Config API used by Backstage core, backend, and CLI",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-api",
|
||||
"description": "Internal Core API used by Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,8 +29,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@types/react": "^16.9",
|
||||
@@ -41,8 +41,8 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core",
|
||||
"description": "Core API used by Backstage plugins and apps",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,9 +29,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/core-api": "0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@backstage/core-api": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -54,8 +54,8 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/create-app",
|
||||
"description": "Create app package for Backstage",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -27,7 +27,7 @@
|
||||
"start": "nodemon --"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli-common": "^0.1.1-alpha.22",
|
||||
"@backstage/cli-common": "^0.1.1-alpha.23",
|
||||
"chalk": "^4.0.0",
|
||||
"commander": "^6.1.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
|
||||
@@ -44,8 +44,8 @@ proxy:
|
||||
changeOrigin: true
|
||||
|
||||
techdocs:
|
||||
storageUrl: http://localhost:7000/techdocs/static/docs
|
||||
requestUrl: http://localhost:7000/techdocs/docs
|
||||
storageUrl: http://localhost:7000/api/techdocs/static/docs
|
||||
requestUrl: http://localhost:7000/api/techdocs/docs
|
||||
generators:
|
||||
techdocs: 'docker'
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('App', () => {
|
||||
app: { title: 'Test' },
|
||||
backend: { baseUrl: 'http://localhost:7000' },
|
||||
techdocs: {
|
||||
storageUrl: 'http://localhost:7000/techdocs/static/docs',
|
||||
storageUrl: 'http://localhost:7000/api/techdocs/static/docs',
|
||||
},
|
||||
},
|
||||
context: 'test',
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
"@gitbeaker/node": "^23.5.0",
|
||||
"dockerode": "^3.2.0",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"knex": "^0.21.1",
|
||||
{{#if dbTypePG}}
|
||||
"pg": "^8.3.0",
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
* Happy hacking!
|
||||
*/
|
||||
|
||||
import Router from 'express-promise-router';
|
||||
import {
|
||||
createDatabaseClient,
|
||||
createServiceBuilder,
|
||||
loadBackendConfig,
|
||||
getRootLogger,
|
||||
useHotMemoize,
|
||||
notFoundHandler,
|
||||
} from '@backstage/backend-common';
|
||||
import { ConfigReader, AppConfig } from '@backstage/config';
|
||||
import auth from './plugins/auth';
|
||||
@@ -51,14 +53,18 @@ async function main() {
|
||||
const proxyEnv = useHotMemoize(module, () => createEnv('proxy'));
|
||||
const techdocsEnv = useHotMemoize(module, () => createEnv('techdocs'));
|
||||
|
||||
const apiRouter = Router();
|
||||
apiRouter.use('/catalog', await catalog(catalogEnv))
|
||||
apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv))
|
||||
apiRouter.use('/auth', await auth(authEnv))
|
||||
apiRouter.use('/identity', await identity(identityEnv))
|
||||
apiRouter.use('/techdocs', await techdocs(techdocsEnv))
|
||||
apiRouter.use('/proxy', await proxy(proxyEnv, '/api/proxy'))
|
||||
apiRouter.use(notFoundHandler());
|
||||
|
||||
const service = createServiceBuilder(module)
|
||||
.loadConfig(configReader)
|
||||
.addRouter('/catalog', await catalog(catalogEnv))
|
||||
.addRouter('/scaffolder', await scaffolder(scaffolderEnv))
|
||||
.addRouter('/auth', await auth(authEnv))
|
||||
.addRouter('/identity', await identity(identityEnv))
|
||||
.addRouter('/techdocs', await techdocs(techdocsEnv))
|
||||
.addRouter('/proxy', await proxy(proxyEnv, '/proxy'));
|
||||
.addRouter('/api', apiRouter)
|
||||
|
||||
await service.start().catch(err => {
|
||||
console.log(err);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/dev-utils",
|
||||
"description": "Utilities for developing Backstage plugins.",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,10 +29,10 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "docgen",
|
||||
"description": "Tool for generating API Documentation for itself",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": true,
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "e2e-test",
|
||||
"description": "E2E test for verifying Backstage packages",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": true,
|
||||
"homepage": "https://backstage.io",
|
||||
"repository": {
|
||||
@@ -21,7 +21,7 @@
|
||||
"test:e2e": "yarn start"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli-common": "^0.1.1-alpha.22",
|
||||
"@backstage/cli-common": "^0.1.1-alpha.23",
|
||||
"@types/fs-extra": "^9.0.1",
|
||||
"@types/node": "^13.7.2",
|
||||
"fs-extra": "^9.0.0",
|
||||
|
||||
@@ -55,6 +55,9 @@ async function main() {
|
||||
print('Creating a Backstage Plugin');
|
||||
const pluginName = await createPlugin('test-plugin', appDir);
|
||||
|
||||
print('Creating a Backstage Backend Plugin');
|
||||
await createPlugin('test-backend-plugin', appDir, ['--backend']);
|
||||
|
||||
print('Starting the app');
|
||||
await testAppServe(pluginName, appDir);
|
||||
|
||||
@@ -238,8 +241,12 @@ async function overrideModuleResolutions(appDir: string, workspaceDir: string) {
|
||||
/**
|
||||
* Uses create-plugin command to create a new plugin in the app
|
||||
*/
|
||||
async function createPlugin(pluginName: string, appDir: string) {
|
||||
const child = spawnPiped(['yarn', 'create-plugin'], {
|
||||
async function createPlugin(
|
||||
pluginName: string,
|
||||
appDir: string,
|
||||
options: string[] = [],
|
||||
) {
|
||||
const child = spawnPiped(['yarn', 'create-plugin', ...options], {
|
||||
cwd: appDir,
|
||||
});
|
||||
|
||||
@@ -384,7 +391,7 @@ async function testBackendStart(appDir: string, isPostgres: boolean) {
|
||||
|
||||
print('Try to fetch entities from the backend');
|
||||
// Try fetch entities, should be ok
|
||||
await fetch('http://localhost:7000/catalog/entities').then(res =>
|
||||
await fetch('http://localhost:7000/api/catalog/entities').then(res =>
|
||||
res.json(),
|
||||
);
|
||||
print('Entities fetched successfully');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "storybook",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"description": "Storybook build for core package",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
@@ -14,7 +14,7 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/theme": "^0.1.1-alpha.22"
|
||||
"@backstage/theme": "^0.1.1-alpha.23"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^6.0.21",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@techdocs/cli",
|
||||
"description": "CLI for running TechDocs locally.",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -44,7 +44,7 @@
|
||||
"ext": "ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"commander": "^6.1.0",
|
||||
"fs-extra": "^9.0.1",
|
||||
"http-proxy": "^1.18.1",
|
||||
|
||||
@@ -82,6 +82,10 @@ Extensions:
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.0.9
|
||||
|
||||
- Change development status to 3 - Alpha
|
||||
|
||||
### 0.0.8
|
||||
|
||||
- Superfences and Codehilite doesn't work very well together (squidfunk/mkdocs-material#1604) so therefore the codehilite extension is replaced by pymdownx.highlight
|
||||
|
||||
@@ -14,13 +14,19 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
from setuptools import setup, find_packages
|
||||
from os import path
|
||||
|
||||
# read the contents of the README file in the current directory
|
||||
this_dir = path.abspath(path.dirname(__file__))
|
||||
with open(path.join(this_dir, "README.md"), encoding="utf-8") as file:
|
||||
long_description = file.read()
|
||||
|
||||
setup(
|
||||
name="mkdocs-techdocs-core",
|
||||
version="0.0.8",
|
||||
version="0.0.9",
|
||||
description="A Mkdocs package that contains TechDocs defaults",
|
||||
long_description="",
|
||||
long_description=long_description,
|
||||
long_description_content_type="text/markdown",
|
||||
keywords="mkdocs",
|
||||
url="https://github.com/spotify/backstage",
|
||||
author="TechDocs Core",
|
||||
@@ -37,7 +43,7 @@ setup(
|
||||
"pymdown-extensions==7.1",
|
||||
],
|
||||
classifiers=[
|
||||
"Development Status :: 1 - Planning",
|
||||
"Development Status :: 3 - Alpha",
|
||||
"Intended Audience :: Developers",
|
||||
"Intended Audience :: Information Technology",
|
||||
"License :: OSI Approved :: Apache Software License",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/test-utils-core",
|
||||
"description": "Utilities to test Backstage core",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/test-utils",
|
||||
"description": "Utilities to test Backstage plugins and apps.",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,10 +29,10 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/core-api": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/core-api": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils-core": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/theme",
|
||||
"description": "material-ui theme for use with Backstage.",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -31,7 +31,7 @@
|
||||
"@material-ui/core": "^4.11.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22"
|
||||
"@backstage/cli": "^0.1.1-alpha.23"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -82,12 +82,12 @@ export const darkTheme = createTheme({
|
||||
default: '#333333',
|
||||
},
|
||||
status: {
|
||||
ok: '#1DB954',
|
||||
warning: '#FF9800',
|
||||
error: '#E22134',
|
||||
running: '#2E77D0',
|
||||
pending: '#FFED51',
|
||||
aborted: '#757575',
|
||||
ok: '#71CF88',
|
||||
warning: '#FFB84D',
|
||||
error: '#F84C55',
|
||||
running: '#3488E3',
|
||||
pending: '#FEF071',
|
||||
aborted: '#9E9E9E',
|
||||
},
|
||||
bursts: {
|
||||
fontColor: '#FEFEFE',
|
||||
@@ -107,7 +107,7 @@ export const darkTheme = createTheme({
|
||||
},
|
||||
border: '#E6E6E6',
|
||||
textContrast: '#FFFFFF',
|
||||
textVerySubtle: '#DDD',
|
||||
textVerySubtle: '#ececec',
|
||||
textSubtle: '#EEEEEE',
|
||||
highlight: '#FFFBCC',
|
||||
errorBackground: '#FFEBEE',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-api-docs",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,10 +20,10 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@kyma-project/asyncapi-react": "^0.11.0",
|
||||
"@material-icons/font": "^1.0.2",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
@@ -39,9 +39,9 @@
|
||||
"swagger-ui-react": "^3.31.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
@@ -49,7 +49,9 @@
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react": "^16.9",
|
||||
"@types/swagger-ui-react": "^3.23.3",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-app-backend",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,8 +20,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.22",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.23",
|
||||
"@backstage/config-loader": "^0.1.1-alpha.23",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
@@ -30,9 +30,9 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"msw": "^0.19.5",
|
||||
"msw": "^0.20.5",
|
||||
"supertest": "^4.0.2"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -34,8 +34,8 @@ Follow this link, [Create new OAuth App](https://github.com/settings/application
|
||||
1. Set Application Name to `backstage-dev` or something along those lines.
|
||||
1. You can set the Homepage URL to whatever you want to.
|
||||
1. The Authorization Callback URL should match the redirect URI set in Backstage.
|
||||
1. Set this to `http://localhost:7000/auth/github` for local development.
|
||||
1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/auth/github` for non-local deployments.
|
||||
1. Set this to `http://localhost:7000/api/auth/github` for local development.
|
||||
1. Set this to `http://{APP_FQDN}:{APP_BACKEND_PORT}/api/auth/github` for non-local deployments.
|
||||
|
||||
```bash
|
||||
export AUTH_GITHUB_CLIENT_ID=x
|
||||
@@ -78,14 +78,14 @@ export AUTH_AUTH0_CLIENT_SECRET=x
|
||||
|
||||
#### Creating an Azure AD App Registration
|
||||
|
||||
An Azure AD App Registration is required to be able to sign in using Azure AD and the Microsoft Graph API.
|
||||
An Azure AD App Registration is required to be able to sign in using Azure AD and the Microsoft Graph API.
|
||||
Click [here](https://portal.azure.com/#blade/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/RegisteredApps) to create a new one.
|
||||
|
||||
- Click on the `New Registration` button.
|
||||
- Give the app a name. e.g. `backstage-dev`
|
||||
- Select `Accounts in this organizational directory only` under supported account types.
|
||||
- Enter the callback URL for your backstage backend instance:
|
||||
- For local development, this is likely `http://localhost:7000/auth/microsoft/handler/frame`
|
||||
- For local development, this is likely `http://localhost:7000/api/auth/microsoft/handler/frame`
|
||||
- For non-local deployments, this will be `https://{APP_FQDN}:{APP_BACKEND_PORT}/auth/microsoft/handler/frame`
|
||||
- Click `Register`.
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-auth-backend",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,8 +20,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.23",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@types/express": "^4.17.6",
|
||||
"compression": "^1.7.4",
|
||||
"cookie-parser": "^1.4.5",
|
||||
@@ -49,7 +49,7 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@types/body-parser": "^1.19.0",
|
||||
"@types/cookie-parser": "^1.4.2",
|
||||
"@types/jwt-decode": "2.2.1",
|
||||
@@ -58,7 +58,8 @@
|
||||
"@types/passport-google-oauth20": "^2.0.3",
|
||||
"@types/passport-microsoft": "^0.0.0",
|
||||
"@types/passport-saml": "^1.1.2",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -18,4 +18,4 @@ fi
|
||||
|
||||
echo "Downloading and starting SAML-IdP"
|
||||
export NPM_CONFIG_REGISTRY=https://registry.npmjs.org
|
||||
exec npx saml-idp --acsUrl "http://localhost:7000/auth/saml/handler/frame" --audience "http://localhost:7000" --port 7001
|
||||
exec npx saml-idp --acsUrl "http://localhost:7000/api/auth/saml/handler/frame" --audience "http://localhost:7000" --port 7001
|
||||
|
||||
@@ -28,6 +28,7 @@ export interface RouterOptions {
|
||||
logger: Logger;
|
||||
database: Knex;
|
||||
config: Config;
|
||||
basePath?: string;
|
||||
}
|
||||
|
||||
export async function createRouter(
|
||||
@@ -38,7 +39,8 @@ export async function createRouter(
|
||||
|
||||
const appUrl = options.config.getString('app.baseUrl');
|
||||
const backendUrl = options.config.getString('backend.baseUrl');
|
||||
const authUrl = `${backendUrl}/auth`;
|
||||
// TODO(Rugvip): Replace with service discovery of external URL
|
||||
const authUrl = backendUrl + (options.basePath ?? '/api/auth');
|
||||
|
||||
const keyDurationSeconds = 3600;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog-backend",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,9 +20,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.23",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
@@ -40,7 +40,7 @@
|
||||
"yup": "^0.29.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@types/git-url-parse": "^9.0.0",
|
||||
"@types/lodash": "^4.14.151",
|
||||
"@types/node-fetch": "^2.5.7",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog-graphql",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,9 +20,9 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.22",
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.23",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@graphql-modules/core": "^0.7.17",
|
||||
"apollo-server": "^2.16.1",
|
||||
"graphql": "^15.3.0",
|
||||
@@ -32,16 +32,16 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@types/express": "^4.17.7",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@graphql-codegen/cli": "^1.17.7",
|
||||
"@graphql-codegen/typescript": "^1.17.7",
|
||||
"@graphql-codegen/typescript-resolvers": "^1.17.7",
|
||||
"ts-node": "^8.10.2",
|
||||
"@types/express": "^4.17.7",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"eslint-plugin-graphql": "^4.0.0",
|
||||
"msw": "^0.19.5",
|
||||
"supertest": "^4.0.2"
|
||||
"msw": "^0.20.5",
|
||||
"supertest": "^4.0.2",
|
||||
"ts-node": "^8.10.2"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -22,6 +22,7 @@ import { Config } from '@backstage/config';
|
||||
import { CatalogClient } from '../service/client';
|
||||
import GraphQLJSON, { GraphQLJSONObject } from 'graphql-type-json';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import typeDefs from '../schema';
|
||||
|
||||
export interface ModuleOptions {
|
||||
logger: Logger;
|
||||
@@ -31,8 +32,6 @@ export interface ModuleOptions {
|
||||
export async function createModule(
|
||||
options: ModuleOptions,
|
||||
): Promise<GraphQLModule> {
|
||||
const { default: typeDefs } = require('../schema');
|
||||
|
||||
const catalogClient = new CatalogClient(
|
||||
options.config.getString('backend.baseUrl'),
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-catalog",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,11 +21,11 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-scaffolder": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-techdocs": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -41,9 +41,9 @@
|
||||
"swr": "^0.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/react-hooks": "^3.3.0",
|
||||
@@ -53,7 +53,8 @@
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"react-test-renderer": "^16.13.1",
|
||||
"whatwg-fetch": "^3.4.0"
|
||||
"whatwg-fetch": "^3.4.0",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-circleci",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,10 +21,10 @@
|
||||
"postpack": "backstage-cli postpack"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -38,15 +38,17 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/react-lazylog": "^4.5.0",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-explore",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,8 +21,8 @@
|
||||
"start": "backstage-cli plugin:serve"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -33,15 +33,17 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
{
|
||||
"name": "@backstage/plugin-gcp-projects",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": true,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
@@ -21,8 +20,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -32,14 +31,16 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-github-actions",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,11 +21,11 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/core-api": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/core-api": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -40,14 +40,16 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-gitops-profiles",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,8 +21,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -32,14 +32,16 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/plugin-graphiql",
|
||||
"description": "Backstage plugin for browsing GraphQL APIs",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -31,8 +31,8 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -43,9 +43,9 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@backstage/test-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
@@ -53,7 +53,9 @@
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"react-router-dom": "6.0.0-beta.0"
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-graphql-backend",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -19,10 +19,10 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-catalog-graphql": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.23",
|
||||
"@backstage/config": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-catalog-graphql": "^0.1.1-alpha.23",
|
||||
"@graphql-modules/core": "^0.7.17",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.22",
|
||||
"@types/express": "^4.17.6",
|
||||
"apollo-server": "^2.16.1",
|
||||
"apollo-server-express": "^2.16.1",
|
||||
@@ -36,7 +36,7 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"eslint-plugin-graphql": "^4.0.0",
|
||||
"msw": "^0.20.5",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-identity-backend",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -20,7 +20,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.1.1-alpha.22",
|
||||
"@backstage/backend-common": "^0.1.1-alpha.23",
|
||||
"@types/express": "^4.17.6",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
@@ -33,8 +33,9 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/plugin-jenkins",
|
||||
"version": "0.1.1-alpha.22",
|
||||
"version": "0.1.1-alpha.23",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,10 +21,10 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.22",
|
||||
"@backstage/core": "^0.1.1-alpha.22",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.22",
|
||||
"@backstage/theme": "^0.1.1-alpha.22",
|
||||
"@backstage/catalog-model": "^0.1.1-alpha.23",
|
||||
"@backstage/core": "^0.1.1-alpha.23",
|
||||
"@backstage/plugin-catalog": "^0.1.1-alpha.23",
|
||||
"@backstage/theme": "^0.1.1-alpha.23",
|
||||
"@material-ui/core": "^4.11.0",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
@@ -36,15 +36,17 @@
|
||||
"react-use": "^15.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.1.1-alpha.22",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.22",
|
||||
"@backstage/cli": "^0.1.1-alpha.23",
|
||||
"@backstage/dev-utils": "^0.1.1-alpha.23",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^10.4.1",
|
||||
"@testing-library/user-event": "^12.0.7",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^12.0.0",
|
||||
"@types/testing-library__jest-dom": "^5.9.1",
|
||||
"jest-fetch-mock": "^3.0.3"
|
||||
"jest-fetch-mock": "^3.0.3",
|
||||
"msw": "^0.20.5",
|
||||
"node-fetch": "^2.6.1"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user