Merge remote-tracking branch 'origin/master' into shutdown-tasks-on-restart
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-msgraph': patch
|
||||
---
|
||||
|
||||
export the function to read ms graph provider config
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Cleaned up cases where deprecated code was being used but had a new location they should be imported from
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
`knex` has been bumped to major version 3 and `better-sqlite3` to major version 9, which deprecate node 16 support.
|
||||
|
||||
You can do the same in your own Backstage repository to ensure that you get future node 18+ relevant updates, by having the following lines in your `packages/backend/package.json`:
|
||||
|
||||
```
|
||||
"dependencies": {
|
||||
// ...
|
||||
"knex": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
// ...
|
||||
"better-sqlite3": "^9.0.0",
|
||||
```
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-bazaar': patch
|
||||
---
|
||||
|
||||
Updated Readme document in bazaar plugin
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-shortcuts': patch
|
||||
---
|
||||
|
||||
Ensure that shortcuts aren't duplicate-checked against themselves
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Fix spacing inconsistency with links and labels in headers
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
The options parameter of `createApp` is now optional.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-search-backend-module-elasticsearch': patch
|
||||
---
|
||||
|
||||
Support AWS OpenSearch Serverless search backend. Does not support `_refresh` endpoint.
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch
|
||||
'@backstage/plugin-catalog-backend-module-unprocessed': patch
|
||||
'@backstage/plugin-search-backend-module-pg': patch
|
||||
'@backstage/plugin-entity-feedback-backend': patch
|
||||
'@backstage/plugin-code-coverage-backend': patch
|
||||
'@backstage/plugin-tech-insights-backend': patch
|
||||
'@backstage/plugin-user-settings-backend': patch
|
||||
'@backstage/backend-plugin-api': patch
|
||||
'@backstage/backend-test-utils': patch
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
'@backstage/plugin-linguist-backend': patch
|
||||
'@backstage/plugin-playlist-backend': patch
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
'@backstage/backend-common': patch
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
'@backstage/backend-tasks': patch
|
||||
'@backstage/plugin-badges-backend': patch
|
||||
'@backstage/plugin-bazaar-backend': patch
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
'@backstage/plugin-app-backend': patch
|
||||
---
|
||||
|
||||
`knex` has been bumped to major version 3 and `better-sqlite3` to major version 9, which deprecate node 16 support.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
Implement new `AppTreeApi`
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-gitlab': patch
|
||||
---
|
||||
|
||||
Resolved a bug affecting the retrieval of users from group members. By appending '/all' to the API call, we now include members from all inherited groups, as per Gitlab's API specifications. This change is reflected in the listSaaSUsers function.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-app-api': patch
|
||||
---
|
||||
|
||||
No longer throw error on invalid input if the child is disabled.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
Add new `AppTreeApi`.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-backend': patch
|
||||
'@backstage/plugin-kubernetes-common': patch
|
||||
---
|
||||
|
||||
Fixed the lack of `resourcequotas` as part of the Default Objects to fetch from the kubernetes api
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Added missing node-gyp dependency to fix Docker image build
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Removed unnecessary `@backstage/integration` dependency, replaced by `@backstage/integration-react`.
|
||||
@@ -1,11 +1,8 @@
|
||||
---
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- about: 'Please ask and answer usage questions in GitHub Discussions'
|
||||
name: Question
|
||||
url: 'https://github.com/backstage/backstage/discussions'
|
||||
- about: 'Alternatively, you can use the Backstage Community Discord'
|
||||
name: Chat
|
||||
- about: 'Use the Backstage Community Discord for questions & discussions'
|
||||
name: Questions
|
||||
url: 'https://discord.gg/backstage-687207715902193673'
|
||||
- about: 'Please check the FAQ before filing new issues'
|
||||
name: 'Backstage FAQ'
|
||||
|
||||
@@ -107,6 +107,7 @@ dynatrace
|
||||
Dynatrace
|
||||
ecco
|
||||
elasticsearch
|
||||
Entra
|
||||
env
|
||||
Env
|
||||
esbuild
|
||||
@@ -196,10 +197,12 @@ Leasot
|
||||
lerna
|
||||
Lerna
|
||||
lightbox
|
||||
Lightsail
|
||||
limitranges
|
||||
LocalStack
|
||||
lockdown
|
||||
lockfile
|
||||
lookbehind
|
||||
lunr
|
||||
Luxon
|
||||
magiclink
|
||||
@@ -285,6 +288,7 @@ Podman
|
||||
posix
|
||||
postgres
|
||||
postpack
|
||||
PR
|
||||
pre
|
||||
prebaked
|
||||
preconfigured
|
||||
@@ -292,11 +296,12 @@ prepack
|
||||
Preprarer
|
||||
productional
|
||||
Protobuf
|
||||
proxying
|
||||
proxied
|
||||
proxying
|
||||
Proxying
|
||||
pseudonymized
|
||||
pubsub
|
||||
Pulumi
|
||||
pygments
|
||||
pymdownx
|
||||
rankdir
|
||||
@@ -304,6 +309,7 @@ readme
|
||||
Readme
|
||||
readonly
|
||||
rebase
|
||||
rebasing
|
||||
Recharts
|
||||
Redash
|
||||
replicasets
|
||||
@@ -312,6 +318,7 @@ Repo
|
||||
repos
|
||||
rerender
|
||||
rerenders
|
||||
resourcequotas
|
||||
reusability
|
||||
Reusability
|
||||
roadmaps
|
||||
@@ -425,8 +432,8 @@ unregistration
|
||||
untracked
|
||||
upsert
|
||||
upvote
|
||||
url
|
||||
URIs
|
||||
url
|
||||
URLs
|
||||
utils
|
||||
Valentina
|
||||
@@ -455,9 +462,3 @@ zod
|
||||
Zolotusky
|
||||
zoomable
|
||||
zsh
|
||||
Pulumi
|
||||
Lightsail
|
||||
PR
|
||||
rebasing
|
||||
lookbehind
|
||||
Entra
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: 'Run analysis'
|
||||
uses: ossf/scorecard-action@483ef80eb98fb506c348f7d62e28055e49fe2398 # v2.3.0
|
||||
uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
@@ -66,6 +66,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: 'Upload to code-scanning'
|
||||
uses: github/codeql-action/upload-sarif@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2.22.4
|
||||
uses: github/codeql-action/upload-sarif@74483a38d39275f33fcff5f35b679b5ca4a26a99 # v2.22.5
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -58,6 +58,6 @@ jobs:
|
||||
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||
NODE_OPTIONS: --max-old-space-size=7168
|
||||
- name: Upload Snyk report
|
||||
uses: github/codeql-action/upload-sarif@v2.22.4
|
||||
uses: github/codeql-action/upload-sarif@v2.22.5
|
||||
with:
|
||||
sarif_file: snyk.sarif
|
||||
|
||||
@@ -55,7 +55,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2.22.4
|
||||
uses: github/codeql-action/init@v2.22.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2.22.4
|
||||
uses: github/codeql-action/autobuild@v2.22.5
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -80,4 +80,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2.22.4
|
||||
uses: github/codeql-action/analyze@v2.22.5
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
|
||||
- run: yarn build-storybook
|
||||
|
||||
- uses: chromaui/action@c7e9d129ad2b8e728e10522270e14596473a7958 # v1
|
||||
- uses: chromaui/action@d726e4e790a99e876f71b8e09d3053bfe783d6b8 # v1
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# projectToken intentionally shared to allow collaborators to run Chromatic on forks
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="371px" height="351px" viewBox="-0.5 -0.5 371 351" content="<mxfile scale="1" border="20"><diagram id="W2IAjyMExVh-jXkT93Bg" name="Page-1">7V1bb6M4FP41fdwR5hbyOM1c9mFGGmmk3Z1HEkiChsQVobf59QuJnYBNGzclxyfktFUVGwzE57P5zs2+8Sarp69FfLf8zpM0v3Gd5OnG+3Tjun95o+p/XfG8q2DRiO1qFkWWiLpDxc/sTyoqHVF7nyXppnViyXleZnftyhlfr9NZ2aqLi4I/tk+b87x917t4kWoVP2dxrtf+myXlclfrBc6h/u80WyzFnT1PHJjGs9+Lgt+vxe1uXG++/dkdXsXyUuL8zTJO+GOjyvt8400Kzsvdp9XTJM3rrpW9tmv35YWj+8cu0nVp0sCTknqI8/tUPvP2ycpn2RnpOvlY92lVWvN1VXmbxJtlWl+CVYVlucrFxzlfl0KYlSS92zSpelVciBflki/4Os4/H2pvq+csnv+rDjsfAln8Ja62LXyqMeXsS8+y9JSVjWZV6VfjyKFRXZBt9M4R/bXh98VMfFnX8QTe4mKRivPcMNpV1l+o0VR06teUr9Lq6aoTHg9okWBZNoAi64o0j8vsoY22WIB2sb/c/g4/eFY9s+uIARY5wa6JGF+h077C7guJRk3xH7lOoFxn1wfadaoPjS99qNqC6yWgRZaBdgJgTgWnEdA8x78AoLE2QEYXALQxAa0NNHYJM5rbBkiEHmgsMgDalgpscVWL63GZlenPu3grmceKOXVgTbTdEw1NtA9pUaZPr4pMHB2NnVZfyGJDol6HRJnzsvBa/fVa58h7vdY5Ffm5qz/mnN99y1ZZebyL9nyp7s7N77ScLWWhLPjvPVmrzy14WSGSr6viuD5jxR/iaZ5+i6dpLhrl9ecffJOJ82ZVZ6dFdaDu5Kyig9+UE1ZZkmxHeZxni84WH8WB/ZlLXmR/KsnG8qabxnRSpFWpfipxbPvMjXIPKIiYRRQEhIIeUDDPs7t/xPfpY14YWUPEnl33N2lmeT7hOS+2bb15UP+KybRRv/vZI6RxJNz+9NSzyowbQfasf7xnXwL0lJclX3Xgt+T60NuO1tXTotb8P0zjTTb7cMfz50V9pdv604TzItl+leC2+qu+3ITdBNX9J3WpYjjupP7/cg1rtXittGvbKMtS9bd7mDxbH4ZVE1etSWSRx5uN+Dzjq2xmNrk02aGrgzGJ02g+6wTdLEqn8y2fzORIZ4fZSc5dR+aFJM3TZmthDWnU9PH+cNqYHumYZmfCtOczDcLHCXgDrd1cXB55Gxc/sO9uLt7SIDS1QVM4qpovWS7fBmk+3R6U48/YWiG7ucntPWbK7Y1Ju7nEOpQtkthRibljaxIb0Rg7RWLG+nPvEts/z3VwKOYAkijGjnetNRLltGjOSyV2pPQqFbs0+qQyIpUxnQOPDI4AucwlPKLB43w+d2edeEzCaRiEMHhU7TmgeAwN5kdUNs+Ozul6ebi9dI7BYCVrlw2bpwtIIUIDCxehwLbNExQRBq7v4fB1D7JnDVxwRI+uiq6r5kof0FzpYGdHUdjuHEPPTx/syHOIHWFhR9FJKOhniBA7QsiOIt8iIgz8lpfLjtQZF9Aj7DkG0RfkEcbGkS7AI6zoUpAe4f59Hz1TrLFnj2KZeC/o5QpBscaBvReqic+AUABNscb2wjD3AeDDpFjqjAtJsRgF3RHFOgPFGvsWKZaB2oDLxxdCjviQ3q44OJbmCAeFgUHeLMHAtpcPFhJX5eZjAWDXRpHWkxSj2s7xFJ7Qdoyq6HsLUcUyOZIk9iaJRb41iY1JYqdIbGwv10I+z5W8cDp0oLO9cKQ5FqVeT4ElKOLAO+xMZ9PJ949LeLSPRyRx4I5NPBr4GMjuiW2WxG/31CZZyPA918BNQqDGNtXiB7VmqAQFNeYgoGtjDkmQRonfBefInXqhrQwyUOZg4D9BFSNtakPuJYLHJbcCFu9SdBoM+hkkBq4EgoHtKGlYSAza2KdNupDeJY94PzqiNARlVgM1JO/3KDING6iHocyqIRaQoPZ7D2Q9c/Q/KEXwDYY8sUYb4f+wMKBFdxEqD2r8Pywkeje6YFIetEkXUnnwDSw2xLNIeXg3qEF5loH9iUBNysO7eREoqLHv18EcNaQDkiNI0RBttK09MMe1iQNKIseoPth0RwXukNUHfdqF1B8Ccj5go1qD0B90VENyrYC8D9hQPQgFQqdGoKgONBBTitTxrTKk9bG/FCnR9MhmpUwNyTLeRDJSLqTur9vjdqVBSJg6AVOSkp4dU8q+pMw7FVNj5ULqVrp9YsrAJo1qBQ/TRaN7ibEMKLgOiaFDW6YfcO1wLzAwBxIMbK/gAQoJ+Zofpp1Dm3QB90DwTDaOIYWQzBzvBjXgbkieyYY/BGqycryZFzkWQS0HFCmkLyikntzhqaWQjoR6Z2EdGJMdpy6XtmiBMJDrwISY7dhXlz2LYh0YdWlW0OzZEP3arCeGLvRi2QlpbVYklp2RzVTJkJKoMVp2bAa3DXwLRnXShQxgQb0H43UqwcOw7Nj09Esdg0CNBtSDsOxovAgU1AbmSlTZs6Bu4RHtTIlEeVCzREB9gCNKokaoPKjZs7CQ6N3ogkl50CZdSLfwyMBiQzyLlId3gxrSgzYatLFBTyyAdMiMMFsbyCFjwyHDHJvrmcp7oQTklb7Bjq+yi/8NpsXGQr7BIoNoPVSL9ILqA5GBHYVURBuL9MLCgAxGCC0F6iK9sJAwMB5dLvPXJl1IS0FkYIQhnoWN/l8Az1JBDcqzyPyFDdSDcDOqi/TCgppWRMQG6mFoxCrZBwS1L/dapayIhuxaWRFjUdnMivCd3pd+MJaYJKcksTdJjFmT2P55rkR76gj9OZv2NDYwWZHbBI/eBLELnIrHjqDh8yUZUl4VIjzi2JVQpXegeLy0vCrQ0Mgx5VUhcXio8cOg1u0x5VUhdHioeVWwkBh0qJM26QI6PHwpVpQU6UrNaENweGighjSjObQMFDZQD8LhMbIY7+s7mI0r1wnqQTg8NLIPCmrsW22q9ivPMSNnfWjEvkNZYkg0YtVsZAqDfgYJbbWJUCNWQwBhITHorTY1p8EYsmspsAQdzxqCRqyBGjB/yHcwJ7RdKaiHoDxo7jRIUDPsyoOaHwyqPDBSHpAoD+pKI6BMkZHygFB5UFcagYXEoJUHbVEGSOWBkfKAjmcNQXnQQA3Kswzc7wRqUh7ey4tAQe1SbCgiOKOIVdZiFgBjQ33XgKcTHvFMrxB4VD2zkHiUhAMlHumlP5yXPmS4gUeBYQRqADP4GRfcrooF52Xj2NcaZN95UhvTPv8P</diagram></mxfile>" style="background-color: rgb(255, 255, 255);">
|
||||
<defs/>
|
||||
<g>
|
||||
<path d="M 115 90 L 115 50" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 185 90 L 185 80 Q 185 70 175 70 L 125 70 Q 115 70 115 60 L 115 50" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 255 90 L 255 80 Q 255 70 245 70 L 125 70 Q 115 70 115 60 L 115 50" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="100" y="100" width="30" height="10" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 130 100 L 140 100 L 150 108 L 150 110 L 120 110 L 120 108 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,135,105)" pointer-events="all"/>
|
||||
<path d="M 90 100 L 100 100 L 110 108 L 110 110 L 80 110 L 80 108 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,105)scale(1,-1)translate(0,-105)rotate(-90,95,105)" pointer-events="all"/>
|
||||
<rect x="100" y="90" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 110 90 L 115 85 L 115 85 L 120 90 L 120 90 L 120 90 L 115 90 L 110 90 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 115 120 L 115 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 115 120 L 115 130 Q 115 140 125 140 L 175 140 Q 185 140 185 150 L 185 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 115 120 L 115 130 Q 115 140 105 140 L 55 140 Q 45 140 45 150 L 45 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="100" y="110" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 100 120 L 100 120 L 100 120 L 110 120 L 110 120 L 110 130 L 105 125 L 100 130 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 120 120 L 120 120 L 120 120 L 130 120 L 130 120 L 130 130 L 125 125 L 120 130 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="100" y="20" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 130 30 L 140 30 L 150 38 L 150 40 L 120 40 L 120 38 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,135,35)" pointer-events="all"/>
|
||||
<path d="M 90 30 L 100 30 L 110 38 L 110 40 L 80 40 L 80 38 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,35)scale(1,-1)translate(0,-35)rotate(-90,95,35)" pointer-events="all"/>
|
||||
<rect x="100" y="40" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 110 50 L 110 50 L 110 50 L 120 50 L 120 50 L 120 60 L 115 55 L 110 60 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="170" y="100" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 200 100 L 210 100 L 220 108 L 220 110 L 190 110 L 190 108 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,205,105)" pointer-events="all"/>
|
||||
<path d="M 160 100 L 170 100 L 180 108 L 180 110 L 150 110 L 150 108 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,105)scale(1,-1)translate(0,-105)rotate(-90,165,105)" pointer-events="all"/>
|
||||
<rect x="170" y="90" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 180 90 L 185 85 L 185 85 L 190 90 L 190 90 L 190 90 L 185 90 L 180 90 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="240" y="100" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 270 100 L 280 100 L 290 108 L 290 110 L 260 110 L 260 108 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,275,105)" pointer-events="all"/>
|
||||
<path d="M 230 100 L 240 100 L 250 108 L 250 110 L 220 110 L 220 108 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,105)scale(1,-1)translate(0,-105)rotate(-90,235,105)" pointer-events="all"/>
|
||||
<rect x="240" y="90" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 250 90 L 255 85 L 255 85 L 260 90 L 260 90 L 260 90 L 255 90 L 250 90 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="100" y="170" width="30" height="10" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 130 170 L 140 170 L 150 178 L 150 180 L 120 180 L 120 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,135,175)" pointer-events="all"/>
|
||||
<path d="M 90 170 L 100 170 L 110 178 L 110 180 L 80 180 L 80 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,175)scale(1,-1)translate(0,-175)rotate(-90,95,175)" pointer-events="all"/>
|
||||
<rect x="100" y="160" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 115 190 L 115 230" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 115 190 L 115 200 Q 115 210 125 210 L 175 210 Q 185 210 185 220 L 185 230" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 115 190 L 115 200 Q 115 210 105 210 L 55 210 Q 45 210 45 220 L 45 230" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="100" y="180" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 100 190 L 100 190 L 100 190 L 110 190 L 110 190 L 110 200 L 105 195 L 100 200 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 110 190 L 110 190 L 110 190 L 120 190 L 120 190 L 120 200 L 115 195 L 110 200 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 100 160 L 105 155 L 105 155 L 110 160 L 110 160 L 110 160 L 105 160 L 100 160 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 120 160 L 125 155 L 125 155 L 130 160 L 130 160 L 130 160 L 125 160 L 120 160 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 120 190 L 120 190 L 120 190 L 130 190 L 130 190 L 130 200 L 125 195 L 120 200 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="170" y="170" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 200 170 L 210 170 L 220 178 L 220 180 L 190 180 L 190 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,205,175)" pointer-events="all"/>
|
||||
<path d="M 160 170 L 170 170 L 180 178 L 180 180 L 150 180 L 150 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,175)scale(1,-1)translate(0,-175)rotate(-90,165,175)" pointer-events="all"/>
|
||||
<rect x="170" y="160" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 170 160 L 175 155 L 175 155 L 180 160 L 180 160 L 180 160 L 175 160 L 170 160 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 180 160 L 185 155 L 185 155 L 190 160 L 190 160 L 190 160 L 185 160 L 180 160 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="240" y="170" width="30" height="10" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 270 170 L 280 170 L 290 178 L 290 180 L 260 180 L 260 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,275,175)" pointer-events="all"/>
|
||||
<path d="M 230 170 L 240 170 L 250 178 L 250 180 L 220 180 L 220 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,175)scale(1,-1)translate(0,-175)rotate(-90,235,175)" pointer-events="all"/>
|
||||
<rect x="240" y="160" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 240 160 L 245 155 L 245 155 L 250 160 L 250 160 L 250 160 L 245 160 L 240 160 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 260 160 L 265 155 L 265 155 L 270 160 L 270 160 L 270 160 L 265 160 L 260 160 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="310" y="170" width="30" height="10" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 340 170 L 350 170 L 360 178 L 360 180 L 330 180 L 330 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,345,175)" pointer-events="all"/>
|
||||
<path d="M 300 170 L 310 170 L 320 178 L 320 180 L 290 180 L 290 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,175)scale(1,-1)translate(0,-175)rotate(-90,305,175)" pointer-events="all"/>
|
||||
<rect x="310" y="160" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 310 160 L 315 155 L 315 155 L 320 160 L 320 160 L 320 160 L 315 160 L 310 160 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 330 160 L 335 155 L 335 155 L 340 160 L 340 160 L 340 160 L 335 160 L 330 160 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 115 120 L 115 130 Q 115 140 125 140 L 245 140 Q 255 140 255 150 L 255 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 115 120 L 115 130 Q 115 140 125 140 L 315 140 Q 325 140 325 150 L 325 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="100" y="240" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 130 240 L 140 240 L 150 248 L 150 250 L 120 250 L 120 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,135,245)" pointer-events="all"/>
|
||||
<path d="M 90 240 L 100 240 L 110 248 L 110 250 L 80 250 L 80 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,245)scale(1,-1)translate(0,-245)rotate(-90,95,245)" pointer-events="all"/>
|
||||
<rect x="100" y="230" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 100 230 L 105 225 L 105 225 L 110 230 L 110 230 L 110 230 L 105 230 L 100 230 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 110 230 L 115 225 L 115 225 L 120 230 L 120 230 L 120 230 L 115 230 L 110 230 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 255 190 L 255 230" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="240" y="180" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 250 190 L 250 190 L 250 190 L 260 190 L 260 190 L 260 200 L 255 195 L 250 200 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="30" y="170" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 60 170 L 70 170 L 80 178 L 80 180 L 50 180 L 50 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,65,175)" pointer-events="all"/>
|
||||
<path d="M 20 170 L 30 170 L 40 178 L 40 180 L 10 180 L 10 178 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,175)scale(1,-1)translate(0,-175)rotate(-90,25,175)" pointer-events="all"/>
|
||||
<rect x="30" y="160" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 30 160 L 35 155 L 35 155 L 40 160 L 40 160 L 40 160 L 35 160 L 30 160 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 50 160 L 55 155 L 55 155 L 60 160 L 60 160 L 60 160 L 55 160 L 50 160 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="240" y="240" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 270 240 L 280 240 L 290 248 L 290 250 L 260 250 L 260 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,275,245)" pointer-events="all"/>
|
||||
<path d="M 230 240 L 240 240 L 250 248 L 250 250 L 220 250 L 220 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,245)scale(1,-1)translate(0,-245)rotate(-90,235,245)" pointer-events="all"/>
|
||||
<rect x="240" y="230" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 240 230 L 245 225 L 245 225 L 250 230 L 250 230 L 250 230 L 245 230 L 240 230 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="310" y="180" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 320 190 L 320 190 L 320 190 L 330 190 L 330 190 L 330 200 L 325 195 L 320 200 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 120 230 L 125 225 L 125 225 L 130 230 L 130 230 L 130 230 L 125 230 L 120 230 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="170" y="240" width="30" height="10" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 200 240 L 210 240 L 220 248 L 220 250 L 190 250 L 190 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,205,245)" pointer-events="all"/>
|
||||
<path d="M 160 240 L 170 240 L 180 248 L 180 250 L 150 250 L 150 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,245)scale(1,-1)translate(0,-245)rotate(-90,165,245)" pointer-events="all"/>
|
||||
<rect x="170" y="230" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 170 230 L 175 225 L 175 225 L 180 230 L 180 230 L 180 230 L 175 230 L 170 230 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 180 230 L 185 225 L 185 225 L 190 230 L 190 230 L 190 230 L 185 230 L 180 230 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 190 230 L 195 225 L 195 225 L 200 230 L 200 230 L 200 230 L 195 230 L 190 230 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 185 260 L 185 300" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 185 260 L 185 270 Q 185 280 195 280 L 245 280 Q 255 280 255 290 L 255 300" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="170" y="250" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 170 260 L 170 260 L 170 260 L 180 260 L 180 260 L 180 270 L 175 265 L 170 270 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 190 260 L 190 260 L 190 260 L 200 260 L 200 260 L 200 270 L 195 265 L 190 270 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="30" y="240" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 60 240 L 70 240 L 80 248 L 80 250 L 50 250 L 50 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,65,245)" pointer-events="all"/>
|
||||
<path d="M 20 240 L 30 240 L 40 248 L 40 250 L 10 250 L 10 248 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,245)scale(1,-1)translate(0,-245)rotate(-90,25,245)" pointer-events="all"/>
|
||||
<rect x="30" y="230" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 30 230 L 35 225 L 35 225 L 40 230 L 40 230 L 40 230 L 35 230 L 30 230 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 40 230 L 45 225 L 45 225 L 50 230 L 50 230 L 50 230 L 45 230 L 40 230 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 50 230 L 55 225 L 55 225 L 60 230 L 60 230 L 60 230 L 55 230 L 50 230 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="170" y="310" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 200 310 L 210 310 L 220 318 L 220 320 L 190 320 L 190 318 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,205,315)" pointer-events="all"/>
|
||||
<path d="M 160 310 L 170 310 L 180 318 L 180 320 L 150 320 L 150 318 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,315)scale(1,-1)translate(0,-315)rotate(-90,165,315)" pointer-events="all"/>
|
||||
<rect x="170" y="300" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 170 300 L 175 295 L 175 295 L 180 300 L 180 300 L 180 300 L 175 300 L 170 300 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 190 300 L 195 295 L 195 295 L 200 300 L 200 300 L 200 300 L 195 300 L 190 300 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="240" y="310" width="30" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<path d="M 270 310 L 280 310 L 290 318 L 290 320 L 260 320 L 260 318 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="rotate(90,275,315)" pointer-events="all"/>
|
||||
<path d="M 230 310 L 240 310 L 250 318 L 250 320 L 220 320 L 220 318 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" transform="translate(0,315)scale(1,-1)translate(0,-315)rotate(-90,235,315)" pointer-events="all"/>
|
||||
<rect x="240" y="300" width="30" height="10" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 240 300 L 245 295 L 245 295 L 250 300 L 250 300 L 250 300 L 245 300 L 240 300 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 260 300 L 265 295 L 265 295 L 270 300 L 270 300 L 270 300 L 265 300 L 260 300 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 30 260 L 30 260 L 30 260 L 40 260 L 40 260 L 40 270 L 35 265 L 30 270 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 50 260 L 50 260 L 50 260 L 60 260 L 60 260 L 60 270 L 55 265 L 50 270 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 260 230 L 265 225 L 265 225 L 270 230 L 270 230 L 270 230 L 265 230 L 260 230 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 190 160 L 195 155 L 195 155 L 200 160 L 200 160 L 200 160 L 195 160 L 190 160 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,331 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="582px" height="432px" viewBox="-0.5 -0.5 582 432" content="<mxfile scale="1.5" border="20"><diagram id="gKBvn7eBFHudv9VMIRD-" name="Page-1">7Vpfc6IwEP80Pp6DBAEfPWt7fbi5Tnudu3tMJUqmkTghKvbTXyDhb8Raix4yhzMOu2RDsvvb3WRDD0yW0R2DK/879RDpmYYX9cBNzzRNeyj+Y8ZOMhzLlYwFw55kDXLGE35Dimko7hp7KCw15JQSjldl5owGAZrxEg8yRrflZnNKym9dwQXSGE8zSHTuL+xxX3KBa+T8bwgv/PTNlnrwAmevC0bXgXpdzwTz5JKPlzDtSrUPfejRbYEFpj0wYZRyebeMJojEmk21JuVua55mw2Yo4McImFJgA8lazXy8Wqmh8V2qjWRCKBYxeuDr1sccPa3gLH66FeYXPJ8viaAG4naOCZlQQlkiC6Z2/BP8kDP6igpP7OSKJWjAC3x5Cb4+GTW/DWIcRQWWmtwdokvE2U40icp42pXJbcGmtuL5BXum5oQKRous41yX4kapc79qgabaacRREGIahB3SsDkqq9g0dB27Z1LxQIfvA6MbETo0BSNP+LciAxqgskZRhPnvwv2f2BD9oaJuImWXhNilRCBG+7tISClzmNK5XELlgt44DlGCfCF09vrTx4Fk32KSjShIo6KVWBYyXqDLcImNhUXsGhO8ED3dLLHnkWSGlOE3YXqYIUcQqhv3kPlDumYzpTlLhV/IFoiXoB3r9CBEGCKQ4005pu4zuBJ9oFgMJG9C5/NQvLMUwNI2UQVuqYePKriSU1FSFWhlQz0KbZYONrJe4E5587CiTedyzrwnYN4HAvgBx5B/2KEz5+w7w5J/Xo1zejD0k14Gh0x7ilsqWxt9w7YdKXMBTwUVbA2Pc1StH8utJByn0pFUSRMeb9r7IRk3OhWOV4rGWgC2IQk4zSDLqQl+zQMLGBqwnjkmmMcKGz/cdymlZHsdpdbR5VIKGGhq/kxKaYsvnpzcTkgpRnu83HSbcXNQ3RS65/PzPWvGdIMiXmrDZey58l9wnsPG1jmDd6DQHjRfHQ6r+Mm2IG0Gor6SyaoRgv1DWI8lFbfu5J2qlcAF846jafuRrnm31PsP07qrr57+R866yOm0KHIaDUXODEZp5KzCqsHIOapP4SfVGFO8pWXGI4uMhbpiR1D6XpGxRcCtBrtPpPxRf1S8DnfbHIwtfcf5iEJKNp3YBp17F9QmKNrN7IK0ascZsWc2FULromEH6mht3e1UU21jeDtfyrZqThI+U7btNt7s9uANNAM3UM6zrmuX0Xe+2q6l13zS7fWJK8aPnUq3BXl15+POe+fjRy0YU8i29Fh6AD60zjs6iNZ9bHEGGA+bT9pXHUMrsDruNLa2fPJFpBQpc4mY2lBQ1T4PaAx+gsw/epPN8+8KwfQv</diagram></mxfile>" style="background-color: rgb(255, 255, 255);">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="19.5" y="19.5" width="540" height="60" fill="#e6e6e6" stroke="#666666" stroke-width="1.5" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 358px; height: 1px; padding-top: 33px; margin-left: 14px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
App
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="193" y="37" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
App
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="439.5" y="304.5" width="120" height="60" fill="#e6e6e6" stroke="#666666" stroke-width="1.5" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 223px; margin-left: 294px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Extensions
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="333" y="227" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Extensions
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 199.5 289.5 L 430.39 318.36" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 437.84 319.29 L 430.08 320.84 L 430.7 315.88 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 203px; margin-left: 213px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Provide
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="213" y="205" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Provide
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="79.5" y="259.5" width="120" height="60" fill="#e6e6e6" stroke="#666666" stroke-width="1.5" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 193px; margin-left: 54px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Plugins
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="93" y="197" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Plugins
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 529.5 79.5 L 529.5 295.32" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="stroke"/>
|
||||
<path d="M 529.5 302.82 L 527 295.32 L 532 295.32 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 133px; margin-left: 353px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Instantiate
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="353" y="135" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Instantiate
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 109.5 79.5 L 109.5 250.32" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 109.5 257.82 L 107 250.32 L 112 250.32 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 113px; margin-left: 73px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Install
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="73" y="115" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Install
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="334.5" y="139.5" width="120" height="60" fill="#e6e6e6" stroke="#666666" stroke-width="1.5" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 113px; margin-left: 224px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Utility APIs
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="263" y="117" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Utility APIs
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 424.5 79.5 L 424.5 130.32" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="stroke"/>
|
||||
<path d="M 424.5 137.82 L 422 130.32 L 427 130.32 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 73px; margin-left: 283px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Instantiate
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="283" y="75" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Instantiate
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 469.5 304.5 L 428.11 207.93" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 425.16 201.04 L 430.41 206.95 L 425.82 208.92 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 168px; margin-left: 298px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Provide & Use
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="298" y="170" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Provide & Use
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="19.5" y="349.5" width="120" height="60" fill="#e6e6e6" stroke="#666666" stroke-width="1.5" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 253px; margin-left: 14px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Extension Overrides
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="53" y="257" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Extension Ove...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="184.5" y="139.5" width="120" height="60" fill="#e6e6e6" stroke="#666666" stroke-width="1.5" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 113px; margin-left: 124px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Routes
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="163" y="117" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Routes
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 454.5 304.5 L 282.43 204.12" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 275.95 200.34 L 283.69 201.96 L 281.17 206.28 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 168px; margin-left: 243px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Use
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="243" y="170" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Use
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 169.5 259.5 L 208.99 206.84" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 213.49 200.84 L 210.99 208.34 L 206.99 205.34 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 153px; margin-left: 128px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Provide
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="128" y="155" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Provide
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 244.5 79.5 L 244.5 130.32" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" stroke-dasharray="4.5 4.5" pointer-events="stroke"/>
|
||||
<path d="M 244.5 137.82 L 242 130.32 L 247 130.32 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 73px; margin-left: 163px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Resolve
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="163" y="75" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Resolve
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 364.5 79.5 L 364.5 130.32" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 364.5 137.82 L 362 130.32 L 367 130.32 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 73px; margin-left: 243px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Provide
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="243" y="75" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Provide
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 49.5 79.5 L 49.5 340.32" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 49.5 347.82 L 47 340.32 L 52 340.32 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 143px; margin-left: 33px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Install
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="33" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Install
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 139.5 379.5 L 430.37 350.41" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 437.83 349.67 L 430.62 352.9 L 430.12 347.93 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 243px; margin-left: 193px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Override
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="193" y="245" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Override
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 499.5 79.5 L 499.5 295.32" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 499.5 302.82 L 497 295.32 L 502 295.32 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)scale(1.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 113px; margin-left: 333px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); background-color: rgb(255, 255, 255); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 8px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
Provide
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="333" y="115" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
Provide
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
</g>
|
||||
<switch>
|
||||
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
|
||||
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
|
||||
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
|
||||
Text is not SVG - cannot display
|
||||
</text>
|
||||
</a>
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 28 KiB |
@@ -0,0 +1,332 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="411px" height="382px" viewBox="-0.5 -0.5 411 382" content="<mxfile scale="1" border="20"><diagram id="W2IAjyMExVh-jXkT93Bg" name="Page-1">7Zxtb6M4EIB/TT5eBZi3fGzS7u5Jd7qVetLtfXTABFSCI3Ca9H792WAI2ITQBJO0SbbS4gEcmHlmxq+ZgPlq9z2F6/BP7KN4Ymj+bgKeJoahaxag/zHJO5e4jl5Ilmnkc9le8BL9h8pbuXQT+ShrXEgwjkm0bgo9nCTIIw0ZTFO8bV4W4Lj5rWu4RJLgxYOxLP0n8klYSMFU28t/oGgZ8m8GNj+xgN7rMsWbhH/dxABB/ilOr2BZFb8+C6GPtzUReJ6AeYoxKY5WuzmKmW5LrRX3fTtwtnrsFCWk1w22U9zyBuMNKp85fzLyXiojfx/E7tAmYLYNI4Je1tBjZ7fU/lQWklVMSzo9DKI4nuMYp/m9ILDYPybHCanJiw+VZyTFr6h2xs4/9Iz8Lvz13lBK0K4m4u/2HeEVIuk7vYSf/U13uKJLEEsLbvdWLW0X1gxqchnkHC2rqvfKpAdcn4d0a39t3bqX062pS5pEPvVgXkxwgpqqQ7uI/GJafrB46d/amacdN0BeeC8Lif/IYsm+Qir5FrGHys8fVGKGN6nHH4ubnMB0icpoUYjYA3cqOkUxJNFbMySdozVD4vF5R1CSRTg5D8whcDJEVwUyTroqnoCkmb82ZL0hX8lfq6TDFeyM6K7mR911CNczh/YzfutPHNFvrSnWFMh1BJUVzs9vE7RWPUc/RUqc/p7kmGpyQPzMqF4wbZvW8aw9ArxV60E5vrp1Gr77msoLcRBk6FzEOxj/Ws2n6QWbppoMuR9lcBFTXYo6Zu8U0a7RH3CB4p84iwhrL4CnVeT77JpZecFjHC0bJ2q6px2dNatvtVuyLuPDAmaR97DG8fuSVTZjR3OMUz9/K2tG/7QHnXZO5trEok8xzwUuaAh0VnowLEHgWOItek2QV1oTSLfUKqV/xaPFEfPjHLKqy5YXXhHxwrKQ81L2F9lbx4LKPMoLojDNIFdVJWhDF8Yx3j6WvVlWH+/uMjsN2PICPVpeykicusfD7SECF5gQvGohkOD1+fg12eso6UdKdfqsnK4mbfoh1JrRrQHeMoZZxo89vIq8fkCyUMdHW3RDDok+RG7gtYY+z0WLQFF6N1qAM1qAMwYArmopXSVwnahIkiZcXaXiXv3GsRM6QNaI1Jk9xtnu1I1KXRAEhtdKnW8vbEvVMNmo1N2T683xViXTSyRXc3oH7mqAGymtih3ZUYEro+kduCsAbqyMal8SuGvuPtwacL6FXN9sA841FmC4obpLptRyLcWB4WeckhBTKGD8vJfOvE36lhtCn5w9DerDLKzqEg0yzBypxX1I/VSN3rSkJVhouJma6p1a5hS1J0igZFVKIxGiQINqrt+6C3CRNJp4cES2bZhRrdd2QnBGs1aXXdBV5oLd05dfygXrCxWqUQv1c1BCJ1kcWi4etWMKqsJEa1ZkK3RweQ6lnKi6+/fH/VuYRDdapsPUObitxMGNuodrF/dwS/bwavpldA+XPLO3i0vNf1ehjzs3CkY1dDg+GNOTY791BLEhwXCVgOF8AjCmY4EhzBIC7WQwxNyiboGWYU1vFYzS+9SDISQAoJ8MhtixV5hKyoe+QTD0scAQEgAQG4f9wTCOJKUBl3QCuR9Bz4msjL4eW1wUpMnt8bZFQUO0x00g75zwcBJES6YTL0QruXd1cf2MuGjKBPJqggB6BPNqrkov7ojcmN3N9Y8v1aV6SN9/VRfTQh5meQOelfehNi9Ve0fKAF2PztWIT3t87hViq3DRCLEjBVhhpu/klrq49FVdQ900uxvqwyHhXBAJ53JIiEv5Tm6k68LIna5ubN40uxvpw0FxjImD9r0CU0oGON2UCjfEWN3N6jNMqX1iUwJdmS0VxupyDkK5LY+m7+s25ukDIfaIxuyetx7OmEcT71Ub8/SEKRlTYcK05Q2ykBDohX9jycr3/UI3tl+oZw9vkJ6vLe8OLEcG7hjeNoZ9RxoG4dCRR2BeCE0MnsThfdq/y4jC5B9oGWU0W2w4yGiR02N3xOjDZ2J7u00jqtYamo68fH+eR9dNmjvvne1z2G4ZIXZUWbL87lOWKavPkIYlZEjH+nCGdIS9ZUWl9wzZFUtGXLdsuj0Wyh+JpzBbFz/uFkQ75u0X2uACeiQpvUWN4rT0aWo0JDXWfrSJrzTUHhWE5hgF5LzArCKoAnHyv9UYyqwh94OHhnqkTTQS1MaYUMu9uBaoZ7cLdasxlFmjxy8enRupx9moI0Ftjgm1vGihBer57ULdaoxhrEGL+x9NLQY4979MC57/Bw==</diagram></mxfile>" style="background-color: rgb(255, 255, 255);">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="230" y="200" width="60" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<rect x="220" y="200" width="60" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<path d="M 210 180 L 210 120" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<rect x="130" y="140" width="160" height="40" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 158px; height: 1px; padding-top: 160px; margin-left: 131px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Extension
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="210" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Extension
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="180" y="80" width="60" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 100px; margin-left: 181px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Output
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="210" y="104" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Output
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 160 200 L 160 180" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<rect x="130" y="200" width="60" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 220px; margin-left: 131px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Input 1
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="160" y="224" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Input 1
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 250 200 L 250 180" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<rect x="210" y="200" width="60" height="40" fill="#f5f5f5" stroke="#666666" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 220px; margin-left: 211px;">
|
||||
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Input 2
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="240" y="224" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Input 2
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 40.2 140 L 79.8 140 L 90 150 L 90 170 L 79.8 180 L 40.2 180 L 30 170 L 30 150 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 160px; margin-left: 31px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
disabled
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="60" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
disabled
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 130 240 L 130 240 L 130 240 L 150 240 L 150 240 L 150 260 L 140 250 L 130 260 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 180 80 L 190 70 L 190 70 L 200 80 L 200 80 L 200 80 L 190 80 L 180 80 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 220 80 L 230 70 L 230 70 L 240 80 L 240 80 L 240 80 L 230 80 L 220 80 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 170 240 L 170 240 L 170 240 L 190 240 L 190 240 L 190 260 L 180 250 L 170 260 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 210 240 L 210 240 L 210 240 L 230 240 L 230 240 L 230 260 L 220 250 L 210 260 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 240 240 L 240 240 L 240 240 L 260 240 L 260 240 L 260 260 L 250 250 L 240 260 Z" fill="#fff2cc" stroke="#d6b656" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 270 240 L 270 240 L 270 240 L 290 240 L 290 240 L 290 260 L 280 250 L 270 260 Z" fill="#d5e8d4" stroke="#82b366" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 210 40 Q 210 50 200 50 Q 190 50 190 60" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="170" y="20" width="80" height="20" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 30px; margin-left: 171px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Output Data
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="210" y="33" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
|
||||
Output Data
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 210 40 Q 210 50 220 50 Q 230 50 230 60" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="160" y="300" width="80" height="20" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 78px; height: 1px; padding-top: 310px; margin-left: 161px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Input Data
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="200" y="313" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
|
||||
Input Data
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 180 300 Q 180 280 160 280 Q 140 280 140 260" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 180 300 Q 180 300 180 260" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 220 300 Q 220 300 220 260" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 220 300 Q 220 280 235 280 Q 250 280 250 260" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<path d="M 220 300 Q 220 280 250 280 Q 280 280 280 260" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" stroke-dasharray="3 3" pointer-events="stroke"/>
|
||||
<rect x="330" y="110" width="60" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 120px; margin-left: 331px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
id
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="360" y="124" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
id
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="330" y="140" width="60" height="40" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 160px; margin-left: 331px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
config schema
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="360" y="164" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
config sch...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="330" y="190" width="60" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 200px; margin-left: 331px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
factory
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="360" y="204" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
factory
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 330 120 L 290 150" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 330 200 L 290 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 330 160 L 290 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 90 160 L 130 160" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 90 110 L 130 150" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 90 210 L 130 170" fill="none" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 40.2 90 L 79.8 90 L 90 100 L 90 120 L 79.8 130 L 40.2 130 L 30 120 L 30 100 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 110px; margin-left: 31px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
attachTo
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="60" y="114" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
attachTo
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 40.2 190 L 79.8 190 L 90 200 L 90 220 L 79.8 230 L 40.2 230 L 30 220 L 30 200 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 210px; margin-left: 31px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
config
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="60" y="214" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
config
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="320" y="310" width="40" height="20" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 320px; margin-left: 321px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Static
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="340" y="323" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
|
||||
Static
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="300" y="310" width="20" height="20" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" pointer-events="all"/>
|
||||
<rect x="320" y="340" width="70" height="20" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 350px; margin-left: 321px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Configurable
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="355" y="353" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
|
||||
Configurable
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 305 340 L 315 340 L 320 345 L 320 355 L 315 360 L 305 360 L 300 355 L 300 345 Z" fill="rgb(255, 255, 255)" stroke="rgb(0, 0, 0)" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="20" y="310" width="10" height="10" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"/>
|
||||
<rect x="40" y="310" width="100" height="10" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 98px; height: 1px; padding-top: 315px; margin-left: 42px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Extension Data A
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="42" y="318" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px">
|
||||
Extension Data A
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="20" y="330" width="10" height="10" fill="#fff2cc" stroke="#d6b656" pointer-events="all"/>
|
||||
<rect x="40" y="330" width="100" height="10" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 98px; height: 1px; padding-top: 335px; margin-left: 42px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Extension Data B
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="42" y="338" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px">
|
||||
Extension Data B
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="20" y="350" width="10" height="10" fill="#d5e8d4" stroke="#82b366" pointer-events="all"/>
|
||||
<rect x="40" y="350" width="100" height="10" fill="none" stroke="none" pointer-events="all"/>
|
||||
<g transform="translate(-0.5 -0.5)">
|
||||
<switch>
|
||||
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
|
||||
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 98px; height: 1px; padding-top: 355px; margin-left: 42px;">
|
||||
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: left;">
|
||||
<div style="display: inline-block; font-size: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Extension Data C
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="42" y="358" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px">
|
||||
Extension Data C
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
</g>
|
||||
<switch>
|
||||
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
|
||||
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
|
||||
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
|
||||
Text is not SVG - cannot display
|
||||
</text>
|
||||
</a>
|
||||
</switch>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 29 KiB |
@@ -194,8 +194,6 @@ WORKDIR /app
|
||||
|
||||
COPY --from=packages --chown=node:node /app .
|
||||
|
||||
# Stop cypress from downloading it's massive binary.
|
||||
ENV CYPRESS_INSTALL_BINARY=0
|
||||
RUN --mount=type=cache,target=/home/node/.cache/yarn,sharing=locked,uid=1000,gid=1000 \
|
||||
yarn install --frozen-lockfile --network-timeout 600000
|
||||
|
||||
|
||||
@@ -452,6 +452,7 @@ Overrides for the Kubernetes object types fetched from the cluster. The default
|
||||
- services
|
||||
- configmaps
|
||||
- limitranges
|
||||
- resourcequotas
|
||||
- deployments
|
||||
- replicasets
|
||||
- horizontalpodautoscalers
|
||||
@@ -504,6 +505,7 @@ rules:
|
||||
- ingresses
|
||||
- statefulsets
|
||||
- limitranges
|
||||
- resourcequotas
|
||||
- daemonsets
|
||||
verbs:
|
||||
- get
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: index
|
||||
title: Frontend System Architecture
|
||||
sidebar_label: Overview
|
||||
# prettier-ignore
|
||||
description: The structure and architecture of the new Frontend System
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
|
||||
## Building Blocks
|
||||
|
||||
This section introduces the high-level building blocks upon which this new
|
||||
system is built. Most of these concepts exist in our current system too, although
|
||||
in some cases you need to squint quite a lot to see the similarity.
|
||||
|
||||
Regardless of whether you are setting up your own backstage instance,
|
||||
developing plugins, or extending plugins with new features, it is
|
||||
important to understand these concepts.
|
||||
|
||||
The diagram below provides an overview of the different building blocks, and the other blocks that each of them interact with.
|
||||
|
||||

|
||||
|
||||
### App
|
||||
|
||||
This is the app instance itself that you create and use as the root of your Backstage frontend application. It does not have any direct functionality in and of itself, but is simply responsible for wiring things together.
|
||||
|
||||
### Extensions
|
||||
|
||||
Extensions are the building blocks that build out both the visual and non-visual structure of the application. There are both built-in extensions provided by the app itself, as well as extensions provided by plugins. Each extension is attached to a parent with which it shares data, and can have any number of children of its own. It is up to the app to wire together all extensions into a single tree known as the app extension tree. It is from this structure that the entire app can then be instantiated and rendered.
|
||||
|
||||
### Plugins
|
||||
|
||||
Plugins provide the actual features inside an app. The size of a plugin can range from a tiny component to an entire new system in which other plugins can be composed and integrated. Plugins can be completely standalone, or build on top of each other to extend existing plugins and augment their features. Plugins can communicate with each other by composing their extensions, or by sharing Utility APIs and routes.
|
||||
|
||||
### Extension Overrides
|
||||
|
||||
In addition to the built-in extensions and extensions provided by plugins, it is also possible to install extension overrides. This is a collection of extensions with high priority that can replace existing extensions. They can for example be used to override an individual extension provided by a plugin, or install a completely new extension, such as a new app theme.
|
||||
|
||||
### Utility APIs
|
||||
|
||||
Utility APIs provide functionality that makes it easier to build plugins, make it possible for plugins to share functionality with other plugins, as well as serve as a customization point for integrators to change the behavior of the app. Each Utility API is defined by a TypeScript interface as well as a reference used to access the implementations. The implementations of Utility APIs are defined by extensions that are provided and can be overridden the same as any other extension.
|
||||
|
||||
### Routes
|
||||
|
||||
The Backstage routing system adds a layer of indirection that makes it possible for plugins to route to each other's extensions without explicit knowledge of what URL paths the extension are rendered at or if they even exist at all. It makes it possible for plugins to share routes with each other and dynamically generate concrete links at runtime. It is the responsibility of the app to resolve these links to actual URLs, but it is also possible for integrators to define their own route bindings that decide how the links should be resolved. The routing system also lets plugins define internal routes, aiding in the linking to different content in the same plugin.
|
||||
|
||||
## Package structure
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,48 @@
|
||||
---
|
||||
id: apps
|
||||
title: App Instances
|
||||
sidebar_label: App
|
||||
# prettier-ignore
|
||||
description: App instances
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
|
||||
## The App Instance
|
||||
|
||||
The app instance is main entry point for creating a frontend app. It doesn't do much on its own, but is instead responsible for wiring things together that have been provided as features from other parts of the system.
|
||||
|
||||
Below is a simple example of how to create and render an app instance:
|
||||
|
||||
```ts
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { createApp } from '@backstage/frontend-app-api';
|
||||
|
||||
// Create your app instance
|
||||
const app = createApp({
|
||||
// Features such as plugins can be installed explicitly, but we will explore other options later on
|
||||
features: [catalogPlugin],
|
||||
});
|
||||
|
||||
// This creates a React element that renders the entire app
|
||||
const root = app.createRoot();
|
||||
|
||||
// Just like any other React we need a root element. No server side rendering is used.
|
||||
const rootEl = document.getElementById('root')!;
|
||||
|
||||
ReactDOM.createRoot(rootEl).render(app);
|
||||
```
|
||||
|
||||
We call `createApp` to create a new app instance, which is responsible for wiring together all of the features that we provide to the app. It also provides a set of built-in [Extensions](./03-extensions.md) that help build out the foundations of the app, as well as defaults for many other systems such as [Utility API](./06-utility-apis.md) implementations, components, icons, themes, and how to load configuration. No real work is done at the point of creating the app though, it's all deferred to the rendering of the element returned from `app.createRoot()`.
|
||||
|
||||
It is possible to explicitly install features when creating the app, although typically these will instead be discovered automatically which we'll explore later on. Nevertheless these features are what build out the actual functionality of the app by providing [Extensions](./03-extensions.md). These extensions are wired together by the app into a tree structure known as the app extension tree. Each node in this tree receives data from its child nodes, and pass along data to its parent. The following diagram illustrates the shape of a small app extension tree.
|
||||
|
||||

|
||||
|
||||
Each node in this tree is an extension with a parent node and children. The colored shapes represent extension data inputs and output, where each color is one unique type of data. You can see that there are both extensions that output data that is ignored by the parent, as well as extensions that accept inputs but do not have any children. There are a couple of different tools at your disposal when creating and extension that lets you define different requirements for your inputs and output, which we will cover in greater details in the [Extensions](./03-extensions.md) section.
|
||||
|
||||
A common type of data that is shared between extensions is React elements and components. These can in turn be rendered by each other in their own React components, which ends up forming a parallel tree of React components that is similar in shape to that of the app extension tree. At the top of the app extension tree is a built-in root extension that among other things outputs a React element. This element also ends up being the root of the parallel React tree, and is rendered by the React element returned by `app.createRoot()`.
|
||||
|
||||
## Feature Discovery
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,104 @@
|
||||
---
|
||||
id: extensions
|
||||
title: Frontend Extensions
|
||||
sidebar_label: Extensions
|
||||
# prettier-ignore
|
||||
description: Frontend extensions
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
|
||||
As mentioned in the [previous section](./02-app.md), Backstage apps are built up from a tree of extensions. This section will go into more detail about what extensions are, how to create and use them, and how to create your own extensibility patterns.
|
||||
|
||||
## Extension Structure
|
||||
|
||||
Each extensions has a number of different properties that define how it behaves and how it interacts with other extensions and the rest of the app. Some of these properties are fixed, while others can be customized by integrators. The diagram below illustrates the structure of an extension.
|
||||
|
||||

|
||||
|
||||
### ID
|
||||
|
||||
The ID of an extension is used to uniquely identity it, and it should ideally by unique across the entire Backstage ecosystem. For each frontend app instance there can only be a single extension for any given ID. Installing multiple extensions with the same ID will either result in an error or one of the extensions will override the others. The ID is also used to reference the extensions from other extensions, in configuration, and in other places such as developer tools and analytics.
|
||||
|
||||
### Output
|
||||
|
||||
The output of an extension is the data that it provides to its parent extension, and ultimately its contribution to the app. The output itself comes in the form of a collection of arbitrary values, anything that can be represented as a TypeScript type. However, each individual output value must be associated with a shared reference known as an extension data reference. You must also use these same references to be able to access individual output values of an extension.
|
||||
|
||||
### Inputs
|
||||
|
||||
The inputs of an extension define the data that it received from its children. Each extension can have multiple different inputs identified by an input name. These inputs each have their own set of data that they expect, which is defined as a collection of extension data references. An extension will only have access to the data that it has explicitly requested from each input.
|
||||
|
||||
### Attachment Point
|
||||
|
||||
The attachment point of an extension decides where in the app extension tree it will be located. It is defined by the ID of the parent extension, as well as the name of the input to attach to. Through the attachment point the extension will share its own output as inputs to the parent extension. An extension can only be attached to an input that matches its own output, it is an error to try to attach an extension to an input the requires data that the extension does not provide in its output.
|
||||
|
||||
The attachment point is one of the configurable properties of an extension, and can be overridden by integrators. In doing so, care must be taken to make sure that one doesn't attach an extension to an incompatible input. Extensions can also only be attached to a single input and parent at a time. This means that the app extension tree can not contain any cycles, as the extension ancestry will either be terminated at the root, or be detached from it.
|
||||
|
||||
### Disabled
|
||||
|
||||
Each extension in the app can be disabled, meaning it will not be instantiated and its parent will effectively not see it in its inputs. When creating an extension you can also specify whether extensions should be disabled by default. This makes it possible to for example install multiple extensions in an app, but only choose to enable one or a few of them depending on the environment.
|
||||
|
||||
The ordering of extensions is sometimes very important, as it may for example affect in which order they show up in the UI. When an extension is toggled from disabled to enabled through configuration it resets the ordering of the extension, pushing it to the end of the list. It is generally recommended to leave extensions as disabled by default if their order is important, allowing for the order in which their are enabled in the configuration to determine their order in the app.
|
||||
|
||||
### Configuration & Configuration Schema
|
||||
|
||||
Each extension can define a configuration schema that describes the configuration that it accepts. This schema is used to validate the configuration provided by integrators, but also to fill in default configuration values. The configuration itself is provided by integrators in order to customize the extension. It is not possible to provide a default configuration of an extension, this must instead be done through defaults in the configuration schema. This allows for a simpler configuration logic where multiple configurations of the same extension completely replace each other rather than being merged.
|
||||
|
||||
### Factory
|
||||
|
||||
The extension factory is the implementation of the extension itself. It is a function that is provided with any inputs and configuration that the extension received, and must produce the output that it defined. When an app instance starts up it will call the factory function of each extension that is part of the app, starting at leaf nodes and working its way up to the root of the app extension tree. The factory will only be called for active extensions, which is an extension that is not disabled and has an active parent.
|
||||
|
||||
Extension factories should be lean and not do any heavy lifting or async work, as they are called during the initialization of the app. For example, if you need to do an expensive computation to generate your output, then prefer outputting a callback that does the computation instead. This allows the parent extension to defer the computation for later so that you avoid blocking the app startup.
|
||||
|
||||
## Creating an Extensions
|
||||
|
||||
Extensions are created using the `createExtension` function from `@backstage/frontend-plugin-api`. At minimum you need to provide an ID, attachment point, output definition, and a factory function. The following example shows the creation of a minimal extension:
|
||||
|
||||
```tsx
|
||||
const extension = createExtension({
|
||||
id: 'my-extension',
|
||||
// This is the attachment point, `id` is the ID of the parent extension,
|
||||
// while `input` is the name of the input to attach to.
|
||||
attachTo: { id: 'my-parent', input: 'content' },
|
||||
// The output map defines the outputs of the extension. The object keys
|
||||
// are only used internally to map the outputs of the factory and do
|
||||
// not need to match the keys of the input.
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
// This factory is called to instantiate the extensions and produce its output.
|
||||
factory({ bind }) {
|
||||
bind({
|
||||
element: <div>Hello World</div>,
|
||||
});
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
Note that while the `createExtension` is public API and used in many places, it is not typically what you use when building plugins and features. Instead there are many extension creator functions exported by both the core APIs and plugins that make it easier to create extensions for more specific usages.
|
||||
|
||||
... TODO ...
|
||||
|
||||
## Extension Data
|
||||
|
||||
TODO
|
||||
|
||||
## Extension Inputs
|
||||
|
||||
TODO
|
||||
|
||||
## Configuration
|
||||
|
||||
TODO
|
||||
|
||||
## Configuration Schema
|
||||
|
||||
TODO
|
||||
|
||||
## Extension Creators
|
||||
|
||||
TODO
|
||||
|
||||
## Extension Boundary
|
||||
|
||||
TODO
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
id: plugins
|
||||
title: Frontend Plugins
|
||||
sidebar_label: Plugins
|
||||
# prettier-ignore
|
||||
description: Frontend plugins
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
id: extension overrides
|
||||
title: Frontend Extension Overrides
|
||||
sidebar_label: Extension Overrides
|
||||
# prettier-ignore
|
||||
description: Frontend extension overrides
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
id: utility-apis
|
||||
title: Utility APIs
|
||||
sidebar_label: Utility APIs
|
||||
# prettier-ignore
|
||||
description: Utility APIs
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
|
||||
See [Utility APIs docs](../../api/utility-apis.md).
|
||||
@@ -0,0 +1,11 @@
|
||||
---
|
||||
id: routes
|
||||
title: Frontend Routes
|
||||
sidebar_label: Routes
|
||||
# prettier-ignore
|
||||
description: Frontend routes
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
|
||||
See [routing system docs](../../plugins/composability.md#routing-system)
|
||||
@@ -0,0 +1,15 @@
|
||||
---
|
||||
id: index
|
||||
title: The Frontend System
|
||||
sidebar_label: Introduction
|
||||
# prettier-ignore
|
||||
description: The Frontend System
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
|
||||
## Status
|
||||
|
||||
The new frontend system is in an experimental phase and we do not recommend any plugins or apps to migrate.
|
||||
|
||||
You can find an example app setup in [the `app-next` package](https://github.com/backstage/backstage/tree/master/packages/app-next).
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
id: org
|
||||
title: Microsoft Entra tenantal Data
|
||||
title: Microsoft Entra Tenant Data
|
||||
sidebar_label: Org Data
|
||||
# prettier-ignore
|
||||
description: Importing users and groups from Microsoft Entra ID into Backstage
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
---
|
||||
title: Nexus Repository Manager
|
||||
author: Red Hat
|
||||
authorUrl: https://redhat.com
|
||||
category: Image
|
||||
description: View information about the build artifacts in your Nexus Repository Manager in Backstage.
|
||||
documentation: https://janus-idp.io/plugins/nexus-repository-manager
|
||||
iconUrl: https://janus-idp.io/images/plugins/nexus-repository-manager.svg
|
||||
npmPackageName: '@janus-idp/backstage-plugin-nexus-repository-manager'
|
||||
addedDate: '2023-10-25'
|
||||
+3
-3
@@ -3265,9 +3265,9 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/webpack-env@npm:^1.18.0":
|
||||
version: 1.18.2
|
||||
resolution: "@types/webpack-env@npm:1.18.2"
|
||||
checksum: 883908ade827d35a10efc574fb6f2728a7c520d4296cf1507633ac7457204ccd697bc6c8cadac99bc5d96074a6109c658ebfde59f42ba5ba0fdfffc538892b0f
|
||||
version: 1.18.3
|
||||
resolution: "@types/webpack-env@npm:1.18.3"
|
||||
checksum: f24e82485d8e325b1875608766ba6dad2b2f53a0fb182bc96173b4590110c6b791163402cdf19b50c04e8c05292f227a08aafe9230b2bba52c40c5f7ceccc101
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import { HostDiscovery as HostDiscovery_2 } from '@backstage/backend-app-api';
|
||||
import { IdentityService } from '@backstage/backend-plugin-api';
|
||||
import { isChildPath } from '@backstage/cli-common';
|
||||
import { Knex } from 'knex';
|
||||
import knexFactory from 'knex';
|
||||
import { KubeConfig } from '@kubernetes/client-node';
|
||||
import { LifecycleService } from '@backstage/backend-plugin-api';
|
||||
import { LoadConfigOptionsRemote } from '@backstage/config-loader';
|
||||
@@ -222,7 +223,7 @@ export function createDatabaseClient(
|
||||
lifecycle: LifecycleService;
|
||||
pluginMetadata: PluginMetadataService;
|
||||
},
|
||||
): Knex<any, any[]>;
|
||||
): knexFactory.Knex<any, any[]>;
|
||||
|
||||
// @public
|
||||
export function createRootLogger(
|
||||
|
||||
@@ -89,14 +89,14 @@
|
||||
"isomorphic-git": "^1.23.0",
|
||||
"jose": "^4.6.0",
|
||||
"keyv": "^4.5.2",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"logform": "^2.3.2",
|
||||
"luxon": "^3.0.0",
|
||||
"minimatch": "^5.0.0",
|
||||
"mysql2": "^2.2.5",
|
||||
"node-fetch": "^2.6.7",
|
||||
"pg": "^8.3.0",
|
||||
"pg": "^8.11.3",
|
||||
"raw-body": "^2.4.1",
|
||||
"tar": "^6.1.12",
|
||||
"uuid": "^8.3.2",
|
||||
@@ -129,7 +129,7 @@
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@types/yauzl": "^2.10.0",
|
||||
"aws-sdk-client-mock": "^2.0.0",
|
||||
"better-sqlite3": "^8.0.0",
|
||||
"better-sqlite3": "^9.0.0",
|
||||
"http-errors": "^2.0.0",
|
||||
"msw": "^1.0.0",
|
||||
"mysql2": "^2.2.5",
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"@backstage/types": "workspace:^",
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"knex": "^2.0.0"
|
||||
"knex": "^3.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "workspace:^"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"@opentelemetry/api": "^1.3.0",
|
||||
"@types/luxon": "^3.0.0",
|
||||
"cron": "^2.0.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.0",
|
||||
"uuid": "^8.0.0",
|
||||
|
||||
@@ -49,13 +49,13 @@
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/plugin-auth-node": "workspace:^",
|
||||
"@backstage/types": "workspace:^",
|
||||
"better-sqlite3": "^8.0.0",
|
||||
"better-sqlite3": "^9.0.0",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "^10.0.1",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"msw": "^1.0.0",
|
||||
"mysql2": "^2.2.5",
|
||||
"pg": "^8.3.0",
|
||||
"pg": "^8.11.3",
|
||||
"testcontainers": "^8.1.2",
|
||||
"textextensions": "^5.16.0",
|
||||
"uuid": "^8.0.0"
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"@opentelemetry/exporter-prometheus": "^0.44.0",
|
||||
"@opentelemetry/sdk-metrics": "^1.13.0",
|
||||
"azure-devops-node-api": "^11.0.1",
|
||||
"better-sqlite3": "^8.0.0",
|
||||
"better-sqlite3": "^9.0.0",
|
||||
"dockerode": "^3.3.1",
|
||||
"example-app": "link:../app",
|
||||
"express": "^4.17.1",
|
||||
@@ -92,7 +92,7 @@
|
||||
"express-promise-router": "^4.1.0",
|
||||
"luxon": "^3.0.0",
|
||||
"mysql2": "^2.2.5",
|
||||
"pg": "^8.3.0",
|
||||
"pg": "^8.11.3",
|
||||
"pg-connection-string": "^2.3.0",
|
||||
"prom-client": "^14.0.1",
|
||||
"winston": "^3.2.1"
|
||||
|
||||
+7
-3
@@ -55,8 +55,12 @@ export class IdentityAuthInjectorFetchMiddleware implements FetchMiddleware {
|
||||
apply(next: typeof fetch): typeof fetch {
|
||||
return async (input, init) => {
|
||||
// Skip this middleware if the header already exists, or if the URL
|
||||
// doesn't match any of the allowlist items, or if there was no token
|
||||
const request = new Request(input, init);
|
||||
// doesn't match any of the allowlist items, or if there was no token.
|
||||
// NOTE(freben): The "as any" casts here and below are because of subtle
|
||||
// undici type differences that happened in a node types bump. Those are
|
||||
// immaterial to the code at hand at runtime, as the global fetch and
|
||||
// Request are always taken from the same place.
|
||||
const request = new Request(input as any, init);
|
||||
const { token } = await this.identityApi.getCredentials();
|
||||
if (
|
||||
request.headers.get(this.headerName) ||
|
||||
@@ -64,7 +68,7 @@ export class IdentityAuthInjectorFetchMiddleware implements FetchMiddleware {
|
||||
!token ||
|
||||
!this.allowUrl(request.url)
|
||||
) {
|
||||
return next(input, init);
|
||||
return next(input as any, init);
|
||||
}
|
||||
|
||||
request.headers.set(this.headerName, this.headerValue(token));
|
||||
|
||||
+7
-3
@@ -34,11 +34,15 @@ export class PluginProtocolResolverFetchMiddleware implements FetchMiddleware {
|
||||
|
||||
apply(next: typeof fetch): typeof fetch {
|
||||
return async (input, init) => {
|
||||
const request = new Request(input, init);
|
||||
// NOTE(freben): The "as any" casts here and below are because of subtle
|
||||
// undici type differences that happened in a node types bump. Those are
|
||||
// immaterial to the code at hand at runtime, as the global fetch and
|
||||
// Request are always taken from the same place.
|
||||
const request = new Request(input as any, init);
|
||||
const prefix = 'plugin://';
|
||||
|
||||
if (!request.url.startsWith(prefix)) {
|
||||
return next(input, init);
|
||||
return next(input as any, init);
|
||||
}
|
||||
|
||||
// Switch to a known protocol, since browser URL parsing misbehaves wildly
|
||||
@@ -57,7 +61,7 @@ export class PluginProtocolResolverFetchMiddleware implements FetchMiddleware {
|
||||
const target = `${join(base, pathname)}${search}${hash}`;
|
||||
return next(
|
||||
target,
|
||||
typeof input === 'string' || isUrl(input) ? init : input,
|
||||
typeof input === 'string' || isUrl(input) ? init : (input as any),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -51,4 +51,29 @@ describe('<HeaderLabel />', () => {
|
||||
expect(rendered.getByText('Value')).toBeInTheDocument();
|
||||
expect(anchor.href).toBe('http://localhost/test');
|
||||
});
|
||||
|
||||
it('should use a `p` tag if the provided value is a string', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderLabel label="Label" value="Value" />,
|
||||
);
|
||||
expect(rendered.getByText('Value').tagName).toBe('P');
|
||||
});
|
||||
|
||||
it('should use a `span` tag if the provided value is not a string', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderLabel label="Label" value={<>Value</>} />,
|
||||
);
|
||||
expect(rendered.getByText('Value').tagName).toBe('SPAN');
|
||||
});
|
||||
|
||||
it('should use the correct custom typography root component', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<HeaderLabel
|
||||
label="Label"
|
||||
value="Value"
|
||||
contentTypograpyRootComponent="tr"
|
||||
/>,
|
||||
);
|
||||
expect(rendered.container.querySelector('tr')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,12 +49,19 @@ const useStyles = makeStyles<BackstageTheme>(
|
||||
type HeaderLabelContentProps = PropsWithChildren<{
|
||||
value: React.ReactNode;
|
||||
className: string;
|
||||
typographyRootComponent?: keyof JSX.IntrinsicElements;
|
||||
}>;
|
||||
|
||||
const HeaderLabelContent = ({ value, className }: HeaderLabelContentProps) => {
|
||||
const HeaderLabelContent = ({
|
||||
value,
|
||||
className,
|
||||
typographyRootComponent,
|
||||
}: HeaderLabelContentProps) => {
|
||||
return (
|
||||
<Typography
|
||||
component={typeof value === 'string' ? 'p' : 'span'}
|
||||
component={
|
||||
typographyRootComponent ?? (typeof value === 'string' ? 'p' : 'span')
|
||||
}
|
||||
className={className}
|
||||
>
|
||||
{value}
|
||||
@@ -65,6 +72,7 @@ const HeaderLabelContent = ({ value, className }: HeaderLabelContentProps) => {
|
||||
type HeaderLabelProps = {
|
||||
label: string;
|
||||
value?: HeaderLabelContentProps['value'];
|
||||
contentTypograpyRootComponent?: HeaderLabelContentProps['typographyRootComponent'];
|
||||
url?: string;
|
||||
};
|
||||
|
||||
@@ -75,12 +83,13 @@ type HeaderLabelProps = {
|
||||
*
|
||||
*/
|
||||
export function HeaderLabel(props: HeaderLabelProps) {
|
||||
const { label, value, url } = props;
|
||||
const { label, value, url, contentTypograpyRootComponent } = props;
|
||||
const classes = useStyles();
|
||||
const content = (
|
||||
<HeaderLabelContent
|
||||
className={classes.value}
|
||||
value={value || '<Unknown>'}
|
||||
typographyRootComponent={contentTypograpyRootComponent}
|
||||
/>
|
||||
);
|
||||
return (
|
||||
|
||||
@@ -53,12 +53,15 @@ jest.mock('./versions', () => ({
|
||||
'@backstage/plugin-auth-backend': '1.0.0',
|
||||
'@backstage/plugin-auth-node': '1.0.0',
|
||||
'@backstage/plugin-catalog-backend': '1.0.0',
|
||||
'@backstage/plugin-catalog-backend-module-scaffolder-entity-model': '1.0.0',
|
||||
'@backstage/plugin-permission-common': '1.0.0',
|
||||
'@backstage/plugin-permission-node': '1.0.0',
|
||||
'@backstage/plugin-proxy-backend': '1.0.0',
|
||||
'@backstage/plugin-scaffolder-backend': '1.0.0',
|
||||
'@backstage/plugin-search-backend': '1.0.0',
|
||||
'@backstage/plugin-search-backend-module-catalog': '1.0.0',
|
||||
'@backstage/plugin-search-backend-module-pg': '1.0.0',
|
||||
'@backstage/plugin-search-backend-module-techdocs': '1.0.0',
|
||||
'@backstage/plugin-search-backend-node': '1.0.0',
|
||||
'@backstage/plugin-techdocs-backend': '1.0.0',
|
||||
'@backstage/app-defaults': '1.0.0',
|
||||
|
||||
@@ -55,6 +55,7 @@ import { version as pluginCatalog } from '../../../../plugins/catalog/package.js
|
||||
import { version as pluginCatalogCommon } from '../../../../plugins/catalog-common/package.json';
|
||||
import { version as pluginCatalogReact } from '../../../../plugins/catalog-react/package.json';
|
||||
import { version as pluginCatalogBackend } from '../../../../plugins/catalog-backend/package.json';
|
||||
import { version as pluginCatalogBackendModuleScaffolderEntityModel } from '../../../../plugins/catalog-backend-module-scaffolder-entity-model/package.json';
|
||||
import { version as pluginCatalogGraph } from '../../../../plugins/catalog-graph/package.json';
|
||||
import { version as pluginCatalogImport } from '../../../../plugins/catalog-import/package.json';
|
||||
import { version as pluginCircleci } from '../../../../plugins/circleci/package.json';
|
||||
@@ -72,7 +73,9 @@ import { version as pluginScaffolderBackend } from '../../../../plugins/scaffold
|
||||
import { version as pluginSearch } from '../../../../plugins/search/package.json';
|
||||
import { version as pluginSearchReact } from '../../../../plugins/search-react/package.json';
|
||||
import { version as pluginSearchBackend } from '../../../../plugins/search-backend/package.json';
|
||||
import { version as pluginSearchBackendModuleCatalog } from '../../../../plugins/search-backend-module-catalog/package.json';
|
||||
import { version as pluginSearchBackendModulePg } from '../../../../plugins/search-backend-module-pg/package.json';
|
||||
import { version as pluginSearchBackendModuleTechdocs } from '../../../../plugins/search-backend-module-techdocs/package.json';
|
||||
import { version as pluginSearchBackendNode } from '../../../../plugins/search-backend-node/package.json';
|
||||
import { version as pluginTechRadar } from '../../../../plugins/tech-radar/package.json';
|
||||
import { version as pluginTechdocs } from '../../../../plugins/techdocs/package.json';
|
||||
@@ -104,6 +107,8 @@ export const packageVersions = {
|
||||
'@backstage/plugin-catalog-common': pluginCatalogCommon,
|
||||
'@backstage/plugin-catalog-react': pluginCatalogReact,
|
||||
'@backstage/plugin-catalog-backend': pluginCatalogBackend,
|
||||
'@backstage/plugin-catalog-backend-module-scaffolder-entity-model':
|
||||
pluginCatalogBackendModuleScaffolderEntityModel,
|
||||
'@backstage/plugin-catalog-graph': pluginCatalogGraph,
|
||||
'@backstage/plugin-catalog-import': pluginCatalogImport,
|
||||
'@backstage/plugin-circleci': pluginCircleci,
|
||||
@@ -121,7 +126,11 @@ export const packageVersions = {
|
||||
'@backstage/plugin-search': pluginSearch,
|
||||
'@backstage/plugin-search-react': pluginSearchReact,
|
||||
'@backstage/plugin-search-backend': pluginSearchBackend,
|
||||
'@backstage/plugin-search-backend-module-catalog':
|
||||
pluginSearchBackendModuleCatalog,
|
||||
'@backstage/plugin-search-backend-module-pg': pluginSearchBackendModulePg,
|
||||
'@backstage/plugin-search-backend-module-techdocs':
|
||||
pluginSearchBackendModuleTechdocs,
|
||||
'@backstage/plugin-search-backend-node': pluginSearchBackendNode,
|
||||
'@backstage/plugin-tech-radar': pluginTechRadar,
|
||||
'@backstage/plugin-techdocs': pluginTechdocs,
|
||||
|
||||
@@ -25,20 +25,24 @@
|
||||
"@backstage/plugin-auth-backend": "^{{version '@backstage/plugin-auth-backend'}}",
|
||||
"@backstage/plugin-auth-node": "^{{version '@backstage/plugin-auth-node'}}",
|
||||
"@backstage/plugin-catalog-backend": "^{{version '@backstage/plugin-catalog-backend'}}",
|
||||
"@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "^{{version '@backstage/plugin-catalog-backend-module-scaffolder-entity-model'}}",
|
||||
"@backstage/plugin-permission-common": "^{{version '@backstage/plugin-permission-common'}}",
|
||||
"@backstage/plugin-permission-node": "^{{version '@backstage/plugin-permission-node'}}",
|
||||
"@backstage/plugin-proxy-backend": "^{{version '@backstage/plugin-proxy-backend'}}",
|
||||
"@backstage/plugin-scaffolder-backend": "^{{version '@backstage/plugin-scaffolder-backend'}}",
|
||||
"@backstage/plugin-search-backend": "^{{version '@backstage/plugin-search-backend'}}",
|
||||
"@backstage/plugin-search-backend-module-catalog": "^{{version '@backstage/plugin-search-backend-module-catalog'}}",
|
||||
"@backstage/plugin-search-backend-module-pg": "^{{version '@backstage/plugin-search-backend-module-pg'}}",
|
||||
"@backstage/plugin-search-backend-module-techdocs": "^{{version '@backstage/plugin-search-backend-module-techdocs'}}",
|
||||
"@backstage/plugin-search-backend-node": "^{{version '@backstage/plugin-search-backend-node'}}",
|
||||
"@backstage/plugin-techdocs-backend": "^{{version '@backstage/plugin-techdocs-backend'}}",
|
||||
"app": "link:../app",
|
||||
"better-sqlite3": "^8.0.0",
|
||||
"better-sqlite3": "^9.0.0",
|
||||
"dockerode": "^3.3.1",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"pg": "^8.3.0",
|
||||
"node-gyp": "^9.0.0",
|
||||
"pg": "^8.11.3",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { CatalogBuilder } from '@backstage/plugin-catalog-backend';
|
||||
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-scaffolder-backend';
|
||||
import { ScaffolderEntitiesProcessor } from '@backstage/plugin-catalog-backend-module-scaffolder-entity-model';
|
||||
import { Router } from 'express';
|
||||
import { PluginEnvironment } from '../types';
|
||||
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@ import {
|
||||
LunrSearchEngine,
|
||||
} from '@backstage/plugin-search-backend-node';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import { DefaultCatalogCollatorFactory } from '@backstage/plugin-catalog-backend';
|
||||
import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-techdocs-backend';
|
||||
import { DefaultCatalogCollatorFactory } from '@backstage/plugin-search-backend-module-catalog';
|
||||
import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs';
|
||||
import { Router } from 'express';
|
||||
|
||||
export default async function createPlugin(
|
||||
|
||||
@@ -27,7 +27,7 @@ export type AppRouteBinder = <
|
||||
) => void;
|
||||
|
||||
// @public (undocumented)
|
||||
export function createApp(options: {
|
||||
export function createApp(options?: {
|
||||
features?: (BackstagePlugin | ExtensionOverrides)[];
|
||||
configLoader?: () => Promise<ConfigApi>;
|
||||
bindRoutes?(context: { bind: AppRouteBinder }): void;
|
||||
|
||||
@@ -18,7 +18,7 @@ import { RouteRef, coreExtensionData } from '@backstage/frontend-plugin-api';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { toLegacyPlugin } from '../wiring/createApp';
|
||||
import { BackstageRouteObject } from './types';
|
||||
import { AppNode } from '../tree';
|
||||
import { AppNode } from '@backstage/frontend-plugin-api';
|
||||
|
||||
// We always add a child that matches all subroutes but without any route refs. This makes
|
||||
// sure that we're always able to match each route no matter how deep the navigation goes.
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
import { readAppExtensionsConfig } from './readAppExtensionsConfig';
|
||||
import { resolveAppTree } from './resolveAppTree';
|
||||
import { resolveAppNodeSpecs } from './resolveAppNodeSpecs';
|
||||
import { AppTree } from './types';
|
||||
import { AppTree } from '@backstage/frontend-plugin-api';
|
||||
import { Config } from '@backstage/config';
|
||||
import { instantiateAppNodeTree } from './instantiateAppNodeTree';
|
||||
|
||||
|
||||
@@ -14,10 +14,4 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type {
|
||||
AppNode,
|
||||
AppNodeEdges,
|
||||
AppNodeInstance,
|
||||
AppNodeSpec,
|
||||
} from './types';
|
||||
export { createAppTree } from './createAppTree';
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
createAppNodeInstance,
|
||||
instantiateAppNodeTree,
|
||||
} from './instantiateAppNodeTree';
|
||||
import { AppNodeInstance, AppNodeSpec } from './types';
|
||||
import { AppNodeInstance, AppNodeSpec } from '@backstage/frontend-plugin-api';
|
||||
import { resolveAppTree } from './resolveAppTree';
|
||||
|
||||
const testDataRef = createExtensionDataRef<string>('test');
|
||||
@@ -167,7 +167,8 @@ describe('instantiateAppNodeTree', () => {
|
||||
{
|
||||
...makeSpec(simpleExtension),
|
||||
id: 'child-node',
|
||||
attachTo: { id: 'root-node', input: 'test' },
|
||||
// Using an invalid input should not be an error when disabled
|
||||
attachTo: { id: 'root-node', input: 'invalid' },
|
||||
disabled: true,
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -20,7 +20,11 @@ import {
|
||||
ExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import mapValues from 'lodash/mapValues';
|
||||
import { AppNode, AppNodeInstance, AppNodeSpec } from './types';
|
||||
import {
|
||||
AppNode,
|
||||
AppNodeInstance,
|
||||
AppNodeSpec,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
type Mutable<T> = {
|
||||
-readonly [P in keyof T]: T[P];
|
||||
@@ -176,7 +180,9 @@ export function instantiateAppNodeTree(rootNode: AppNode): void {
|
||||
}
|
||||
return [{ id: child.spec.id, instance: childInstance }];
|
||||
});
|
||||
instantiatedAttachments.set(input, instantiatedChildren);
|
||||
if (instantiatedChildren.length > 0) {
|
||||
instantiatedAttachments.set(input, instantiatedChildren);
|
||||
}
|
||||
}
|
||||
|
||||
(node as Mutable<AppNode>).instance = createAppNodeInstance({
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides';
|
||||
import { ExtensionParameters } from './readAppExtensionsConfig';
|
||||
import { AppNodeSpec } from './types';
|
||||
import { AppNodeSpec } from '@backstage/frontend-plugin-api';
|
||||
|
||||
/** @internal */
|
||||
export function resolveAppNodeSpecs(options: {
|
||||
|
||||
@@ -14,7 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { AppTree, AppNode, AppNodeInstance, AppNodeSpec } from './types';
|
||||
import {
|
||||
AppTree,
|
||||
AppNode,
|
||||
AppNodeInstance,
|
||||
AppNodeSpec,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
function indent(str: string) {
|
||||
return str.replace(/^/gm, ' ');
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
AppTreeApi,
|
||||
appTreeApiRef,
|
||||
createPageExtension,
|
||||
createPlugin,
|
||||
createThemeExtension,
|
||||
@@ -23,6 +25,7 @@ import { screen, waitFor } from '@testing-library/react';
|
||||
import { createApp } from './createApp';
|
||||
import { MockConfigApi, renderWithEffects } from '@backstage/test-utils';
|
||||
import React from 'react';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
describe('createApp', () => {
|
||||
it('should allow themes to be installed', async () => {
|
||||
@@ -90,4 +93,58 @@ describe('createApp', () => {
|
||||
expect(screen.getByText('Last Page')).toBeInTheDocument(),
|
||||
);
|
||||
});
|
||||
|
||||
it('should make the app structure available through the AppTreeApi', async () => {
|
||||
let appTreeApi: AppTreeApi | undefined = undefined;
|
||||
|
||||
const app = createApp({
|
||||
configLoader: async () => new MockConfigApi({}),
|
||||
features: [
|
||||
createPlugin({
|
||||
id: 'my-plugin',
|
||||
extensions: [
|
||||
createPageExtension({
|
||||
id: 'plugin.my-plugin.page',
|
||||
defaultPath: '/',
|
||||
loader: async () => {
|
||||
const Component = () => {
|
||||
appTreeApi = useApi(appTreeApiRef);
|
||||
return <div>My Plugin Page</div>;
|
||||
};
|
||||
return <Component />;
|
||||
},
|
||||
}),
|
||||
],
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
await renderWithEffects(app.createRoot());
|
||||
|
||||
expect(appTreeApi).toBeDefined();
|
||||
const { tree } = appTreeApi!.getTree();
|
||||
|
||||
expect(String(tree.root)).toMatchInlineSnapshot(`
|
||||
"<core out=[core.reactElement]>
|
||||
root [
|
||||
<core.layout out=[core.reactElement]>
|
||||
content [
|
||||
<core.routes out=[core.reactElement]>
|
||||
routes [
|
||||
<plugin.my-plugin.page out=[core.routing.path, core.routing.ref, core.reactElement] />
|
||||
]
|
||||
</core.routes>
|
||||
]
|
||||
nav [
|
||||
<core.nav out=[core.reactElement] />
|
||||
]
|
||||
</core.layout>
|
||||
]
|
||||
themes [
|
||||
<themes.light out=[core.theme] />
|
||||
<themes.dark out=[core.theme] />
|
||||
]
|
||||
</core>"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
import React, { JSX } from 'react';
|
||||
import { ConfigReader, Config } from '@backstage/config';
|
||||
import {
|
||||
AppTree,
|
||||
appTreeApiRef,
|
||||
BackstagePlugin,
|
||||
coreExtensionData,
|
||||
ExtensionDataRef,
|
||||
@@ -87,7 +89,8 @@ import { AppRouteBinder } from '../routing';
|
||||
import { RoutingProvider } from '../routing/RoutingProvider';
|
||||
import { resolveRouteBindings } from '../routing/resolveRouteBindings';
|
||||
import { collectRouteIds } from '../routing/collectRouteIds';
|
||||
import { AppNode, createAppTree } from '../tree';
|
||||
import { createAppTree } from '../tree';
|
||||
import { AppNode } from '@backstage/frontend-plugin-api';
|
||||
|
||||
const builtinExtensions = [
|
||||
Core,
|
||||
@@ -222,7 +225,7 @@ function deduplicateFeatures(
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export function createApp(options: {
|
||||
export function createApp(options?: {
|
||||
features?: (BackstagePlugin | ExtensionOverrides)[];
|
||||
configLoader?: () => Promise<ConfigApi>;
|
||||
bindRoutes?(context: { bind: AppRouteBinder }): void;
|
||||
@@ -240,11 +243,11 @@ export function createApp(options: {
|
||||
);
|
||||
|
||||
const discoveredFeatures = getAvailableFeatures(config);
|
||||
const loadedFeatures = (await options.featureLoader?.({ config })) ?? [];
|
||||
const loadedFeatures = (await options?.featureLoader?.({ config })) ?? [];
|
||||
const allFeatures = deduplicateFeatures([
|
||||
...discoveredFeatures,
|
||||
...loadedFeatures,
|
||||
...(options.features ?? []),
|
||||
...(options?.features ?? []),
|
||||
]);
|
||||
|
||||
const tree = createAppTree({
|
||||
@@ -262,13 +265,13 @@ export function createApp(options: {
|
||||
const routeIds = collectRouteIds(allFeatures);
|
||||
|
||||
const App = () => (
|
||||
<ApiProvider apis={createApiHolder(tree.root, config)}>
|
||||
<ApiProvider apis={createApiHolder(tree, config)}>
|
||||
<AppContextProvider appContext={appContext}>
|
||||
<AppThemeProvider>
|
||||
<RoutingProvider
|
||||
{...extractRouteInfoFromAppNode(tree.root)}
|
||||
routeBindings={resolveRouteBindings(
|
||||
options.bindRoutes,
|
||||
options?.bindRoutes,
|
||||
config,
|
||||
routeIds,
|
||||
)}
|
||||
@@ -356,17 +359,17 @@ function createLegacyAppContext(plugins: BackstagePlugin[]): AppContext {
|
||||
};
|
||||
}
|
||||
|
||||
function createApiHolder(core: AppNode, configApi: ConfigApi): ApiHolder {
|
||||
function createApiHolder(tree: AppTree, configApi: ConfigApi): ApiHolder {
|
||||
const factoryRegistry = new ApiFactoryRegistry();
|
||||
|
||||
const pluginApis =
|
||||
core.edges.attachments
|
||||
tree.root.edges.attachments
|
||||
.get('apis')
|
||||
?.map(e => e.instance?.getData(coreExtensionData.apiFactory))
|
||||
.filter((x): x is AnyApiFactory => !!x) ?? [];
|
||||
|
||||
const themeExtensions =
|
||||
core.edges.attachments
|
||||
tree.root.edges.attachments
|
||||
.get('themes')
|
||||
?.map(e => e.instance?.getData(coreExtensionData.theme))
|
||||
.filter((x): x is AppTheme => !!x) ?? [];
|
||||
@@ -414,6 +417,14 @@ function createApiHolder(core: AppNode, configApi: ConfigApi): ApiHolder {
|
||||
},
|
||||
});
|
||||
|
||||
factoryRegistry.register('static', {
|
||||
api: appTreeApiRef,
|
||||
deps: {},
|
||||
factory: () => ({
|
||||
getTree: () => ({ tree }),
|
||||
}),
|
||||
});
|
||||
|
||||
factoryRegistry.register('static', {
|
||||
api: appThemeApiRef,
|
||||
deps: {},
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import { AnyApiFactory } from '@backstage/core-plugin-api';
|
||||
import { AnyApiRef } from '@backstage/core-plugin-api';
|
||||
import { ApiRef } from '@backstage/core-plugin-api';
|
||||
import { AppTheme } from '@backstage/core-plugin-api';
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
@@ -55,6 +56,66 @@ export type AnyRoutes = {
|
||||
[name in string]: RouteRef;
|
||||
};
|
||||
|
||||
// @public
|
||||
export interface AppNode {
|
||||
readonly edges: AppNodeEdges;
|
||||
readonly instance?: AppNodeInstance;
|
||||
readonly spec: AppNodeSpec;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AppNodeEdges {
|
||||
// (undocumented)
|
||||
readonly attachedTo?: {
|
||||
node: AppNode;
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
readonly attachments: ReadonlyMap<string, AppNode[]>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AppNodeInstance {
|
||||
getData<T>(ref: ExtensionDataRef<T>): T | undefined;
|
||||
getDataRefs(): Iterable<ExtensionDataRef<unknown>>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AppNodeSpec {
|
||||
// (undocumented)
|
||||
readonly attachTo: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
// (undocumented)
|
||||
readonly config?: unknown;
|
||||
// (undocumented)
|
||||
readonly disabled: boolean;
|
||||
// (undocumented)
|
||||
readonly extension: Extension<unknown>;
|
||||
// (undocumented)
|
||||
readonly id: string;
|
||||
// (undocumented)
|
||||
readonly source?: BackstagePlugin;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AppTree {
|
||||
readonly nodes: ReadonlyMap<string, AppNode>;
|
||||
readonly orphans: Iterable<AppNode>;
|
||||
readonly root: AppNode;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AppTreeApi {
|
||||
getTree(): {
|
||||
tree: AppTree;
|
||||
};
|
||||
}
|
||||
|
||||
// @public
|
||||
export const appTreeApiRef: ApiRef<AppTreeApi>;
|
||||
|
||||
// @public (undocumented)
|
||||
export interface BackstagePlugin<
|
||||
Routes extends AnyRoutes = AnyRoutes,
|
||||
|
||||
+34
-21
@@ -14,20 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
BackstagePlugin,
|
||||
Extension,
|
||||
ExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
/*
|
||||
NOTE: These types are marked as @internal for now, but the intention is for this to be a public API in the future.
|
||||
*/
|
||||
import { createApiRef } from '@backstage/core-plugin-api';
|
||||
import { BackstagePlugin, Extension, ExtensionDataRef } from '../../wiring';
|
||||
|
||||
/**
|
||||
* The specification for this node in the app tree.
|
||||
* The specification for this {@link AppNode} in the {@link AppTree}.
|
||||
*
|
||||
* @internal
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* The specifications for a collection of app nodes is all the information needed
|
||||
@@ -43,9 +36,9 @@ export interface AppNodeSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* The connections from this node to other nodes.
|
||||
* The connections from this {@link AppNode} to other nodes.
|
||||
*
|
||||
* @internal
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* The app node edges are resolved based on the app node specs, regardless of whether
|
||||
@@ -57,9 +50,9 @@ export interface AppNodeEdges {
|
||||
}
|
||||
|
||||
/**
|
||||
* The instance of this node in the app tree.
|
||||
* The instance of this {@link AppNode} in the {@link AppTree}.
|
||||
*
|
||||
* @internal
|
||||
* @public
|
||||
* @remarks
|
||||
*
|
||||
* The app node instance is created when the `factory` function of an extension is called.
|
||||
@@ -74,8 +67,9 @@ export interface AppNodeInstance {
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in the {@link AppTree}.
|
||||
*
|
||||
* @internal
|
||||
* @public
|
||||
*/
|
||||
export interface AppNode {
|
||||
/** The specification for how this node should be instantiated */
|
||||
@@ -87,15 +81,34 @@ export interface AppNode {
|
||||
}
|
||||
|
||||
/**
|
||||
* The app tree containing all nodes of the app.
|
||||
* The app tree containing all {@link AppNode}s of the app.
|
||||
*
|
||||
* @internal
|
||||
* @public
|
||||
*/
|
||||
export interface AppTree {
|
||||
/** The root node of the app */
|
||||
root: AppNode;
|
||||
readonly root: AppNode;
|
||||
/** A map of all nodes in the app by ID, including orphaned or disabled nodes */
|
||||
nodes: ReadonlyMap<string /* id */, AppNode>;
|
||||
readonly nodes: ReadonlyMap<string /* id */, AppNode>;
|
||||
/** A sequence of all nodes with a parent that is not reachable from the app root node */
|
||||
orphans: Iterable<AppNode>;
|
||||
readonly orphans: Iterable<AppNode>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The API for interacting with the {@link AppTree}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AppTreeApi {
|
||||
/**
|
||||
* Get the {@link AppTree} for the app.
|
||||
*/
|
||||
getTree(): { tree: AppTree };
|
||||
}
|
||||
|
||||
/**
|
||||
* The `ApiRef` of {@link AppTreeApi}.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const appTreeApiRef = createApiRef<AppTreeApi>({ id: 'core.app-tree' });
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export {
|
||||
appTreeApiRef,
|
||||
type AppNode,
|
||||
type AppNodeEdges,
|
||||
type AppNodeInstance,
|
||||
type AppNodeSpec,
|
||||
type AppTree,
|
||||
type AppTreeApi,
|
||||
} from './AppTreeApi';
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './definitions';
|
||||
@@ -20,6 +20,7 @@
|
||||
* @packageDocumentation
|
||||
*/
|
||||
|
||||
export * from './apis';
|
||||
export * from './components';
|
||||
export * from './extensions';
|
||||
export * from './routing';
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"fs-extra": "10.1.0",
|
||||
"globby": "^11.0.0",
|
||||
"helmet": "^6.0.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.0",
|
||||
"winston": "^3.2.1",
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/passport": "^1.0.3",
|
||||
"compression": "^1.7.4",
|
||||
"connect-session-knex": "^3.0.1",
|
||||
"connect-session-knex": "^4.0.0",
|
||||
"cookie-parser": "^1.4.5",
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
@@ -61,7 +61,7 @@
|
||||
"google-auth-library": "^8.0.0",
|
||||
"jose": "^4.6.0",
|
||||
"jwt-decode": "^3.1.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.0",
|
||||
"minimatch": "^5.0.0",
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"knex": "^2.4.2",
|
||||
"knex": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"supertest": "^6.3.3",
|
||||
"uuid": "^9.0.0",
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
"@types/express": "^4.17.6",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"winston": "^3.2.1",
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
|
||||
### What is the Bazaar?
|
||||
|
||||
The Bazaar is a place where teams can propose projects for cross-functional team development. Essentially a marketplace for internal projects suitable for [Inner Sourcing](https://en.wikipedia.org/wiki/Inner_source). With "Inner Sourcing", we mean projects that are developed internally within a company, but with Open Source best practices.
|
||||
The Bazaar is a place where teams can propose projects for cross-functional team development. Essentially, it’s a marketplace for internal projects suitable for [Inner Sourcing](https://en.wikipedia.org/wiki/Inner_source). By “Inner Sourcing,” we mean projects that are developed internally within a company but follow Open Source best practices.
|
||||
|
||||
### Why?
|
||||
|
||||
Many companies today are of high need to increase the ease of cross-team cooperation. In large organizations, engineers often have limited ways of discovering or announcing the projects which could benefit from a wider development effort in terms of different expertise, experiences, and teams spread across the organization. With no good way to find these existing internal projects to join, the possibility of working with Inner Sourcing practices suffers.
|
||||
Many companies today have a high need to increase the ease of cross-team cooperation. In large organizations, engineers often have limited ways of discovering or announcing projects that could benefit from a wider development effort in terms of different expertise, experiences, and teams spread across the organization. With no good way to find these existing internal projects to join, the possibility of working with Inner Sourcing practices suffers.
|
||||
|
||||
### How?
|
||||
|
||||
The Bazaar allows engineers and teams to open up and announce their new and exciting projects for transparent cooperation in other parts of larger organizations. The Bazaar ensures that new Inner Sourcing friendly projects gain visibility through Backstage and a way for interested engineers to show their interest and in the future contribute with their specific skill set. The Bazaar also provides an easy way to manage, catalog, and browse these Inner Sourcing friendly projects and components.
|
||||
The Bazaar allows engineers and teams to open up and announce their new and exciting projects for transparent cooperation in other parts of larger organizations. The Bazaar ensures that new Inner Sourcing-friendly projects gain visibility through Backstage and a way for interested engineers to show their interest and, in the future, contribute with their specific skill set. The Bazaar also provides an easy way to manage, catalog, and browse these Inner Sourcing-friendly projects and components.
|
||||
|
||||
# Note
|
||||
|
||||
|
||||
@@ -92,7 +92,7 @@ export class GitLabClient {
|
||||
options?: CommonListOptions,
|
||||
): Promise<PagedResponse<GitLabUser>> {
|
||||
return this.pagedRequest(
|
||||
`/groups/${encodeURIComponent(groupPath)}/members`,
|
||||
`/groups/${encodeURIComponent(groupPath)}/members/all`,
|
||||
{
|
||||
...options,
|
||||
show_seat_info: true,
|
||||
|
||||
+1
-1
@@ -569,7 +569,7 @@ describe('GitlabOrgDiscoveryEntityProvider', () => {
|
||||
),
|
||||
),
|
||||
rest.get(
|
||||
`https://gitlab.com/api/v4/groups/group1/members`,
|
||||
`https://gitlab.com/api/v4/groups/group1/members/all`,
|
||||
(_req, res, ctx) => {
|
||||
const response = [
|
||||
{
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
"@types/luxon": "^3.0.0",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"luxon": "^3.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"winston": "^3.2.1"
|
||||
|
||||
@@ -261,6 +261,17 @@ export function readMicrosoftGraphOrg(
|
||||
groups: GroupEntity[];
|
||||
}>;
|
||||
|
||||
// @public
|
||||
export function readProviderConfig(
|
||||
id: string,
|
||||
config: Config,
|
||||
): MicrosoftGraphProviderConfig;
|
||||
|
||||
// @public
|
||||
export function readProviderConfigs(
|
||||
config: Config,
|
||||
): MicrosoftGraphProviderConfig[];
|
||||
|
||||
// @public
|
||||
export type UserTransformer = (
|
||||
user: MicrosoftGraph.User,
|
||||
|
||||
@@ -226,6 +226,13 @@ export function readMicrosoftGraphConfig(
|
||||
return providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses all configured providers.
|
||||
*
|
||||
* @param config - The root of the msgraph config hierarchy
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function readProviderConfigs(
|
||||
config: Config,
|
||||
): MicrosoftGraphProviderConfig[] {
|
||||
@@ -248,6 +255,14 @@ export function readProviderConfigs(
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single configured provider by id.
|
||||
*
|
||||
* @param id - the id of the provider to parse
|
||||
* @param config - The root of the msgraph config hierarchy
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export function readProviderConfig(
|
||||
id: string,
|
||||
config: Config,
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
export { MicrosoftGraphClient } from './client';
|
||||
export type { GroupMember, ODataQuery } from './client';
|
||||
export { readMicrosoftGraphConfig } from './config';
|
||||
export {
|
||||
readMicrosoftGraphConfig,
|
||||
readProviderConfigs,
|
||||
readProviderConfig,
|
||||
} from './config';
|
||||
export type { MicrosoftGraphProviderConfig } from './config';
|
||||
export {
|
||||
MICROSOFT_EMAIL_ANNOTATION,
|
||||
|
||||
@@ -38,6 +38,6 @@
|
||||
"@backstage/catalog-model": "workspace:^",
|
||||
"@backstage/plugin-auth-node": "workspace:^",
|
||||
"express-promise-router": "^4.1.1",
|
||||
"knex": "^2.4.2"
|
||||
"knex": "^3.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
"fs-extra": "10.1.0",
|
||||
"git-url-parse": "^13.0.0",
|
||||
"glob": "^7.1.6",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.0",
|
||||
"minimatch": "^5.0.0",
|
||||
@@ -93,7 +93,7 @@
|
||||
"@types/lodash": "^4.14.151",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"@types/uuid": "^8.0.0",
|
||||
"better-sqlite3": "^8.0.0",
|
||||
"better-sqlite3": "^9.0.0",
|
||||
"luxon": "^3.0.0",
|
||||
"msw": "^1.0.0",
|
||||
"supertest": "^6.1.3",
|
||||
|
||||
@@ -22,7 +22,7 @@ import { PropsWithChildren } from 'react';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { scmIntegrationsApiRef } from '@backstage/integration-react';
|
||||
import { StyleRules } from '@material-ui/core/styles/withStyles';
|
||||
import { SystemEntity } from '@backstage/catalog-model';
|
||||
import { TableColumn } from '@backstage/core-components';
|
||||
@@ -597,7 +597,7 @@ export function getEntityRelations(
|
||||
// @public (undocumented)
|
||||
export function getEntitySourceLocation(
|
||||
entity: Entity,
|
||||
scmIntegrationsApi: ScmIntegrationRegistry,
|
||||
scmIntegrationsApi: typeof scmIntegrationsApiRef.T,
|
||||
): EntitySourceLocation | undefined;
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
"@backstage/integration-react": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
"@backstage/plugin-permission-react": "workspace:^",
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Entity,
|
||||
parseLocationRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { scmIntegrationsApiRef } from '@backstage/integration-react';
|
||||
|
||||
/** @public */
|
||||
export type EntitySourceLocation = {
|
||||
@@ -30,7 +30,7 @@ export type EntitySourceLocation = {
|
||||
/** @public */
|
||||
export function getEntitySourceLocation(
|
||||
entity: Entity,
|
||||
scmIntegrationsApi: ScmIntegrationRegistry,
|
||||
scmIntegrationsApi: typeof scmIntegrationsApiRef.T,
|
||||
): EntitySourceLocation | undefined {
|
||||
const sourceLocation =
|
||||
entity.metadata.annotations?.[ANNOTATION_SOURCE_LOCATION];
|
||||
|
||||
@@ -233,4 +233,35 @@ describe('EntityLayout', () => {
|
||||
expect(screen.queryByText('tabbed-test-title-2')).not.toBeInTheDocument();
|
||||
expect(screen.getByText('tabbed-test-title-3')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders the owner links inside `p` tags', async () => {
|
||||
const mockTargetRef = 'my:target/ref';
|
||||
const ownerEntity = {
|
||||
...mockEntity,
|
||||
relations: [{ type: 'ownedBy', targetRef: mockTargetRef }],
|
||||
};
|
||||
await renderInTestApp(
|
||||
<ApiProvider apis={mockApis}>
|
||||
<EntityProvider entity={ownerEntity}>
|
||||
<EntityLayout>
|
||||
<EntityLayout.Route path="/" title="tabbed-test-title">
|
||||
<div>tabbed-test-content</div>
|
||||
</EntityLayout.Route>
|
||||
</EntityLayout>
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/catalog/:namespace/:kind/:name': entityRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const ownerLink = screen.getByText(mockTargetRef).closest('a');
|
||||
expect(ownerLink).toBeInTheDocument();
|
||||
expect(ownerLink?.tagName).toBe('A');
|
||||
const linkParent = ownerLink?.parentElement;
|
||||
expect(linkParent).toBeInTheDocument();
|
||||
expect(linkParent?.tagName).toBe('P');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -119,6 +119,7 @@ function EntityLabels(props: { entity: Entity }) {
|
||||
{ownedByRelations.length > 0 && (
|
||||
<HeaderLabel
|
||||
label="Owner"
|
||||
contentTypograpyRootComponent="p"
|
||||
value={
|
||||
<EntityRefLinks
|
||||
entityRefs={ownedByRelations}
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"body-parser-xml": "^2.0.5",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"uuid": "^8.3.2",
|
||||
"winston": "^3.2.1",
|
||||
"yn": "^4.0.0"
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
"@types/express": "*",
|
||||
"express": "^4.18.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"winston": "^3.2.1",
|
||||
"yn": "^4.0.0"
|
||||
|
||||
@@ -341,6 +341,7 @@ export type KubernetesObjectTypes =
|
||||
| 'configmaps'
|
||||
| 'deployments'
|
||||
| 'limitranges'
|
||||
| 'resourcequotas'
|
||||
| 'replicasets'
|
||||
| 'horizontalpodautoscalers'
|
||||
| 'jobs'
|
||||
|
||||
@@ -78,6 +78,12 @@ export const DEFAULT_OBJECTS: ObjectToFetch[] = [
|
||||
plural: 'limitranges',
|
||||
objectType: 'limitranges',
|
||||
},
|
||||
{
|
||||
group: '',
|
||||
apiVersion: 'v1',
|
||||
plural: 'resourcequotas',
|
||||
objectType: 'resourcequotas',
|
||||
},
|
||||
{
|
||||
group: 'apps',
|
||||
apiVersion: 'v1',
|
||||
|
||||
@@ -95,6 +95,7 @@ export type KubernetesObjectTypes =
|
||||
| 'configmaps'
|
||||
| 'deployments'
|
||||
| 'limitranges'
|
||||
| 'resourcequotas'
|
||||
| 'replicasets'
|
||||
| 'horizontalpodautoscalers'
|
||||
| 'jobs'
|
||||
|
||||
@@ -20,6 +20,7 @@ import { V1Job } from '@kubernetes/client-node';
|
||||
import { V1LimitRange } from '@kubernetes/client-node';
|
||||
import { V1Pod } from '@kubernetes/client-node';
|
||||
import { V1ReplicaSet } from '@kubernetes/client-node';
|
||||
import { V1ResourceQuota } from '@kubernetes/client-node';
|
||||
import { V1Service } from '@kubernetes/client-node';
|
||||
import { V1StatefulSet } from '@kubernetes/client-node';
|
||||
|
||||
@@ -249,6 +250,7 @@ export type FetchResponse =
|
||||
| ConfigMapFetchResponse
|
||||
| DeploymentFetchResponse
|
||||
| LimitRangeFetchResponse
|
||||
| ResourceQuotaFetchResponse
|
||||
| ReplicaSetsFetchResponse
|
||||
| HorizontalPodAutoscalersFetchResponse
|
||||
| JobsFetchResponse
|
||||
@@ -403,6 +405,14 @@ export interface ReplicaSetsFetchResponse {
|
||||
type: 'replicasets';
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export interface ResourceQuotaFetchResponse {
|
||||
// (undocumented)
|
||||
resources: Array<V1ResourceQuota>;
|
||||
// (undocumented)
|
||||
type: 'resourcequotas';
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ResourceRef {
|
||||
// (undocumented)
|
||||
|
||||
@@ -27,6 +27,7 @@ import {
|
||||
V1LimitRange,
|
||||
V1Pod,
|
||||
V1ReplicaSet,
|
||||
V1ResourceQuota,
|
||||
V1Service,
|
||||
V1StatefulSet,
|
||||
} from '@kubernetes/client-node';
|
||||
@@ -125,6 +126,7 @@ export type FetchResponse =
|
||||
| ConfigMapFetchResponse
|
||||
| DeploymentFetchResponse
|
||||
| LimitRangeFetchResponse
|
||||
| ResourceQuotaFetchResponse
|
||||
| ReplicaSetsFetchResponse
|
||||
| HorizontalPodAutoscalersFetchResponse
|
||||
| JobsFetchResponse
|
||||
@@ -171,6 +173,12 @@ export interface LimitRangeFetchResponse {
|
||||
resources: Array<V1LimitRange>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface ResourceQuotaFetchResponse {
|
||||
type: 'resourcequotas';
|
||||
resources: Array<V1ResourceQuota>;
|
||||
}
|
||||
|
||||
/** @public */
|
||||
export interface HorizontalPodAutoscalersFetchResponse {
|
||||
type: 'horizontalpodautoscalers';
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
"express": "^4.18.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"linguist-js": "^2.5.3",
|
||||
"luxon": "^2.0.2",
|
||||
"node-fetch": "^2.6.7",
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
"@types/express": "*",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"node-fetch": "^2.6.7",
|
||||
"uuid": "^8.2.0",
|
||||
"winston": "^3.2.1",
|
||||
|
||||
@@ -82,7 +82,7 @@
|
||||
"isolated-vm": "^4.5.0",
|
||||
"isomorphic-git": "^1.23.0",
|
||||
"jsonschema": "^1.2.6",
|
||||
"knex": "^2.0.0",
|
||||
"knex": "^3.0.0",
|
||||
"libsodium-wrappers": "^0.7.11",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^3.0.0",
|
||||
|
||||
@@ -370,6 +370,7 @@ export type ElasticSearchSearchEngineIndexerOptions = {
|
||||
logger: Logger | LoggerService;
|
||||
elasticSearchClientWrapper: ElasticSearchClientWrapper;
|
||||
batchSize: number;
|
||||
skipRefresh?: boolean;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
|
||||
@@ -115,6 +115,19 @@ export interface Config {
|
||||
* Eg. https://my-es-cluster.eu-west-1.es.amazonaws.com
|
||||
*/
|
||||
node: string;
|
||||
|
||||
/**
|
||||
* The AWS region.
|
||||
* Only needed if using a custom DNS record.
|
||||
*/
|
||||
region?: string;
|
||||
|
||||
/**
|
||||
* The AWS service used for request signature.
|
||||
* Either 'es' for "Managed Clusters" or 'aoss' for "Serverless".
|
||||
* Only needed if using a custom DNS record.
|
||||
*/
|
||||
service?: 'es' | 'aoss';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+11
-1
@@ -26,7 +26,10 @@ import { isEmpty, isNumber, isNaN as nan } from 'lodash';
|
||||
import { AwsSigv4Signer } from '@opensearch-project/opensearch/aws';
|
||||
import { RequestSigner } from 'aws4';
|
||||
import { Config } from '@backstage/config';
|
||||
import { ElasticSearchClientOptions } from './ElasticSearchClientOptions';
|
||||
import {
|
||||
ElasticSearchClientOptions,
|
||||
OpenSearchElasticSearchClientOptions,
|
||||
} from './ElasticSearchClientOptions';
|
||||
import { ElasticSearchClientWrapper } from './ElasticSearchClientWrapper';
|
||||
import { ElasticSearchCustomIndexTemplate } from './types';
|
||||
import { ElasticSearchSearchEngineIndexer } from './ElasticSearchSearchEngineIndexer';
|
||||
@@ -302,6 +305,11 @@ export class ElasticSearchSearchEngine implements SearchEngine {
|
||||
elasticSearchClientWrapper: this.elasticSearchClientWrapper,
|
||||
logger: indexerLogger,
|
||||
batchSize: this.batchSize,
|
||||
skipRefresh:
|
||||
(
|
||||
this
|
||||
.elasticSearchClientOptions as OpenSearchElasticSearchClientOptions
|
||||
)?.service === 'aoss',
|
||||
});
|
||||
|
||||
// Attempt cleanup upon failure.
|
||||
@@ -473,6 +481,8 @@ export class ElasticSearchSearchEngine implements SearchEngine {
|
||||
return {
|
||||
provider: 'aws',
|
||||
node: config.getString('node'),
|
||||
region: config.getOptionalString('region'),
|
||||
service,
|
||||
...(sslConfig
|
||||
? {
|
||||
ssl: {
|
||||
|
||||
+39
-1
@@ -34,6 +34,7 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
let createSpy: jest.Mock;
|
||||
let aliasesSpy: jest.Mock;
|
||||
let deleteSpy: jest.Mock;
|
||||
let refreshSpy: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
// Instantiate the indexer to be tested.
|
||||
@@ -45,6 +46,7 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
logger: getVoidLogger(),
|
||||
elasticSearchClientWrapper: clientWrapper,
|
||||
batchSize: 1000,
|
||||
skipRefresh: false,
|
||||
});
|
||||
|
||||
// Set up all requisite Elastic mocks.
|
||||
@@ -57,12 +59,13 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
},
|
||||
bulkSpy,
|
||||
);
|
||||
refreshSpy = jest.fn().mockReturnValue({});
|
||||
mock.add(
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/:index/_refresh',
|
||||
},
|
||||
jest.fn().mockReturnValue({}),
|
||||
refreshSpy,
|
||||
);
|
||||
|
||||
catSpy = jest.fn().mockReturnValue([
|
||||
@@ -212,6 +215,7 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
|
||||
// Ensure multiple bulk requests were made.
|
||||
expect(bulkSpy).toHaveBeenCalledTimes(2);
|
||||
expect(refreshSpy).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Ensure the first and last documents were included in the payloads.
|
||||
const docLocations: string[] = [
|
||||
@@ -269,6 +273,7 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
logger: getVoidLogger(),
|
||||
elasticSearchClientWrapper: mockClientWrapper,
|
||||
batchSize: 1000,
|
||||
skipRefresh: false,
|
||||
});
|
||||
|
||||
// When the indexer is run in the test pipeline
|
||||
@@ -279,4 +284,37 @@ describe('ElasticSearchSearchEngineIndexer', () => {
|
||||
// Then the pipeline should have received the expected error
|
||||
expect(error).toBe(expectedError);
|
||||
});
|
||||
|
||||
it('indexes documents, skip refresh', async () => {
|
||||
// Instantiate the indexer to be tested.
|
||||
indexer = new ElasticSearchSearchEngineIndexer({
|
||||
type: 'some-type',
|
||||
indexPrefix: '',
|
||||
indexSeparator: '-index__',
|
||||
alias: 'some-type-index__search',
|
||||
logger: getVoidLogger(),
|
||||
elasticSearchClientWrapper: clientWrapper,
|
||||
batchSize: 1000,
|
||||
skipRefresh: true,
|
||||
});
|
||||
|
||||
const documents = [
|
||||
{
|
||||
title: 'testTerm',
|
||||
text: 'testText',
|
||||
location: 'test/location',
|
||||
},
|
||||
{
|
||||
title: 'Another test',
|
||||
text: 'Some more text',
|
||||
location: 'test/location/2',
|
||||
},
|
||||
];
|
||||
|
||||
await TestPipeline.fromIndexer(indexer).withDocuments(documents).execute();
|
||||
|
||||
// Ensure bulk called but refresh not
|
||||
expect(bulkSpy).toHaveBeenCalledTimes(1);
|
||||
expect(refreshSpy).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user