Merge branch 'backstage:master' into refine-custom-permissions-doc
This commit is contained in:
@@ -0,0 +1,314 @@
|
||||
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="732px" height="462px" viewBox="-0.5 -0.5 732 462" content="<mxfile scale="1.5" border="20"><diagram id="gKBvn7eBFHudv9VMIRD-" name="Page-1">7VtZc6M4EP41foxL3PBoO8nMVu2RqtmtnTwqINuqYMTK8jW/fgVIgBC+wUlm4qmaQg1qpD6+PkQG1mSx/UJhOv+DRCgemCDaDqz7gWnajsP/zwi7guAaQUGYURwVJKMifMM/kCACQV3hCC2VBxkhMcOpSgxJkqCQKTRIKdmoj01JrL41hTOkEb6FMNap/+KIzcW2fFDRvyI8m8s3O+LGCwxfZ5SsEvG6gWlN819xewElK/H8cg4jsqmRrIeBNaGEsOJqsZ2gOJOslFox73HP3XLZFCXslAlmMWEN45XY+ShNxdLYTkoj3xDKpoCBNd7MMUPfUhhmdzdc/Zw2Z4uYjwx+OcVxPCExoflc68HN/nH6klHyimp33PyXzSAJq9GLH6frmxH7WyPK0LZGEpv7gsgCMbrjj2xVe9qpw02lU1Oqbl7Tpy1oUJjRrGRcyZJfCHG2i9bVZasJFkXc6sQwIQlqSJLL5REucJyt/iuK14jhEIobwmf8Q3JakhUNxats4UWQzpDcpfDJbBUHhUlRDBleq75xlWxsTTYpJevc59tk9Dt84fiiCAfGeJbw65BvGnHLGWdGwcUTj8SNBY6ijMeYoiX+AV9yfpn9pgQnLF+8Mx4497o898pdF3Sp5KZFltAkXqz4d5ul3oEhAJ6tmOudYHWy/AX3p2yHNdaOytVTGZDpdMlNoqm+coknaVRX6AQyGJMZJz7FqxlOBqYbc6mNXyi/mrFcnAUlk7eic/e/FZE37pa5Ykb8AQOk22KauJ8hOwzVORNu85gbhAn+RJv6o8U702It1iiUyyvWwPdXLEMurUMIBMCHALRBIACOl99pQKCIGZ1AoNOAQE/HwKAvCASaHA8jHt8l3X3PhDw0Azl+5uPMOyxJuN8KNRSjnRidApdGOe0JUcw3lEFHPvskELXcW6BoPnVEKdzVHhCotdfJNT0boKGqguOlDu5Kw6uHs/P819fct3A2itKYu9JSRwi8SGO04BrhgiMHEOSY9942hhgHrbEliIAOg4gdeGYnUcNoRA3D7iFsSIeqWdXDlqFkmasb/MVFQluzgo+bkjb8NLghHFstLvyGGamng6kUzhuDaRkkhZJMr2MwdTRNfND8V5pUJ/lvINldm/BaKnS5PSCXtN6aCv8mEXmv6S7L1/aL5brl+AbgavuaPYxh+LpkWafJBI+U7xolUX4JF2hD6OtPFNRM/2ijxehL8p5zZhRDW8y+166f84LDEaOqusgGsrhoDUvHQ1zQRfTS8M1ygqHv2jZwPS9wXRCoqRqX9NBwTMc2Lc/0fcNsFPxFwBU8rwPBoCWjuBrlCliq+geTGKOc2T8Mx5hlAhw9/XYUYM/FSphivqQCMYeiPTBcIrrGnM8+6OwfUY0XaCCzHVHdh9Fjv4hqguPtg5LWtWdL+O7DvESwvqFJZSF4mFE+bWl/eO7PlvRK88ywoaTFquJbekwixvCs1q+FGWMIDseZbNBsTVVdsXpLTESsAw2xvSpT2letka37kKWq3mlotAiaWkjS2DSbXMA/KbSdW/9Z7a/Zt6rm4x1Xi46eWn6a7qAqwW5su7Kzeq3xlnx7Nl73LOOV3cjOjNf8NN620sDU7Vk2Q9+4+bXH3DsziLYu5LUHCXpmVvWttKwMJ7wqj+PlqRnXW58eNG1et6/SybpowxlG4CkW0NExdPNAwXRUDp205Vy9LddZyfCIWDi/Uc0QEoqG0+KFv2i5YDU+3mk5KTGsnqoFt62xcXnUOnBUUgUsGaCeB1Vrak+wqo7Jy1j0rISiKwKTbGTWI5N7q0wraOYuF6ZadrNQaDLqrgfm6U2KD3qWI23+PX/LZDRCUx+H0l7bpw43dH5wkfMbXTi/dJsbfOCiO7+vJgj+hb7f5KPVax36vl7cRChFScRdH2RfMHwk//f2Rej35P9yhqxW+3D/m30mcclBVM393cAYKIVpEBwrTS//9E2iwFukBeURX7NxfC40aIxAPy0Yu3lqEnRbQ0vf+jlwp9NvSG6DO3dGo/F8CfLwYfWXHsXj1R/TWA//Aw==</diagram></mxfile>" style="background-color: rgb(255, 255, 255);">
|
||||
<defs/>
|
||||
<g>
|
||||
<rect x="19.5" y="19.5" width="375" 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: 248px; 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="138" y="37" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
App
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 214.5 289.5 L 294.95 289.5" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 302.82 289.5 L 292.32 294.75 L 294.95 289.5 L 292.32 284.25 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: 187px; margin-left: 168px;">
|
||||
<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;">
|
||||
provides
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="168" y="190" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
provides
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="79.5" y="259.5" width="135" height="60" fill="#008a00" stroke="#005700" 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: 88px; height: 1px; padding-top: 193px; margin-left: 54px;">
|
||||
<div data-drawio-colors="color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Catalog Plugin
|
||||
<br/>
|
||||
<font face="Courier New" style="font-size: 10px;">
|
||||
plugin:catalog
|
||||
</font>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="98" y="197" fill="#ffffff" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Catalog Plugin...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 214.5 169.5 L 364.5 169.5 Q 379.5 169.5 379.32 184.5 L 378.56 248.15" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 378.47 256.02 L 373.35 245.46 L 378.56 248.15 L 383.84 245.59 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: 99px; margin-left: 201px;">
|
||||
<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: 10px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
<font style="font-size: 8px;">
|
||||
replaces
|
||||
<br/>
|
||||
implementation
|
||||
<br/>
|
||||
</font>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="201" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="10px" text-anchor="middle">
|
||||
replaces...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="79.5" y="139.5" width="135" 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: 88px; height: 1px; padding-top: 113px; 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;">
|
||||
Extension Overrides
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="98" y="117" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Extension Overr...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 214.5 409.5 L 244.5 409.5 Q 259.5 409.5 274.5 409.5 L 294.95 409.5" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 302.82 409.5 L 292.32 414.75 L 294.95 409.5 L 292.32 404.25 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: 267px; margin-left: 167px;">
|
||||
<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;">
|
||||
provides
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="167" y="270" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
provides
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="79.5" y="379.5" width="135" height="60" fill="#008a00" stroke="#005700" 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: 88px; height: 1px; padding-top: 273px; margin-left: 54px;">
|
||||
<div data-drawio-colors="color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
Todo Plugin
|
||||
<br/>
|
||||
<font face="Courier New" style="font-size: 10px;">
|
||||
plugin:todo
|
||||
</font>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="98" y="277" fill="#ffffff" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Todo Plugin...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="424.5" y="19.5" width="285" 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: 188px; height: 1px; padding-top: 33px; margin-left: 284px;">
|
||||
<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;">
|
||||
Backstage Frontend Framework
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="378" y="37" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Backstage Frontend Framework
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 559.5 289.5 L 553.85 289.59" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 545.97 289.7 L 556.39 284.3 L 553.85 289.59 L 556.55 294.79 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<rect x="304.5" y="259.5" width="255" height="60" fill="#1ba1e2" stroke="#006eaf" 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: 168px; height: 1px; padding-top: 193px; margin-left: 204px;">
|
||||
<div data-drawio-colors="color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
<font style="font-size: 10px;">
|
||||
Catalog Client Utility API
|
||||
<br/>
|
||||
<font face="Courier New">
|
||||
api:plugin.catalog.service
|
||||
</font>
|
||||
</font>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="288" y="197" fill="#ffffff" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Catalog Client Utility API...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="304.5" y="379.5" width="255" height="60" fill="#1ba1e2" stroke="#006eaf" 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: 168px; height: 1px; padding-top: 273px; margin-left: 204px;">
|
||||
<div data-drawio-colors="color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
<font style="font-size: 10px;">
|
||||
Todo Utility API
|
||||
<br/>
|
||||
<font face="Courier New">
|
||||
api:plugin.todo.api
|
||||
</font>
|
||||
</font>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="288" y="277" fill="#ffffff" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Todo Utility API...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 49.5 79.5 L 49.5 151.5 Q 49.5 166.5 49.5 181.5 L 49.5 244.5 Q 49.5 259.5 60.11 270.11 L 72.75 282.75" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 78.31 288.31 L 67.18 284.6 L 72.75 282.75 L 74.6 277.18 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 49.5 169.5 L 49.5 241.5 Q 49.5 256.5 49.5 271.5 L 49.5 379.5 Q 49.5 394.5 60.23 399.86 L 70.96 405.23" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 78 408.75 L 66.26 408.75 L 70.96 405.23 L 70.96 399.36 Z" fill="rgb(0, 0, 0)" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="all"/>
|
||||
<path d="M 49.5 82.5 L 49.5 154.5 Q 49.5 169.5 59.72 169.5 L 69.95 169.5" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 77.82 169.5 L 67.32 174.75 L 69.95 169.5 L 67.32 164.25 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: 64px; margin-left: 47px;">
|
||||
<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: 10px; font-family: "Courier New"; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; background-color: rgb(255, 255, 255); white-space: nowrap;">
|
||||
<font face="Helvetica" style="font-size: 8px;">
|
||||
installs
|
||||
</font>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="47" y="67" fill="rgb(0, 0, 0)" font-family="Courier New" font-size="10px" text-anchor="middle">
|
||||
installs
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<rect x="469.5" y="139.5" width="195" height="60" fill="#1ba1e2" stroke="#006eaf" 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: 128px; height: 1px; padding-top: 113px; margin-left: 314px;">
|
||||
<div data-drawio-colors="color: #ffffff; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
|
||||
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(255, 255, 255); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
|
||||
<font style="font-size: 10px;">
|
||||
Fetch Utility API
|
||||
<br/>
|
||||
<font face="Courier New">
|
||||
api:core.fetch
|
||||
</font>
|
||||
</font>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="378" y="117" fill="#ffffff" font-family="Helvetica" font-size="12px" text-anchor="middle">
|
||||
Fetch Utility API...
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 567 79.5 L 567 129.95" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 567 137.82 L 561.75 127.32 L 567 129.95 L 572.25 127.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: 69px; margin-left: 394px;">
|
||||
<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;">
|
||||
provides
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="394" y="72" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
provides
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 432 379.5 L 432 329.05" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 432 321.18 L 437.25 331.68 L 432 329.05 L 426.75 331.68 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: 235px; margin-left: 310px;">
|
||||
<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;">
|
||||
depends on
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="310" y="238" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
depends on
|
||||
</text>
|
||||
</switch>
|
||||
</g>
|
||||
<path d="M 559.5 289.5 L 589.5 289.5 Q 604.5 289.5 604.46 274.5 L 604.27 208.75" fill="none" stroke="rgb(0, 0, 0)" stroke-width="1.5" stroke-miterlimit="10" pointer-events="stroke"/>
|
||||
<path d="M 604.25 200.88 L 609.53 211.36 L 604.27 208.75 L 599.03 211.39 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: 160px; margin-left: 425px;">
|
||||
<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;">
|
||||
depends on
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</foreignObject>
|
||||
<text x="425" y="163" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="8px" text-anchor="middle">
|
||||
depends on
|
||||
</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: 27 KiB |
@@ -6,8 +6,6 @@ sidebar_label: Overview
|
||||
description: The structure and architecture of the new Backend System and its component parts
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
## Building Blocks
|
||||
|
||||
This section introduces the high-level building blocks upon which this new
|
||||
@@ -59,7 +57,7 @@ Just like plugins, modules also have access to services and can depend on their
|
||||
|
||||
A detailed explanation of the package architecture can be found in the
|
||||
[Backstage Architecture
|
||||
Overview](../../overview/architecture-overview/#package-architecture). The
|
||||
Overview](../../overview/architecture-overview.md#package-architecture). The
|
||||
most important packages to consider for this system are the following:
|
||||
|
||||
- `plugin-<pluginId>-backend` houses the implementation of the backend plugins
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Backend
|
||||
description: Backend instances
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
## The Backend Instance
|
||||
|
||||
This is the main entry point for creating a backend. It does not have any functionality in and of itself, but is simply responsible for wiring things together.
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Services
|
||||
description: Services for backend plugins
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
Backend services provide shared functionality available to all backend plugins and modules. They are made available through service references that embed a type that represents the service interface, similar to how [Utility APIs](../../api/utility-apis.md) work in the Backstage frontend system. To use a service in your plugin or module you request an implementation of that service using the service reference.
|
||||
|
||||
The system surrounding services exists to provide a level of indirection between the service interfaces and their implementation. It is an implementation of dependency injection, where each backend instance is the dependency injection container. The implementation for each service is provided by a service factory, which encapsulates the logic for how each service instance is created.
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Plugins
|
||||
description: Backend plugins
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
Plugins provide the actual base features of a Backstage backend. Each plugin operates completely independently of all other plugins and they only communicate with each other through network calls. This means that there is a strong degree of isolation between plugins, and that each plugin can be considered a separate microservice. While a default Backstage project has all plugins installed within a single backend, it is also possible to split this setup into multiple backends, with each backend housing one or more plugins.
|
||||
|
||||
## Defining a Plugin
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Extension Points
|
||||
description: Extension points of backend plugins
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
While plugins are able to use static configuration for lightweight forms of customization, you can quickly hit a limit where you need something more powerful to allow users to extend your plugin. For this purpose, the backend system provides a mechanism for plugins to provide extension points, which can be used to expose deeper customizations for your plugin. Extension points are used by modules, which are installed in the backend adjacent to plugins. Modules are covered more in-depth in the [next section](./06-modules.md).
|
||||
|
||||
Extension points are quite similar to services, in that they both encapsulate an interface in a reference object. The key difference is that extension points are registered and provided by plugins themselves, and do not have any factory associated with them. Extension points for a given plugin are also only accessible to modules that extend that same plugin.
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Modules
|
||||
description: Modules for backend plugins
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
Backend modules are used to extend [plugins](./04-plugins.md) or sometimes other modules with additional features or change existing behavior. They must always be installed in the same backend instance as the plugin that they extend, and may only extend a single plugin. Modules interact with their target plugin using the [extension points](./05-extension-points.md) registered by the plugin, while also being able to depend on the [services](./03-services.md) of that plugin.
|
||||
|
||||
Both modules and plugins register an `init` method that is called during startup. In order to ensure that modules have registered all their extensions before the plugin starts up, all modules for each plugin are completely initialized before the plugin itself is initialized. In practice this means that all promises returned by each `init` method of the modules need to resolve before the plugin `init` method is called. This also means that it is not possible to further interact with the extension points once the `init` method has resolved.
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Naming Patterns
|
||||
description: Naming patterns in the backend system
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
These are the naming patterns to adhere to within the backend system. They help us keep exports consistent across packages and make it easier to understand the usage and intent of exports.
|
||||
|
||||
As a rule, all names should be camel case, with the exceptions of plugin and module IDs, which should be kebab case.
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Overview
|
||||
description: Building backends using the new backend system
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
> NOTE: If you have an existing backend that is not yet using the new backend
|
||||
> system, see [migrating](./08-migrating.md).
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Migration Guide
|
||||
description: How to migrate existing backends to the new backend system
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
## Overview
|
||||
|
||||
This section describes how to migrate an existing Backstage backend service
|
||||
@@ -21,8 +19,7 @@ internal plugins and support classes themselves to the backend system first -
|
||||
the migration here will mostly deal with wiring and using compatibility wrappers
|
||||
where possible in the backend package itself. We hope that you will find that
|
||||
you end up with a much smaller, easier to understand, and easier to maintain
|
||||
package as a result of these steps, and then being able to [migrate
|
||||
plugins](../building-plugins-and-modules/08-migrating.md) as a separate
|
||||
package as a result of these steps, and then being able to [migrate plugins](../building-plugins-and-modules/08-migrating.md) as a separate
|
||||
endeavour later.
|
||||
|
||||
## Overall Structure
|
||||
@@ -262,9 +259,259 @@ If you have other customizations made to `plugins/catalog.ts`, such as adding
|
||||
custom processors or entity providers, read on. Otherwise, you should be able to
|
||||
just delete that file at this point.
|
||||
|
||||
#### Amazon Web Services
|
||||
|
||||
`AwsEksClusterProcessor` and `AwsOrganizationCloudAccountProcessor` have not yet been migrated to the new backend system.
|
||||
See [Other Catalog Extensions](#other-catalog-extensions) for how to use these in the new backend system.
|
||||
|
||||
For `AwsS3DiscoveryProcessor`, first migrate to `AwsS3EntityProvider`.
|
||||
|
||||
To migrate `AwsS3EntityProvider` to the new backend system, add a reference to the `@backstage/plugin-catalog-backend-module-aws` module.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(import('@backstage/plugin-catalog-backend-module-aws/alpha'));
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other AWS configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
providers:
|
||||
awsS3:
|
||||
yourProviderId:
|
||||
# ...
|
||||
/* highlight-add-start */
|
||||
schedule:
|
||||
frequency: PT1H
|
||||
timeout: PT50M
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
#### Azure DevOps
|
||||
|
||||
For `AzureDevOpsDiscoveryProcessor`, first migrate to `AzureDevOpsEntityProvider`.
|
||||
|
||||
To migrate `AzureDevOpsEntityProvider` to the new backend system, add a reference to the `@backstage/plugin-catalog-backend-module-azure` module.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(import('@backstage/plugin-catalog-backend-module-azure/alpha'));
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other Azure DevOps configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
providers:
|
||||
azureDevOps:
|
||||
yourProviderId:
|
||||
# ...
|
||||
/* highlight-add-start */
|
||||
schedule:
|
||||
frequency: PT1H
|
||||
timeout: PT50M
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
#### Open API
|
||||
|
||||
`InternalOpenApiDocumentationProvider` has not yet been migrated to the new backend system.
|
||||
See [Other Catalog Extensions](#other-catalog-extensions) for how to use this in the new backend system.
|
||||
|
||||
#### Bitbucket
|
||||
|
||||
For `BitbucketDiscoveryProcessor`, migrate to `BitbucketCloudEntityProvider` or `BitbucketServerEntityProvider`
|
||||
|
||||
To migrate `BitbucketCloudEntityProvider` to the new backend system, add a reference to the `@backstage/plugin-catalog-backend-module-bitbucket-cloud` module.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(
|
||||
import('@backstage/plugin-catalog-backend-module-bitbucket-cloud/alpha'),
|
||||
);
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other Bitbucket Cloud configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
providers:
|
||||
bitbucketCloud:
|
||||
yourProviderId:
|
||||
# ...
|
||||
/* highlight-add-start */
|
||||
schedule:
|
||||
frequency: PT30M
|
||||
timeout: PT3M
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
To migrate `BitbucketServerEntityProvider` to the new backend system, add a reference to `@backstage/plugin-catalog-backend-module-bitbucket-server`.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(
|
||||
import('@backstage/plugin-catalog-backend-module-bitbucket-server/alpha'),
|
||||
);
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other Bitbucket Server configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
providers:
|
||||
bitbucketServer:
|
||||
yourProviderId:
|
||||
# ...
|
||||
/* highlight-add-start */
|
||||
schedule:
|
||||
frequency: PT30M
|
||||
timeout: PT3M
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
#### Google Cloud Platform
|
||||
|
||||
To migrate `GkeEntityProvider` to the new backend system, add a reference to `@backstage/plugin-catalog-backend-module-gcp`.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(import('@backstage/plugin-catalog-backend-module-gcp'));
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
Configuration in app-config.yaml remains the same.
|
||||
|
||||
#### Gerrit
|
||||
|
||||
To migrate `GerritEntityProvider` to the new backend system, add a reference to `@backstage/plugin-catalog-backend-module-gerrit`.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(import('@backstage/plugin-catalog-backend-module-gerrit/alpha'));
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other Gerrit configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
providers:
|
||||
gerrit:
|
||||
yourProviderId:
|
||||
# ...
|
||||
/* highlight-add-start */
|
||||
schedule:
|
||||
frequency: PT30M
|
||||
timeout: PT3M
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
#### Github
|
||||
|
||||
For `GithubDiscoveryProcessor`, `GithubMultiOrgReaderProcessor` and `GithubOrgReaderProcessor`, first migrate to the equivalent Entity Provider.
|
||||
|
||||
To migrate `GithubEntityProvider` to the new backend system, add a reference to `@backstage/plugin-catalog-backend-module-github`.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(import('@backstage/plugin-catalog-backend-module-github/alpha'));
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other Github configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
providers:
|
||||
github:
|
||||
yourProviderId:
|
||||
# ...
|
||||
/* highlight-add-start */
|
||||
schedule:
|
||||
frequency: PT30M
|
||||
timeout: PT3M
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
To migrate `GithubMultiOrgEntityProvider` and `GithubOrgEntityProvider` to the new backend system, add a reference to `@backstage/plugin-catalog-backend-module-github-org`.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
/* highlight-add-start */
|
||||
backend.add(import('@backstage/plugin-catalog-backend-module-github-org'));
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other Github configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
providers:
|
||||
githubOrg:
|
||||
yourProviderId:
|
||||
# ...
|
||||
/* highlight-add-start */
|
||||
schedule:
|
||||
frequency: PT30M
|
||||
timeout: PT3M
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providing transformers, these can be configured by extending `githubOrgEntityProviderTransformsExtensionPoint`
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
import { createBackendModule } from '@backstage/backend-plugin-api';
|
||||
import { githubOrgEntityProviderTransformsExtensionPoint } from '@backstage/plugin-catalog-backend-module-github-org';
|
||||
|
||||
backend.add(
|
||||
createBackendModule({
|
||||
pluginId: 'catalog',
|
||||
moduleId: 'githubOrgTransformers',
|
||||
register(env) {
|
||||
env.registerInit({
|
||||
deps: {
|
||||
/* highlight-add-start */
|
||||
githubOrgTransformers:
|
||||
githubOrgEntityProviderTransformsExtensionPoint,
|
||||
/* highlight-add-end */
|
||||
},
|
||||
async init({ githubOrgTransformers }) {
|
||||
/* highlight-add-start */
|
||||
githubOrgTransformers.setUserTransformer(myUserTransformer);
|
||||
githubOrgTransformers.setTeamTransformer(myTeamTransformer);
|
||||
/* highlight-add-end */
|
||||
},
|
||||
});
|
||||
},
|
||||
}),
|
||||
);
|
||||
```
|
||||
|
||||
#### Microsoft Graph
|
||||
|
||||
Import the Microsoft Graph catalog module
|
||||
For `MicrosoftGraphOrgReaderProcessor`, first migrate to `MicrosoftGraphOrgEntityProvider`
|
||||
|
||||
To migrate `MicrosoftGraphOrgEntityProvider` to the new backend system, add a reference to `@backstage/plugin-catalog-backend-module-msgraph`.
|
||||
|
||||
```ts title="packages/backend/src/index.ts"
|
||||
backend.add(import('@backstage/plugin-catalog-backend/alpha'));
|
||||
@@ -273,7 +520,8 @@ backend.add(import('@backstage/plugin-catalog-backend-module-msgraph/alpha'));
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
If you were providng a `schedule` programtically, this now needs to be set via configuration
|
||||
If you were providing a `schedule` in code, this now needs to be set via configuration.
|
||||
All other Microsoft Graph configuration in `app-config.yaml` remains the same.
|
||||
|
||||
```yaml title="app-config.yaml"
|
||||
catalog:
|
||||
@@ -285,7 +533,6 @@ catalog:
|
||||
frequency: PT4H
|
||||
timeout: PT30M
|
||||
/* highlight-add-end */
|
||||
|
||||
```
|
||||
|
||||
If you were providing transformers, these can be configured by extending `microsoftGraphOrgEntityProviderTransformExtensionPoint`
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Overview
|
||||
description: Building backend plugins and modules using the new backend system
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
> NOTE: If you have an existing backend and/or backend plugins that are not yet
|
||||
> using the new backend system, see [migrating](./08-migrating.md).
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Testing
|
||||
description: Learn how to test your backend plugins and modules
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
Utilities for testing backend plugins and modules are available in
|
||||
`@backstage/backend-test-utils`. This section describes those facilities.
|
||||
|
||||
@@ -120,7 +118,7 @@ describe('MyDatabaseClass', () => {
|
||||
// "physical" databases to test against is much costlier than creating the
|
||||
// "logical" databases within them that the individual tests use.
|
||||
const databases = TestDatabases.create({
|
||||
ids: ['POSTGRES_13', 'POSTGRES_9', 'SQLITE_3', 'MYSQL_8'],
|
||||
ids: ['POSTGRES_16', 'POSTGRES_12', 'SQLITE_3', 'MYSQL_8'],
|
||||
});
|
||||
|
||||
// Just an example of how to conveniently bundle up the setup code
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Migration Guide
|
||||
description: How to migrate existing backend plugins to the new backend system
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
Migrating an existing backend plugin to the new backend system is fairly straightforward. The process is similar across the majority of plugins which just return a `Router` that is then wired up in the `index.ts` file of your backend. The primary thing that we need to do is to make sure that the dependencies that are required by the plugin are available, and then registering the router with the HTTP router service.
|
||||
|
||||
Let's look at an example of migrating the Kubernetes backend plugin. In the existing (old) system, the kubernetes backend is structured like this:
|
||||
|
||||
@@ -6,8 +6,6 @@ sidebar_label: Overview
|
||||
description: Core backend service APIs
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
The default backend provides several [core services](https://github.com/backstage/backstage/blob/master/packages/backend-plugin-api/src/services/definitions/coreServices.ts) out of the box which includes access to configuration, logging, URL Readers, databases and more.
|
||||
|
||||
All core services are available through the `coreServices` namespace in the `@backstage/backend-plugin-api` package.
|
||||
|
||||
@@ -6,10 +6,8 @@ sidebar_label: Introduction
|
||||
description: The Backend System
|
||||
---
|
||||
|
||||
> **NOTE: The new backend system is in alpha, and some plugins do not yet fully implement it.**
|
||||
|
||||
## Status
|
||||
|
||||
The new backend system is in alpha, but many plugins have already been migrated. We recommend all plugins to migrate to the new system, and you can also try it out in your own production deployments.
|
||||
The new backend system is released and ready for production use, and many plugins and modules have already been migrated. We recommend all plugins and deployments to migrate to the new system.
|
||||
|
||||
You can find an example backend setup in [the `backend-next` package](https://github.com/backstage/backstage/tree/master/packages/backend-next).
|
||||
|
||||
@@ -391,8 +391,6 @@ There are other more specific search results layout components that also accept
|
||||
|
||||
## How to migrate your backend installation to use Search together with the new backend system
|
||||
|
||||
> DISCLAIMER: The new backend system is in alpha, and so are the search backend support for the new backend system. We don't recommend you to migrate your backend installations to the new system yet. But if you want to experiment, this is the guide for you!
|
||||
|
||||
Recently, the Backstage maintainers [announced the new Backend System](https://backstage.io/blog/2023/02/15/backend-system-alpha). The search plugins are now migrated to support the new backend system. In this guide you will learn how to update your backend set up.
|
||||
|
||||
In "packages/backend-next/index.ts", install the search plugin [1], the search engine [2], and the search collators/decorators modules [3]:
|
||||
|
||||
@@ -35,9 +35,186 @@ process, not the raw originally ingested entity data. See
|
||||
[The Life of an Entity](./life-of-an-entity.md) for more details about this process and
|
||||
distinction.
|
||||
|
||||
### `GET /entities/by-query`
|
||||
|
||||
Query entities. Supports the following query parameters, described in the section below:
|
||||
|
||||
- [`filter`](#filtering), for selecting only a subset of all entities
|
||||
- [`fields`](#field-selection), for selecting only parts of the full data
|
||||
structure of each entity
|
||||
- `limit` for limiting the number of entities returned (20 is the default)
|
||||
- [`orderField`](#ordering), for deciding the order of the entities
|
||||
- [`fullTextFilter`](#full-text-filtering), for filtering the entities by text
|
||||
- [`cursor`](#pagination), for retrieving the next or previous batch of entities
|
||||
|
||||
The return type is JSON, on the following form
|
||||
|
||||
```json
|
||||
{
|
||||
"items": [{ "kind": "Component", "metadata": { "name": "foo" } }],
|
||||
"totalItems": 4,
|
||||
"pageInfo": {
|
||||
"nextCursor": "a-cursor",
|
||||
"prevCursor": "another-cursor"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Filtering
|
||||
|
||||
You can pass in one or more filter sets that get matched against each entity.
|
||||
Each filter set is a number of conditions that all have to match for the
|
||||
condition to be true (conditions effectively have an AND between them). At least
|
||||
one filter set has to be true for the entity to be part of the result set
|
||||
(filter sets effectively have an OR between them).
|
||||
|
||||
Example:
|
||||
|
||||
```text
|
||||
/entities/by-query?filter=kind=user,metadata.namespace=default&filter=kind=group,spec.type
|
||||
|
||||
Return entities that match
|
||||
|
||||
Filter set 1:
|
||||
Condition 1: kind = user
|
||||
AND
|
||||
Condition 2: metadata.namespace = default
|
||||
|
||||
OR
|
||||
|
||||
Filter set 2:
|
||||
Condition 1: kind = group
|
||||
AND
|
||||
Condition 2: spec.type exists
|
||||
```
|
||||
|
||||
Each condition is either on the form `<key>`, or on the form `<key>=<value>`.
|
||||
The first form asserts on the existence of a certain key (with any value), and
|
||||
the second asserts that the key exists and has a certain value. All checks are
|
||||
always case _insensitive_.
|
||||
|
||||
In all cases, the key is a simplified JSON path in a given piece of entity data.
|
||||
Each part of the path is a key of an object, and the traversal also descends
|
||||
through arrays. There are two special forms:
|
||||
|
||||
- Array items that are simple value types (such as strings) match on a key-value
|
||||
pair where the key is the item as a string, and the value is the string `true`
|
||||
- Relations can be matched on a `relations.<type>=<targetRef>` form
|
||||
|
||||
Let's look at a simplified example to illustrate the concept:
|
||||
|
||||
```json
|
||||
{
|
||||
"a": {
|
||||
"b": ["c", { "d": 1 }],
|
||||
"e": 7
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This would match any one of the following conditions:
|
||||
|
||||
- `a`
|
||||
- `a.b`
|
||||
- `a.b.c`
|
||||
- `a.b.c=true`
|
||||
- `a.b.d`
|
||||
- `a.b.d=1`
|
||||
- `a.e`
|
||||
- `a.e=7`
|
||||
|
||||
Some more real world usable examples:
|
||||
|
||||
- Return all orphaned entities:
|
||||
|
||||
`/entities/by-query?filter=metadata.annotations.backstage.io/orphan=true`
|
||||
|
||||
- Return all users and groups:
|
||||
|
||||
`/entities/by-query?filter=kind=user&filter=kind=group`
|
||||
|
||||
- Return all service components:
|
||||
|
||||
`/entities/by-query?filter=kind=component,spec.type=service`
|
||||
|
||||
- Return all entities with the `java` tag:
|
||||
|
||||
`/entities/by-query?filter=metadata.tags.java`
|
||||
|
||||
- Return all users who are members of the `ops` group (note that the full
|
||||
[reference](references.md) of the group is used):
|
||||
|
||||
`/entities/by-query?filter=kind=user,relations.memberof=group:default/ops`
|
||||
|
||||
#### Full text filtering
|
||||
|
||||
TODO
|
||||
|
||||
#### Field selection
|
||||
|
||||
By default the full entities are returned, but you can pass in a `fields` query
|
||||
parameter which selects what parts of the entity data to retain. This makes the
|
||||
response smaller and faster to transfer, and may allow the catalog to perform
|
||||
more efficient queries.
|
||||
|
||||
The query parameter value is a comma separated list of simplified JSON paths
|
||||
like above. Each path corresponds to the key of either a value, or of a subtree
|
||||
root that you want to keep in the output. The rest is pruned away. For example,
|
||||
specifying `?fields=metadata.name,metadata.annotations,spec` retains only the
|
||||
`name` and `annotations` fields of the `metadata` of each entity (it'll be an
|
||||
object with at most two keys), keeps the entire `spec` unchanged, and cuts out
|
||||
all other roots such as `relations`.
|
||||
|
||||
Some more real world usable examples:
|
||||
|
||||
- Return only enough data to form the full ref of each entity:
|
||||
|
||||
`/entities/by-query?fields=kind,metadata.namespace,metadata.name`
|
||||
|
||||
#### Ordering
|
||||
|
||||
By default the entities are returned ordered by their internal uid. You can
|
||||
customize the `orderField` query parameters to affect that ordering.
|
||||
|
||||
For example, to return entities by their name:
|
||||
|
||||
`/entities/by-query?orderField=metadata.name,asc`
|
||||
|
||||
Each parameter can be followed by `asc` for ascending lexicographical order or
|
||||
`desc` for descending (reverse) lexicographical order.
|
||||
|
||||
#### Pagination
|
||||
|
||||
You may pass the `cursor` query parameters to perform cursor based pagination
|
||||
through the set of entities. The value of `cursor` will be returned in the response, under the `pageInfo` property:
|
||||
|
||||
```json
|
||||
"pageInfo": {
|
||||
"nextCursor": "a-cursor",
|
||||
"prevCursor": "another-cursor"
|
||||
}
|
||||
```
|
||||
|
||||
If `nextCursor` exists, it can be used to retrieve the next batch of entities. Following the same approach,
|
||||
if `prevCursor` exists, it can be used to retrieve the previous batch of entities.
|
||||
|
||||
- [`filter`](#filtering), for selecting only a subset of all entities
|
||||
- [`fields`](#field-selection), for selecting only parts of the full data
|
||||
structure of each entity
|
||||
- `limit` for limiting the number of entities returned (20 is the default)
|
||||
- [`orderField`](#ordering), for deciding the order of the entities
|
||||
- `fullTextFilter`
|
||||
**NOTE**: [`filter`, `orderField`, `fullTextFilter`] and `cursor` are mutually exclusive. This means that,
|
||||
it isn't possible to change any of [`filter`, `orderField`, `fullTextFilter`] when passing `cursor` as query parameters,
|
||||
as changing any of these properties will affect pagination. If any of `filter`, `orderField`, `fullTextFilter` is specified together with `cursor`, only the latter is taken into consideration.
|
||||
|
||||
### `GET /entities`
|
||||
|
||||
Lists entities. Supports the following query parameters, described in sections
|
||||
Lists entities.
|
||||
|
||||
**NOTE**: This endpoint is deprecated in favor of `GET /entities/by-query`, which provides a more efficient implementation and cursor based pagination.
|
||||
|
||||
The endpoint supports the following query parameters, described in sections
|
||||
below:
|
||||
|
||||
- [`filter`](#filtering), for selecting only a subset of all entities
|
||||
|
||||
@@ -231,8 +231,53 @@ spec:
|
||||
inputType: tel
|
||||
```
|
||||
|
||||
### Using Secrets
|
||||
|
||||
You may want to mark things as secret and make sure that these values are protected and not available through REST endpoints. You can do this by using the built in `ui:field: Secret`.
|
||||
|
||||
You can define this property as any normal parameter, however the consumption of this parameter will not be available through `${{ parameters.myKey }}` you will instead need to use `${{ secrets.myKey }}` in your `template.yaml`.
|
||||
|
||||
Parameters will be automatically masked in the review step.
|
||||
|
||||
```yaml
|
||||
apiVersion: scaffolder.backstage.io/v1beta3
|
||||
kind: Template
|
||||
metadata:
|
||||
name: v1beta3-demo
|
||||
title: Test Action template
|
||||
description: scaffolder v1beta3 template demo
|
||||
spec:
|
||||
owner: backstage/techdocs-core
|
||||
type: service
|
||||
|
||||
parameters:
|
||||
- title: Authenticaion
|
||||
description: Provide authentication for the resource
|
||||
required:
|
||||
- username
|
||||
- password
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
# use the built in Secret field extension
|
||||
ui:field: Secret
|
||||
password:
|
||||
type: string
|
||||
ui:field: Secret
|
||||
|
||||
steps:
|
||||
- id: setupAuthentication
|
||||
action: auth:create
|
||||
input:
|
||||
# make sure to use ${{ secrets.parameterName }} to reference these values
|
||||
username: ${{ secrets.username }}
|
||||
password: ${{ secrets.password }}
|
||||
```
|
||||
|
||||
### Hide or mask sensitive data on Review step
|
||||
|
||||
> Note: this approach is soon to be deprecated, please mark things as secret by using the `Secret` field extension instead as mentioned above.
|
||||
|
||||
Sometimes, specially in custom fields, you collect some data on Create form that
|
||||
must not be shown to the user on Review step. To hide or mask this data, you can
|
||||
use `ui:widget: password` or set some properties of `ui:backstage`:
|
||||
|
||||
@@ -6,7 +6,7 @@ sidebar_label: Overview
|
||||
description: The structure and architecture of the new Frontend System
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
## Building Blocks
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
id: apps
|
||||
id: app
|
||||
title: App Instances
|
||||
sidebar_label: App
|
||||
# prettier-ignore
|
||||
description: App instances
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
## The App Instance
|
||||
|
||||
@@ -45,4 +45,35 @@ A common type of data that is shared between extensions is React elements and co
|
||||
|
||||
## Feature Discovery
|
||||
|
||||
TODO
|
||||
App feature discovery lets you automatically discover and install features provided by dependencies in your app. In practice, it means that you don't need to manually `import` features in code, but they are instead installed as soon as you add them as a dependency in your `package.json`.
|
||||
|
||||
Because feature discovery needs to interact with the compilation process, it is only available when using the `@backstage/cli` to build your app. It is hooked into the WebPack compilation process by scanning your app package for compatible dependencies, which are then made part of the app compilation bundle.
|
||||
|
||||
Since the `@backstage/cli` is a more stable component than the new frontend system, feature discovery is currently marked as an experimental feature of the CLI and needs to be enabled manually. To enable it, add the following configuration to your `app-config.yaml`:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
experimental:
|
||||
packages: all
|
||||
```
|
||||
|
||||
This will cause all dependencies in your app package to be installed automatically. If this is not desired, you can use include or exclude filters to narrow down the set of packages:
|
||||
|
||||
```yaml
|
||||
app:
|
||||
experimental:
|
||||
packages:
|
||||
# Only the following packages will be included
|
||||
include:
|
||||
- '@backstage/plugin-catalog'
|
||||
- '@backstage/plugin-scaffolder'
|
||||
---
|
||||
app:
|
||||
experimental:
|
||||
packages:
|
||||
# All but the following package will be included
|
||||
exclude:
|
||||
- '@backstage/plugin-catalog'
|
||||
```
|
||||
|
||||
Note that you do not need to manually exclude packages that you also import explicitly in code, since plugin instances are deduplicated by the app. You will never end up with duplicate plugin installations except if they are in fact two different plugin instances with different IDs.
|
||||
|
||||
@@ -6,7 +6,7 @@ sidebar_label: Extensions
|
||||
description: Frontend extensions
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ sidebar_label: Plugins
|
||||
description: Frontend plugins
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
id: extension overrides
|
||||
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**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -6,6 +6,25 @@ sidebar_label: Utility APIs
|
||||
description: Utility APIs
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
See [Utility APIs docs](../../api/utility-apis.md).
|
||||
## Overview
|
||||
|
||||
Utility APIs are pieces of standalone functionality, interfaces that can be requested by plugins to use. They are defined by a TypeScript interface as well as a reference (an "API ref") used to access its implementation. They can be provided both by plugins and the core framework, and are themselves [extensions](../architecture/03-extensions.md) that can have inputs, be replaced, and be declaratively configured in your app-config.
|
||||
|
||||
A common example of a utility API is a client interface to interact with the backend part of a plugin, such as the catalog client. Any frontend plugin can then request an implementation of that interface to make requests through.
|
||||
|
||||
The following diagram shows a hypothetical application, which depends on two plugins and also provides some extra overrides. Note that both the plugins and the core framework provide utility APIs, and that they depended on each other. The app also chooses to use its overrides mechanism to supply a replacement implementation of one API, which takes precedence over the default one. Thus, all consumers of that API will be sure to get that new implementation provided to them.
|
||||
|
||||

|
||||
|
||||
## Extension structure
|
||||
|
||||
All utility APIs implement the `createApiExtension.factoryDataRef` output data type, and must attach exclusively to the `core` extension's `apis` input no matter who provided them. These defaults are provided out of the box by the `createApiExtension` framework function.
|
||||
|
||||
Since utility APIs are extensions, they can also have inputs in advanced use cases. This is occasionally useful for complex APIs that can themselves be extended with additional programmatic functionality by adopters.
|
||||
|
||||
## Links
|
||||
|
||||
- The [Utility APIs section](../utility-apis/01-index.md) of the plugin docs
|
||||
- The legacy docs on [utility APIs](../../api/utility-apis.md)
|
||||
|
||||
@@ -6,7 +6,7 @@ sidebar_label: Routes
|
||||
description: Frontend routes
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
See [routing system docs](../../plugins/composability.md#routing-system)
|
||||
|
||||
@@ -98,7 +98,7 @@ Explain all of create, use and provide
|
||||
-->
|
||||
|
||||
```ts
|
||||
/*
|
||||
/*
|
||||
|
||||
Some examples
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ sidebar_label: Naming Patterns
|
||||
description: Naming patterns in the frontend system
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
These are the naming patterns to adhere to within the frontend system. They help us keep exports and IDs consistent across packages and make it easier to understand the usage and intent of exports and IDs.
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ sidebar_label: Value References
|
||||
description: Value References
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
<!--
|
||||
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
---
|
||||
id: testing
|
||||
title: Frontend System Testing Plugins
|
||||
sidebar_label: Testing
|
||||
# prettier-ignore
|
||||
description: Testing plugins in the frontend system
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
# Testing Frontend Plugins
|
||||
|
||||
> NOTE: The new frontend system is in alpha, and some plugins do not yet fully implement it.
|
||||
|
||||
Utilities for testing frontend features and components are available in `@backstage/frontend-test-utils`.
|
||||
|
||||
## Testing React components
|
||||
|
||||
A component can be used for more than one extension, and it should be tested independently of an extension environment.
|
||||
|
||||
Use the `renderInTestApp` helper to render a given component inside a Backstage test app:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import { renderInTestApp } from '@backstage/frontend-test-utils';
|
||||
import { EntityDetails } from './plugin';
|
||||
|
||||
describe('Entity details component', () => {
|
||||
it('should render the entity name and owner', async () => {
|
||||
await renderInTestApp(<EntityDetails owner="tools" name="test" />);
|
||||
|
||||
await expect(
|
||||
screen.findByText('The entity "test" is owned by "tools"'),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
To mock [Utility APIs](../architecture/06-utility-apis.md) that are used by your component you can use the `TestApiProvider` to override individual API implementations. In the snippet below, we wrap the component within a `TestApiProvider` in order to mock the catalog client API:
|
||||
|
||||
```tsx
|
||||
import React from 'react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import {
|
||||
renderInTestApp,
|
||||
TestApiProvider,
|
||||
} from '@backstage/frontend-test-utils';
|
||||
import { stringifyEntityRef } from '@backstage/catalog-model';
|
||||
import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
|
||||
import { EntityDetails } from './plugin';
|
||||
|
||||
describe('Entity details component', () => {
|
||||
it('should render the entity name and owner', async () => {
|
||||
const catalogApiMock = {
|
||||
async getEntityFacets() {
|
||||
return {
|
||||
facets: {
|
||||
'relations.ownedBy': [{ count: 1, value: 'group:default/tools' }],
|
||||
},
|
||||
},
|
||||
}
|
||||
} satisfies Partial<typeof catalogApiRef.T>;
|
||||
|
||||
const entityRef = stringifyEntityRef({
|
||||
kind: 'Component',
|
||||
namespace: 'default',
|
||||
name: 'test',
|
||||
});
|
||||
|
||||
await renderInTestApp(
|
||||
<TestApiProvider apis={[[catalogApiRef, catalogApiMock]]}>
|
||||
<EntityDetails entitRef={entityRef} />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
await expect(
|
||||
screen.findByText('The entity "test" is owned by "tools"'),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## Testing extensions
|
||||
|
||||
To facilitate testing of frontend extensions, the `@backstage/frontend-test-utils` package provides a tester class which starts up an entire frontend harness, complete with a number of default features. You can then provide overrides for extensions whose behavior you need to adjust for the test run.
|
||||
|
||||
A number of features (frontend extensions and overrides) are also accepted by the tester. Here are some examples of how these facilities can be useful:
|
||||
|
||||
### Single extension
|
||||
|
||||
In order to test an extension in isolation, you simply need to pass it into the tester factory, then call the render method on the returned instance:
|
||||
|
||||
```tsx
|
||||
import { screen } from '@testing-library/react';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { indexPageExtension } from './plugin';
|
||||
|
||||
describe('Index page', () => {
|
||||
it('should render a the index page', () => {
|
||||
createExtensionTester(indexPageExtension).render();
|
||||
|
||||
expect(screen.getByText('Index Page')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Extension preset
|
||||
|
||||
There are some extensions that rely on other extensions existence, such as a page that links to another page. In that case, you can add more than one extension to the preset of features you want to render in the test, as shown below:
|
||||
|
||||
```tsx
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { indexPageExtension, detailsPageExtension } from './plugin';
|
||||
|
||||
describe('Index page', async () => {
|
||||
it('should link to the details page', () => {
|
||||
createExtensionTester(indexPageExtension)
|
||||
// Adding more extensions to the preset being tested
|
||||
.add(detailsPageExtension)
|
||||
.render();
|
||||
|
||||
await expect(screen.findByText('Index Page')).toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByRole('link', { name: 'See details' }));
|
||||
|
||||
await expect(
|
||||
screen.findByText('Details Page'),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Mocking apis
|
||||
|
||||
If your extensions requires implementation of APIs that aren't wired up by default, you'll have to add overrides to the preset of features being tested:
|
||||
|
||||
```tsx
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { createApiFactory } from '@backestage/core-plugin-api';
|
||||
import {
|
||||
createExtensionOverrides,
|
||||
configApiRef,
|
||||
analyticsApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
createExtensionTester,
|
||||
MockConfigApi,
|
||||
MockAnalyticsApi,
|
||||
} from '@backstage/frontend-test-utils';
|
||||
import { indexPageExtension } from './plugin';
|
||||
|
||||
describe('Index page', () => {
|
||||
it('should capture click events in analytics', async () => {
|
||||
// Mocking the analytics api implementation
|
||||
const analyticsApiMock = new MockAnalyticsApi();
|
||||
|
||||
const analyticsApiOverride = createApiExtension({
|
||||
factory: createApiFactory({
|
||||
api: analyticsApiRef,
|
||||
factory: () => analyticsApiMock,
|
||||
}),
|
||||
});
|
||||
|
||||
createExtensionTester(indexPageExtension)
|
||||
// Overriding the analytics api extension
|
||||
.add(analyticsApiOverride)
|
||||
.render();
|
||||
|
||||
await userEvent.click(
|
||||
await screen.findByRole('link', { name: 'See details' }),
|
||||
);
|
||||
|
||||
expect(analyticsApiMock.getEvents()[0]).toMatchObject({
|
||||
action: 'click',
|
||||
subject: 'See details',
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Setting configuration
|
||||
|
||||
In the case that your extension can be configured, you can test this capability by passing configuration values as follows:
|
||||
|
||||
```tsx
|
||||
import { screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { indexPageExtension, detailsPageExtension } from './plugin';
|
||||
|
||||
describe('Index page', () => {
|
||||
it('should accepts a custom title via config', async () => {
|
||||
createExtensionTester(indexPageExtension, {
|
||||
// Configuration specific of index page
|
||||
config: { title: 'Custom index' },
|
||||
})
|
||||
.add(detailsExtensionPage, {
|
||||
// Configuration specific of details page
|
||||
config: { title: 'Custom details' },
|
||||
})
|
||||
.render({
|
||||
// Configuration specific of the instance
|
||||
config: {
|
||||
app: {
|
||||
title: 'Custom app',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
screen.findByRole('heading', { name: 'Custom app' }),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
await expect(
|
||||
screen.findByRole('heading', { name: 'Custom index' }),
|
||||
).resolves.toBeInTheDocument();
|
||||
|
||||
await userEvent.click(screen.getByRole('link', { name: 'See details' }));
|
||||
|
||||
await expect(
|
||||
screen.findByText('Custom details'),
|
||||
).resolves.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
That's all for testing features!
|
||||
|
||||
## Missing something?
|
||||
|
||||
If there's anything else you think needs to be covered in the docs or that you think isn't covered by the test utilities, please create an issue in the Backstage repository. You are always welcome to contribute as well!
|
||||
@@ -6,10 +6,10 @@ sidebar_label: Introduction
|
||||
description: The Frontend System
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in a highly experimental phase**
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
## Status
|
||||
|
||||
The new frontend system is in an experimental phase and we do not recommend any plugins or apps to migrate.
|
||||
The new frontend system is in alpha, and only a few plugins support the system so far. We do not yet recommend migrating any apps to the new system. If you add support for the new system to your plugin, please do so under a `/alpha` sub-path export.
|
||||
|
||||
You can find an example app setup in [the `app-next` package](https://github.com/backstage/backstage/tree/master/packages/app-next).
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
id: index
|
||||
title: Utility APIs
|
||||
sidebar_label: Overview
|
||||
# prettier-ignore
|
||||
description: Working with Utility APIs in the New Frontend System
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
As described [in the architecture section](../architecture/06-utility-apis.md), utility APIs are pieces of shared functionality - interfaces that can be requested by plugins to use. They are defined by a TypeScript interface as well as a reference (an "API ref") used to access its implementation. They can be provided both by plugins and the core framework, and are themselves [extensions](../architecture/03-extensions.md) that can accept inputs, be declaratively configured in your app-config, or transparently be replaced entirely with custom implementations that fulfill the same contract.
|
||||
|
||||
## Creating utility APIs
|
||||
|
||||
> For details, [see the main article](./02-creating.md).
|
||||
|
||||
Backstage apps, plugins, and the core Backstage framework can all expose utility APIs for general use.
|
||||
|
||||
Some are available out of the box, such as the API for reading app configuration. Some are provided by third party plugins, such as the catalog client API that both the catalog itself and your own code can leverage to talk to the catalog backend. Some, you may create yourself and make available inside your Backstage instance for use within your private ecosystem of plugins.
|
||||
|
||||
[The main article](./02-creating.md) describes the process of creating and exposing utility APIs of your own, for sharing functionality or configurability across plugins and apps.
|
||||
|
||||
## Consuming utility APIs
|
||||
|
||||
> For details, [see the main article](./03-consuming.md).
|
||||
|
||||
Once utility APIs are created, there are a few ways that they can be accessed to be consumed.
|
||||
|
||||
Some utility APIs in turn depend on other utility APIs. This powerful composability lets you leverage already-written reusable pieces. In particular, you may want to rely on Backstage's framework-provided APIs e.g. for reading app configuration and many other use cases. Sometimes you request utility APIs inside your React components, e.g. for accessing i18n strings, or emitting analytics events.
|
||||
|
||||
These are described in detail in [the main article](./03-consuming.md)
|
||||
|
||||
## Configuring utility APIs
|
||||
|
||||
> For details, [see the main article](./04-configuring.md).
|
||||
|
||||
Most utility APIs are usable directly without any configuration. But they are proper extensions, and can therefore have their implementations entirely swapped out by your app for advanced use cases. They can also be built with the ability to configured in your app-config, or to have inputs that extend their functionality.
|
||||
|
||||
These cases are all described in [the main article](./04-configuring.md).
|
||||
|
||||
## Migrating from the old frontend system
|
||||
|
||||
If you want to learn how to migrate your own utility APIs from the old frontend system to the new one, that's described in [a dedicated migration guide](./05-migrating.md).
|
||||
@@ -0,0 +1,145 @@
|
||||
---
|
||||
id: creating
|
||||
title: Creating Utility APIs
|
||||
sidebar_label: Creating APIs
|
||||
# prettier-ignore
|
||||
description: Creating new utility APIs in your plugins and app
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
This section describes how to make a Utility API from scratch, or to add configurability and inputs to an existing one. If you are instead interested in migrating an existing Utility API from the old frontend system, check out [the migrating section](./05-migrating.md).
|
||||
|
||||
## Creating the Utility API contract
|
||||
|
||||
The first step toward exposing a utility API is to define its TypeScript contract, as well as an API reference for consumers use to access the implementation. If you want your API to be accessible by other plugins this should be done in [your plugin's `-react` package](../../architecture-decisions/adr011-plugin-package-structure.md), so that it can be imported separately. If you just want to use the API within your own plugin it is fine to place the definition within the plugin itself. In this example, we have an Example plugin that wants to expose a utility API for performing some type of work.
|
||||
|
||||
```tsx title="in @internal/plugin-example-react"
|
||||
import { createApiRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
/**
|
||||
* The work interface for the Example plugin.
|
||||
* @public
|
||||
*/
|
||||
export interface WorkApi {
|
||||
/**
|
||||
* Performs some work.
|
||||
*/
|
||||
doWork(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* API Reference for {@link WorkApi}.
|
||||
* @public
|
||||
*/
|
||||
export const workApiRef = createApiRef<WorkApi>({
|
||||
id: 'plugin.example.work',
|
||||
});
|
||||
```
|
||||
|
||||
Both of these are properly exported publicly from the package, so that consumers can reach them.
|
||||
|
||||
## Providing an extension through your plugin
|
||||
|
||||
The plugin itself now wants to provide this API and its default implementation, in the form of an API extension. Doing so means that when users install the Example plugin, an instance of the Work utility API will also be automatically available in their apps - both to the Example plugin itself, and to others. We do this in the main plugin package, not the `-react` package.
|
||||
|
||||
```tsx title="in @internal/plugin-example"
|
||||
import {
|
||||
createApiExtension,
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
storageApiRef,
|
||||
StorageApi,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { WorkApi, workApiRef } from '@internal/plugin-example-react';
|
||||
|
||||
class WorkImpl implements WorkApi {
|
||||
constructor(options: { storageApiRef: StorageApi }) {
|
||||
/* TODO */
|
||||
}
|
||||
async doWork() {
|
||||
/* TODO */
|
||||
}
|
||||
}
|
||||
|
||||
const exampleWorkApi = createApiExtension({
|
||||
factory: createApiFactory({
|
||||
api: workApiRef,
|
||||
deps: { storageApi: storageApiRef },
|
||||
factory: ({ storageApi }) => {
|
||||
return new WorkImpl({ storageApi });
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* The Example plugin.
|
||||
* @public
|
||||
*/
|
||||
export default createPlugin({
|
||||
id: 'example',
|
||||
extensions: [exampleWorkApi],
|
||||
});
|
||||
```
|
||||
|
||||
For illustration we make a skeleton implementation class and the API extension and factory for it, in the same file. These are not exported to the public surface of the plugin package; only the plugin is, as the default export. Users who install the plugin will now get the utility API automatically as well.
|
||||
|
||||
The code also illustrates how the API factory declares a dependency on another utility API - the core storage API in this case. An instance of that utility API is then provided to the factory function.
|
||||
|
||||
The resulting extension ID of the work API will be the kind `api:` followed by the plugin ID as the namespace, in this case ending up as `api:plugin.example.work`. Check out [the naming patterns doc](../architecture/08-naming-patterns.md) for more information on how this works. You can now use this ID to refer to the API in app-config and elsewhere.
|
||||
|
||||
## Adding configurability
|
||||
|
||||
Here we will describe how to amend a utility API with the capability of having extension config, which is driven by [your app-config](../../conf/writing.md). You do this by giving an extension config schema to your API extension factory function. Let's make the required additions to our original work example API.
|
||||
|
||||
```tsx title="in @internal/plugin-example"
|
||||
/* highlight-add-next-line */
|
||||
import { createSchemaFromZod } from '@backstage/frontend-plugin-api';
|
||||
|
||||
const exampleWorkApi = createApiExtension({
|
||||
/* highlight-add-start */
|
||||
api: workApiRef,
|
||||
configSchema: createSchemaFromZod(z =>
|
||||
z.object({
|
||||
goSlow: z.boolean().default(false),
|
||||
}),
|
||||
),
|
||||
/* highlight-add-end */
|
||||
/* highlight-remove-next-line */
|
||||
factory: createApiFactory({
|
||||
/* highlight-add-next-line */
|
||||
factory: ({ config }) => createApiFactory({
|
||||
api: workApiRef,
|
||||
deps: { storageApi: storageApiRef },
|
||||
factory: ({ storageApi }) => {
|
||||
/* highlight-add-start */
|
||||
if (config.goSlow) {
|
||||
/* ... */
|
||||
}
|
||||
/* highlight-add-end */
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
We wanted users to be able to set a `goSlow` extension config parameter for our API instances. So we passed in a `configSchema` to `createApiExtension` which matches that interface. This example builds it using [the zod library](https://zod.dev/). The actual extension config values will then be passed in a type safe manner in to the `factory` which is now a callback, wherein we can do what we wish with them. When changing to the callback form, we also had to add a top level `api: workApiRef` under `createApiExtension`.
|
||||
|
||||
Note that the expression "extension config" as used here, is _not_ the same thing as the `configApi` which gives you access to the full app-config. The extension config discussed here is instead the particular configuration settings given to your utility API instance. This is discussed more [in the Configuring section](./04-configuring.md).
|
||||
|
||||
Note also that the extension config schema contained a default value fo the `goSlow` field. This is an important consideration. You want users of your API to be able to get maximum value out of it, without having to dive deep into how to configure it. For that reason you generally want to provide as many sane defaults as possible, while letting users override them rarely but with purpose, only when called for. If you have an extension config schema without defaults, the framework will refuse to instantiate the utility API on startup unless the user had configured those values explicitly. Since it had a default value, the TypeScript code and interfaces also don't have to defensively allow `undefined` - we know that it'll have either the default value or an overridden value when we start consuming the extension config data.
|
||||
|
||||
## Adding inputs
|
||||
|
||||
Inputs are added to Utility APIs in the same way as other extension types:
|
||||
|
||||
- Declaring a set of `inputs` on your extension
|
||||
- If needed, create custom extension data types to be used in those inputs
|
||||
- If needed, export an extension creator function for creating that particular attachment type
|
||||
|
||||
This is a power use case and not very commonly used.
|
||||
|
||||
<!-- TODO: link to main article -->
|
||||
|
||||
## Next steps
|
||||
|
||||
See [the Consuming section](./03-consuming.md) to see how to consume this new utility API in various ways. If you wish to configure and add inputs to it, check out [the Configuring section](./04-configuring.md).
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: consuming
|
||||
title: Consuming Utility APIs
|
||||
sidebar_label: Consuming APIs
|
||||
# prettier-ignore
|
||||
description: Consuming utility APIs
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
All of the utility API extensions that were passed into your app through installed plugins, get instantiated and configured in the right order by the framework, and are then made available for consumption. You can interact with these instances in the following ways.
|
||||
|
||||
## Via React hooks
|
||||
|
||||
The most common consumption pattern for utility APIs is to call the `useApi` hook inside React components to get an implementation via its API ref. This applies whether it was originally provided from the core framework or from a plugin.
|
||||
|
||||
```tsx
|
||||
import { useApi, configApiRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
const MyComponent = () => {
|
||||
const configApi = useApi(configApiRef);
|
||||
const title = configApi.getString('app.title');
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
The `useApi` hook always returns a value, or throws an exception if the API ref could not be resolved to a registered implementation. For advanced use cases, where you explicitly want to optionally request a utility API that may or may not have been provided at runtime, you can use the underlying `useApiHolder` hook instead.
|
||||
|
||||
```tsx
|
||||
import { useApiHolder, configApiRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
const MyComponent = () => {
|
||||
const apis = useApiHolder();
|
||||
const configApi = apis.get(configApiRef); // may return undefined
|
||||
if (configApi) {
|
||||
const title = configApi.getString('app.title');
|
||||
// ...
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Via dependencies
|
||||
|
||||
Your utility APIs can depend on other utility APIs in their factories. You do this by declaring `deps` on your `createApiFactory`, and reading the outcome in your `factory`.
|
||||
|
||||
```tsx
|
||||
import {
|
||||
configApiRef,
|
||||
createApiExtension,
|
||||
createApiFactory,
|
||||
discoveryApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { MyApiImpl } from './MyApiImpl';
|
||||
|
||||
const myApi = createApiExtension({
|
||||
factory: createApiFactory({
|
||||
api: myApiRef,
|
||||
deps: {
|
||||
configApi: configApiRef,
|
||||
discoveryApi: discoveryApiRef,
|
||||
},
|
||||
factory: ({ configApi, discoveryApi }) => {
|
||||
return new MyApiImpl({ configApi, discoveryApi });
|
||||
},
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
Note how the `deps` section essentially assigns free-form names that you choose, to API refs. Here we for example map `configApiRef` to the name `configApi`, but that's just a convention. The framework will ensure that all of those deps get instantiated and passed into your `factory` function with the same set of names as your `deps`. At that point, `configApi` refers to an actual functioning instance of that API ref.
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
id: configuring
|
||||
title: Configuring Utility APIs
|
||||
sidebar_label: Configuring
|
||||
# prettier-ignore
|
||||
description: Configuring, extending, and overriding utility APIs
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
Utility APIs are extensions and can therefore optionally be amended with configurability, as well as inputs that other extensions attach themselves to. This section describes how to make use of that as a consumer of such utility APIs.
|
||||
|
||||
## Configuring
|
||||
|
||||
To configure your Utility API extension, first you'll need to know its ID. That ID is formed from the API ref ID; check [the naming patterns docs](../architecture/08-naming-patterns.md) for details.
|
||||
|
||||
Our example work API from [the creating section](./02-creating.md) would have the ID `api:plugin.example.work`. You configure it and all other extensions under the `app.extensions` section of your app-config.
|
||||
|
||||
```yaml title="in e.g. app-config.yaml or app-config.production.yaml"
|
||||
app:
|
||||
extensions:
|
||||
- api:plugin.example.work:
|
||||
config:
|
||||
goSlow: false
|
||||
- # ... other extensions
|
||||
```
|
||||
|
||||
It's important to note that the `extensions` are a list (mind the initial `-`), and that the `api:plugin.example.work` entry is an object such that the `config` key needs to be indented below it. If you do not get those two pieces right, the application may not start up correctly.
|
||||
|
||||
The extension config schema will tell you what parameters it supports. Here we override the `goSlow` extension config value, which replaces the default.
|
||||
|
||||
## Attaching extensions to inputs
|
||||
|
||||
Like with other extension types, you add input attachments to a Utility API by declaring the `attachTo` section of that attachment to point to the Utility APIs ID and input name.
|
||||
|
||||
Well written input-enabled extension often have extension creator functions that help you make such attachments. Those functions typically set the `attachTo` section correctly on your behalf so that you don't have to figure them out.
|
||||
|
||||
## Replacing a Utility API implementation
|
||||
|
||||
Like with other extension types, you replace Utility APIs with your own custom implementation using [extension overrides](../architecture/05-extension-overrides.md).
|
||||
|
||||
```tsx title="in your app"
|
||||
/* highlight-add-start */
|
||||
import { createExtensionOverrides } from '@backstage/frontend-plugin-api';
|
||||
|
||||
class CustomWorkImpl implements WorkApi {
|
||||
/* ... */
|
||||
}
|
||||
|
||||
const myOverrides = createExtensionOverrides({
|
||||
extensions: [
|
||||
createApiExtension({
|
||||
api: workApiRef,
|
||||
factory: () =>
|
||||
createApiFactory({
|
||||
api: workApiRef,
|
||||
factory: () => new CustomWorkImpl(),
|
||||
}),
|
||||
}),
|
||||
],
|
||||
});
|
||||
/* highlight-add-end */
|
||||
|
||||
// Remember to pass the overrides to your createApp
|
||||
export default createApp({
|
||||
features: [
|
||||
// ... other features
|
||||
/* highlight-add-next-line */
|
||||
myOverrides,
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
In this example the overriding extension is kept minimal, but just like any other extension it can also have `deps`, configurability, and inputs. Check out [the Creating section](./02-creating.md) for more details about that.
|
||||
|
||||
When you create a replacement extension, in general you may want to mimic its extension config schema or input shapes where applicable. This makes it an easier thing to slot in to an app, since it'll be responding to extensibility the same way as the original one did.
|
||||
@@ -0,0 +1,126 @@
|
||||
---
|
||||
id: migrating
|
||||
title: Migrating Utility APIs from the old frontend system
|
||||
sidebar_label: Migrating
|
||||
# prettier-ignore
|
||||
description: Migrating Utility APIs from the old frontend system
|
||||
---
|
||||
|
||||
> **NOTE: The new frontend system is in alpha and is only supported by a small number of plugins.**
|
||||
|
||||
If you are migrating your plugins or app over from the old frontend system, there are a few things to keep in mind in regards to utility APIs.
|
||||
|
||||
## Overview
|
||||
|
||||
- Migrate your repo overall to the latest release of Backstage
|
||||
- Follow the plugin migration guide <!-- TODO: Link -->
|
||||
- Optionally change your package dependencies and code from `core-*-api` to `frontend-*-api`
|
||||
- Keep the TypeScript interface and API ref exported as they were, except possibly reconsidering the choice of ID of the latter
|
||||
- Wrap the old API factory call in an extension using `createApiExtension`
|
||||
- Make sure that this extension is referenced by your migrated plugin
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This guide assumes that you first [upgrade your repo](../../getting-started/keeping-backstage-updated.md) to the latest release of Backstage. This ensures that you do not have to fight several types of incompatibilities and updates at the same time.
|
||||
|
||||
## Dependency changes
|
||||
|
||||
In this article we will discuss some old interfaces that you used to import from the `@backstage/core-plugin-api` package. Those are now generally lifted over to `@backstage/frontend-plugin-api`, next to the new interfaces that are specific to the new frontend system. If you want to, you can already update your `package.json` and code imports to only use the new plugin API, but for the time being you don't have to. The old core exports will continue to work for the foreseeable future.
|
||||
|
||||
To at least get access to the new interfaces, you'll need to run the following command. Note that it's just an example! It refers to `plugins/example`, which you'll have to change to the actual folder name that your package to migrate is in.
|
||||
|
||||
```bash title="from your repo root"
|
||||
yarn --cwd plugins/example add @backstage/frontend-plugin-api
|
||||
```
|
||||
|
||||
## React package interface and ref changes
|
||||
|
||||
Let's begin with [your `-react` package](../../architecture-decisions/adr011-plugin-package-structure.md). The act of exporting TypeScript interfaces and API refs have not changed from the old system. You can typically keep those as-is. For illustrative purposes, this is an example of an interface and its API ref:
|
||||
|
||||
```tsx title="in @internal/plugin-example-react"
|
||||
import { createApiRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
/**
|
||||
* Performs some work.
|
||||
* @public
|
||||
*/
|
||||
export interface WorkApi {
|
||||
doWork(): Promise<void>;
|
||||
}
|
||||
|
||||
/**
|
||||
* The work interface for the Example plugin.
|
||||
* @public
|
||||
*/
|
||||
export const workApiRef = createApiRef<WorkApi>({
|
||||
id: 'plugin.example.work',
|
||||
});
|
||||
```
|
||||
|
||||
In this example, the plugin ID already follows <!-- TODO: Link --> the common naming convention. If it doesn't, you may want to consider renaming that ID at this point. Don't worry, this won't hurt consumers in the old frontend system since the ID is mostly used for debugging purposes there. In the new system, it's much more important and appears in app-config files and similar.
|
||||
|
||||
Note at the top of the file that it uses the updated import from `@backstage/frontend-plugin-api` that we migrated in the previous section, instead of the old `@backstage/core-plugin-api`.
|
||||
|
||||
## Plugin package changes
|
||||
|
||||
Now let's turn to the main plugin package where the plugin itself is exported. You will probably already have a `createPlugin` call in here. Before we changed the `core-plugin-api` imports it'll have looked somewhat similar to the following:
|
||||
|
||||
```tsx title="in @internal/plugin-example, NOTE THIS IS LEGACY CODE"
|
||||
import {
|
||||
storageApiRef,
|
||||
createPlugin,
|
||||
createApiFactory,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { workApiRef } from '@internal/plugin-example-react';
|
||||
import { WorkImpl } from './WorkImpl';
|
||||
|
||||
const exampleWorkApi = createApiFactory({
|
||||
api: workApiRef,
|
||||
deps: { storageApi: storageApiRef },
|
||||
factory: ({ storageApi }) => new WorkImpl({ storageApi }),
|
||||
});
|
||||
|
||||
/** @public */
|
||||
export const catalogPlugin = createPlugin({
|
||||
id: 'example',
|
||||
apis: [exampleWorkApi],
|
||||
});
|
||||
```
|
||||
|
||||
The major changes we'll make are
|
||||
|
||||
- Optionally change the old imports to the new package as per the top section of this guide
|
||||
- Wrap the existing API factory in a `createApiExtension`
|
||||
- Change to the new version of `createPlugin` which exports this extension
|
||||
- Change the plugin export to be the default instead
|
||||
|
||||
The end result, after simplifying imports and cleaning up a bit, might look like this:
|
||||
|
||||
```tsx title="in @internal/plugin-example"
|
||||
import {
|
||||
storageApiRef,
|
||||
createPlugin,
|
||||
createApiFactory,
|
||||
createApiExtension,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { workApiRef } from '@internal/plugin-example-react';
|
||||
import { WorkImpl } from './WorkImpl';
|
||||
|
||||
const exampleWorkApi = createApiExtension({
|
||||
factory: createApiFactory({
|
||||
api: workApiRef,
|
||||
deps: { storageApi: storageApiRef },
|
||||
factory: ({ storageApi }) => new WorkImpl({ storageApi }),
|
||||
}),
|
||||
});
|
||||
|
||||
/** @public */
|
||||
export default createPlugin({
|
||||
id: 'example',
|
||||
extensions: [exampleWorkApi],
|
||||
});
|
||||
```
|
||||
|
||||
## Further work
|
||||
|
||||
Since utility APIs are now complete extensions, you may want to take a bigger look at how they used to be used, and what the new frontend system offers. You may for example consider [adding configurability or inputs](./02-creating.md) to your API, if that makes sense for your current application.
|
||||
@@ -134,7 +134,7 @@ links:
|
||||
|
||||
There are multiple authentication providers available for you to use with
|
||||
Backstage, feel free to follow
|
||||
[the instructions for adding authentication](../auth/).
|
||||
[the instructions for adding authentication](../auth/index.md).
|
||||
|
||||
For this tutorial we choose to use GitHub, a free service most of you might be
|
||||
familiar with. For other options, see
|
||||
@@ -205,7 +205,7 @@ Restart Backstage from the terminal, by stopping it with `Control-C`, and starti
|
||||
To learn more about Authentication in Backstage, here are some docs you
|
||||
could read:
|
||||
|
||||
- [Authentication in Backstage](../auth/)
|
||||
- [Authentication in Backstage](../auth/index.md)
|
||||
- [Using organizational data from GitHub](../integrations/github/org.md)
|
||||
|
||||
### Setting up a GitHub Integration
|
||||
@@ -254,7 +254,7 @@ integrations:
|
||||
|
||||
Some helpful links, for if you want to learn more about:
|
||||
|
||||
- [Other available integrations](../integrations/)
|
||||
- [Other available integrations](../integrations/index.md)
|
||||
- [Using GitHub Apps instead of a Personal Access Token](../integrations/github/github-apps.md#docsNav)
|
||||
|
||||
### Explore what we've done so far
|
||||
|
||||
@@ -32,7 +32,7 @@ catalog:
|
||||
bucketName: sample-bucket
|
||||
prefix: prefix/ # optional
|
||||
region: us-east-2 # optional, uses the default region otherwise
|
||||
schedule: # optional; same options as in TaskScheduleDefinition
|
||||
schedule: # same options as in TaskScheduleDefinition
|
||||
# supports cron, ISO duration, "human duration" as used in code
|
||||
frequency: { minutes: 30 }
|
||||
# supports ISO duration, "human duration" as used in code
|
||||
@@ -52,7 +52,7 @@ catalog:
|
||||
bucketName: sample-bucket
|
||||
prefix: prefix/ # optional
|
||||
region: us-east-2 # optional, uses the default region otherwise
|
||||
schedule: # optional; same options as in TaskScheduleDefinition
|
||||
schedule: # same options as in TaskScheduleDefinition
|
||||
# supports cron, ISO duration, "human duration" as used in code
|
||||
frequency: { minutes: 30 }
|
||||
# supports ISO duration, "human duration" as used in code
|
||||
@@ -79,12 +79,6 @@ const builder = await CatalogBuilder.create(env);
|
||||
builder.addEntityProvider(
|
||||
AwsS3EntityProvider.fromConfig(env.config, {
|
||||
logger: env.logger,
|
||||
// optional: alternatively, use scheduler with schedule defined in app-config.yaml
|
||||
schedule: env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 30 },
|
||||
timeout: { minutes: 3 },
|
||||
}),
|
||||
// optional: alternatively, use schedule
|
||||
scheduler: env.scheduler,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -71,7 +71,7 @@ The parameters available are:
|
||||
- **`repository:`** _(optional)_ The repository name. Wildcards are supported as show on the examples above. If not set, all repositories will be searched.
|
||||
- **`path:`** _(optional)_ Where to find catalog-info.yaml files. Defaults to /catalog-info.yaml.
|
||||
- **`branch:`** _(optional)_ The branch name to use.
|
||||
- **`schedule`** _(optional)_:
|
||||
- **`schedule`**:
|
||||
- **`frequency`**:
|
||||
How often you want the task to run. The system does its best to avoid overlapping invocations.
|
||||
- **`timeout`**:
|
||||
|
||||
@@ -49,19 +49,6 @@ export default async function createPlugin(
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively to the config-based schedule, you can use
|
||||
|
||||
```ts
|
||||
/* highlight-remove-next-line */
|
||||
scheduler: env.scheduler,
|
||||
/* highlight-add-start */
|
||||
schedule: env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 30 },
|
||||
timeout: { minutes: 3 },
|
||||
}),
|
||||
/* highlight-add-end */
|
||||
```
|
||||
|
||||
### Installation with Events Support
|
||||
|
||||
Please follow the installation instructions at
|
||||
@@ -131,7 +118,7 @@ catalog:
|
||||
filters: # optional
|
||||
projectKey: '^apis-.*$' # optional; RegExp
|
||||
repoSlug: '^service-.*$' # optional; RegExp
|
||||
schedule: # optional; same options as in TaskScheduleDefinition
|
||||
schedule: # same options as in TaskScheduleDefinition
|
||||
# supports cron, ISO duration, "human duration" as used in code
|
||||
frequency: { minutes: 30 }
|
||||
# supports ISO duration, "human duration" as used in code
|
||||
@@ -153,7 +140,7 @@ catalog:
|
||||
Regular expression used to filter results based on the project key.
|
||||
- **`repoSlug`** _(optional)_:
|
||||
Regular expression used to filter results based on the repo slug.
|
||||
- **`schedule`** _(optional)_:
|
||||
- **`schedule`**:
|
||||
- **`frequency`**:
|
||||
How often you want the task to run. The system does its best to avoid overlapping invocations.
|
||||
- **`timeout`**:
|
||||
|
||||
@@ -38,12 +38,6 @@ export default async function createPlugin(
|
||||
builder.addEntityProvider(
|
||||
BitbucketServerEntityProvider.fromConfig(env.config, {
|
||||
logger: env.logger,
|
||||
// optional: alternatively, use scheduler with schedule defined in app-config.yaml
|
||||
schedule: env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 30 },
|
||||
timeout: { minutes: 3 },
|
||||
}),
|
||||
// optional: alternatively, use schedule
|
||||
scheduler: env.scheduler,
|
||||
}),
|
||||
);
|
||||
@@ -69,7 +63,7 @@ catalog:
|
||||
filters: # optional
|
||||
projectKey: '^apis-.*$' # optional; RegExp
|
||||
repoSlug: '^service-.*$' # optional; RegExp
|
||||
schedule: # optional; same options as in TaskScheduleDefinition
|
||||
schedule: # same options as in TaskScheduleDefinition
|
||||
# supports cron, ISO duration, "human duration" as used in code
|
||||
frequency: { minutes: 30 }
|
||||
# supports ISO duration, "human duration" as used in code
|
||||
@@ -87,7 +81,7 @@ catalog:
|
||||
Regular expression used to filter results based on the project key.
|
||||
- **`repoSlug`** _(optional)_:
|
||||
Regular expression used to filter results based on the repo slug.
|
||||
- **`schedule`** _(optional)_:
|
||||
- **`schedule`**:
|
||||
- **`frequency`**:
|
||||
How often you want the task to run. The system does its best to avoid overlapping invocations.
|
||||
- **`timeout`**:
|
||||
|
||||
@@ -26,18 +26,11 @@ Then add the plugin to the plugin catalog `packages/backend/src/plugins/catalog.
|
||||
```ts
|
||||
/* packages/backend/src/plugins/catalog.ts */
|
||||
import { GerritEntityProvider } from '@backstage/plugin-catalog-backend-module-gerrit';
|
||||
import { Duration } from 'luxon';
|
||||
const builder = await CatalogBuilder.create(env);
|
||||
/** ... other processors and/or providers ... */
|
||||
builder.addEntityProvider(
|
||||
GerritEntityProvider.fromConfig(env.config, {
|
||||
logger: env.logger,
|
||||
// optional: alternatively, use scheduler with schedule defined in app-config.yaml
|
||||
schedule: env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 30 },
|
||||
timeout: { minutes: 3 },
|
||||
}),
|
||||
// optional: alternatively, use schedule
|
||||
scheduler: env.scheduler,
|
||||
}),
|
||||
);
|
||||
@@ -57,7 +50,7 @@ catalog:
|
||||
host: gerrit-your-company.com
|
||||
branch: master # Optional
|
||||
query: 'state=ACTIVE&prefix=webapps'
|
||||
schedule: # optional; same options as in TaskScheduleDefinition
|
||||
schedule:
|
||||
# supports cron, ISO duration, "human duration" as used in code
|
||||
frequency: { minutes: 30 }
|
||||
# supports ISO duration, "human duration" as used in code
|
||||
|
||||
@@ -40,12 +40,6 @@ export default async function createPlugin(
|
||||
builder.addEntityProvider(
|
||||
GithubEntityProvider.fromConfig(env.config, {
|
||||
logger: env.logger,
|
||||
// optional: alternatively, use scheduler with schedule defined in app-config.yaml
|
||||
schedule: env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 30 },
|
||||
timeout: { minutes: 3 },
|
||||
}),
|
||||
// optional: alternatively, use schedule
|
||||
scheduler: env.scheduler,
|
||||
}),
|
||||
);
|
||||
@@ -85,12 +79,6 @@ export default async function createPlugin(
|
||||
/* highlight-add-start */
|
||||
const githubProvider = GithubEntityProvider.fromConfig(env.config, {
|
||||
logger: env.logger,
|
||||
// optional: alternatively, use scheduler with schedule defined in app-config.yaml
|
||||
schedule: env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 30 },
|
||||
timeout: { minutes: 3 },
|
||||
}),
|
||||
// optional: alternatively, use schedule
|
||||
scheduler: env.scheduler,
|
||||
});
|
||||
env.eventBroker.subscribe(githubProvider);
|
||||
@@ -122,7 +110,7 @@ catalog:
|
||||
filters:
|
||||
branch: 'main' # string
|
||||
repository: '.*' # Regex
|
||||
schedule: # optional; same options as in TaskScheduleDefinition
|
||||
schedule: # same options as in TaskScheduleDefinition
|
||||
# supports cron, ISO duration, "human duration" as used in code
|
||||
frequency: { minutes: 30 }
|
||||
# supports ISO duration, "human duration" as used in code
|
||||
@@ -213,7 +201,7 @@ This provider supports multiple organizations via unique provider IDs.
|
||||
Defaults to `false`.
|
||||
Due to limitations in the GitHub API's ability to query for repository objects, this option cannot be used in
|
||||
conjunction with wildcards in the `catalogPath`.
|
||||
- **`schedule`** _(optional)_:
|
||||
- **`schedule`**:
|
||||
- **`frequency`**:
|
||||
How often you want the task to run. The system does its best to avoid overlapping invocations.
|
||||
- **`timeout`**:
|
||||
@@ -229,13 +217,12 @@ GitHub [rate limits](https://docs.github.com/en/rest/overview/resources-in-the-r
|
||||
accounts). The snippet below refreshes the Backstage catalog data every 35 minutes, which issues an API request for each discovered location.
|
||||
|
||||
If your requests are too frequent then you may get throttled by
|
||||
rate limiting. You can change the refresh frequency of the catalog in your `packages/backend/src/plugins/catalog.ts` file:
|
||||
rate limiting. You can change the refresh frequency of the catalog in your `app-config.yaml` file by controlling the `schedule`.
|
||||
|
||||
```typescript
|
||||
schedule: env.scheduler.createScheduledTaskRunner({
|
||||
frequency: { minutes: 35 },
|
||||
timeout: { minutes: 30 },
|
||||
}),
|
||||
```yaml
|
||||
schedule:
|
||||
frequency: { minutes: 35 }
|
||||
timeout: { minutes: 3 }
|
||||
```
|
||||
|
||||
More information about scheduling can be found on the [TaskScheduleDefinition](https://backstage.io/docs/reference/backend-tasks.taskscheduledefinition) page.
|
||||
|
||||
@@ -182,3 +182,11 @@ The TypeScript release cadence is roughly every three months. An important aspec
|
||||
Our policy is to support the last 3 TypeScript versions, for example 4.8, 4.9, and 5.0. Converted to time, this means that we typically support the TypeScript version from the last six to nine months, depending on where in the TypeScript release window we are. This policy applies as a snapshot at the time of any given Backstage release, new TypeScript releases only apply to the following Backstage main-line release, not to the current one.
|
||||
|
||||
For anyone maintaining their own Backstage project, this means that you should strive to bump to the latest TypeScript version at least every 6 months, or you may encounter breakages as you upgrade Backstage packages. If you encounter any issues in doing so, please [file an issue in the main Backstage repository](https://github.com/backstage/backstage/issues/new/choose), as per this policy we should always support the latest version. In order to ensure that we do not start using new TypeScript features too early, the Backstage project itself uses the version at the beginning of the currently supported window, in the above example that would be version 4.8.
|
||||
|
||||
## PostgreSQL Releases
|
||||
|
||||
The Backstage project recommends and supports using PostgreSQL for persistent storage.
|
||||
|
||||
The PostgreSQL [versioning policy](https://www.postgresql.org/support/versioning/) is to release a new major version every year with new features which is then supported for 5 years after its initial release.
|
||||
|
||||
Our policy mirrors the PostgreSQL versioning policy - we will support the last 5 major versions. We will also test the newest and oldest versions in that range. For example, if the range we support is currently 12 to 16, then we would only test 12 and 16 explicitly.
|
||||
|
||||
@@ -6,25 +6,24 @@ description: Details of the new backend system
|
||||
|
||||
## Status
|
||||
|
||||
The new backend system is in alpha, and some plugins do not yet fully implement it. But do feel free to try it out! We would love to hear back about your impressions.
|
||||
The new backend system is released and ready for production use, and many plugins and modules have already been migrated. We recommend all plugins and deployments to migrate to the new system.
|
||||
|
||||
You can find an example backend setup in [the backend-next package](https://github.com/backstage/backstage/tree/master/packages/backend-next).
|
||||
|
||||
## Overview
|
||||
|
||||
The new Backstage backend system is being built to help make it simpler to install backend plugins and to keep projects up to date. It also changes the foundation to one that makes it a lot easier to evolve plugins and the system itself with minimal disruption or cause for breaking changes. You can read more about the reasoning in the [original RFC](https://github.com/backstage/backstage/issues/11611).
|
||||
The new Backstage backend system was built to help make it simpler to install backend plugins and to keep projects up to date. It also changed the foundation to one that makes it a lot easier to evolve plugins and the system itself with minimal disruption or cause for breaking changes. You can read more about the reasoning in the [original RFC](https://github.com/backstage/backstage/issues/11611).
|
||||
|
||||
One of the goals of the new system was to reduce the code needed for setting up a Backstage backend and installing plugins. This is an example of how you create, add features, and start up your backend in the new system:
|
||||
|
||||
```ts
|
||||
import { createBackend } from '@backstage/backend-defaults';
|
||||
import { catalogPlugin } from '@backstage/plugin-catalog-backend';
|
||||
|
||||
// Create your backend instance
|
||||
const backend = createBackend();
|
||||
|
||||
// Install all desired features
|
||||
backend.add(catalogPlugin());
|
||||
backend.add(import('@backstage/plugin-catalog-backend'));
|
||||
|
||||
// Start up the backend
|
||||
await backend.start();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,135 @@
|
||||
---
|
||||
id: v1.21.0
|
||||
title: v1.21.0
|
||||
description: Backstage Release v1.21.0
|
||||
---
|
||||
|
||||
These are the release notes for the v1.21.0 release of [Backstage](https://backstage.io/).
|
||||
|
||||
A huge thanks to the whole team of maintainers and contributors as well as the amazing Backstage Community for the hard work in getting this release developed and done.
|
||||
|
||||
## Highlights
|
||||
|
||||
### New Frontend System Alpha
|
||||
|
||||
This release marks the alpha release of the new frontend system, which has been in an experimental state since implementation began in the middle of 2023. This new system brings declarative integration of plugins, which is the ability to integrate new features into a Backstage app without writing any TypeScript code. Through this capability it also paves the way for supporting dynamic plugin installation at runtime.
|
||||
|
||||
The alpha release is a point of increased stability following the earlier experimental phase. There is now a complete system that lets you build out a full application, supported by [documentation](https://backstage.io/docs/frontend-system/). From this point on any breaking changes will also be clearly marked in the changelog.
|
||||
|
||||
There is still a long road ahead to a stable release, and this is not the time to migrate existing applications. There are only a few plugins that support this system so far, and if you want to add to a plugin that you own, please do so under an `/alpha` sub-path export.
|
||||
|
||||
Still, we encourage you to explore this new system to see whether you are confident in this path forward. If you have feedback or want to know more you can reach out in the #declarative-integration channel on Discord, or join an [Adoption SIG](https://github.com/backstage/community/blob/main/sigs/sig-adoption/README.md) meeting where the new system is frequently discussed. You can also check out our [maintainer talk at KubeCon NA 2023](https://youtu.be/ONMBYnhxnNU?t=436), where we talk about this new system and show a couple of demos.
|
||||
|
||||
### React Router Beta deprecation
|
||||
|
||||
This release of Backstage officially deprecates, but does _not_ immediately remove, support for old beta versions of [`react-router` 6](https://reactrouter.com/). Actual support for beta versions will be removed entirely in an upcoming release of Backstage. Please upgrade your own Backstage project as soon as possible to a stable version of `react-router`, by [following this guide](https://backstage.io/docs/tutorials/react-router-stable-migration/).
|
||||
|
||||
### New PostgreSQL versioning policy
|
||||
|
||||
The Backstage project has now settled on [a clearer policy](https://backstage.io/docs/overview/versioning-policy/#postgresql-releases) for what versions of PostgreSQL that it supports. In short, we support the last five [released major versions](https://www.postgresql.org/support/versioning/), and actively test against the first and last of those five, in a rolling window over time.
|
||||
|
||||
As part of this, the `TestDatabases` utility class now supports all of the last major versions of PostgreSQL in addition to the ones it supported before. You can also call `TestDatabases.setDefaults` inside your `setupTests.ts` file to configure the set of engines to test against, instead of enumerating them in every individual test.
|
||||
|
||||
Contributed by [@awanlin](https://github.com/awanlin) in [#21510](https://github.com/backstage/backstage/pull/21510)
|
||||
|
||||
### `UnifiedTheme` Now Supports Overrides
|
||||
|
||||
You can now supply overrides for Backstage components when using `createUnifiedTheme`. We've updated the demo site’s Aperture theme to work with this and you can see the code for that [here](https://github.com/backstage/demo/blob/402cbb358cddacd59b339580bef0a4c5c2c7e013/packages/app/src/theme/aperture.ts#L85).
|
||||
|
||||
If you are switching from the old way of defining a theme to `createUnifiedTheme`, note that it uses the MUI v5 overrides format. The style overrides are now nested in a `styleOverrides` key, and if you want access to the theme you’ll need to use a callback:
|
||||
|
||||
```ts
|
||||
BackstageHeaderTabs: {
|
||||
styleOverrides: {
|
||||
defaultTab: {
|
||||
textTransform: 'none',
|
||||
},
|
||||
},
|
||||
},
|
||||
MuiChip: {
|
||||
styleOverrides: {
|
||||
root: ({ theme }) => ({
|
||||
color: theme.palette.primary.dark,
|
||||
}),
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
### Catalog pagination
|
||||
|
||||
`CatalogIndexPage` now offers an optional pagination feature, designed to accommodate adopters managing extensive catalogs. This new capability allows for better handling of large amounts of data.
|
||||
|
||||
To activate the pagination mode, simply update your `App.tsx` as follows:
|
||||
|
||||
```diff
|
||||
const routes = (
|
||||
<FlatRoutes>
|
||||
...
|
||||
- <Route path="/catalog" element={<CatalogIndexPage />} />
|
||||
+ <Route path="/catalog" element={<CatalogIndexPage pagination />} />
|
||||
...
|
||||
```
|
||||
|
||||
In case you have a custom catalog page and you want to enable pagination, you need to pass the `pagination` prop to `EntityListProvider` instead. For now both column sorting and search filtering are still done locally, meaning they only apply to each individual page. This is something we will improve in the future and we still wanted to make this feature available early as it can greatly improve the performance of the catalog page.
|
||||
|
||||
### Azure DevOps Multi-Org Support
|
||||
|
||||
The Azure DevOps plugin now has multi-org support and there is a new processor to help with adding the needed annotations. Contributed by [@awanlin](https://github.com/awanlin) in [#19622](https://github.com/backstage/backstage/issues/19622)
|
||||
|
||||
### New Authentication providers
|
||||
|
||||
A new Atlassian authentication provider has been added to `@backstage/plugin-auth-backend`. Contributed by [@handsamtw](https://github.com/handsamtw) in [#21007](https://github.com/backstage/backstage/pull/21007)
|
||||
|
||||
A new VMware Cloud authentication provider has been added to `@backstage/plugin-auth-backend`. Contributed by [@luchillo17](https://github.com/luchillo17) in [#21337](https://github.com/backstage/backstage/pull/21337)
|
||||
|
||||
### Kubernetes single cluster selection
|
||||
|
||||
You can now select a `single` kubernetes cluster that the entity is part of from all your defined kubernetes clusters, by providing the `backstage.io/kubernetes-cluster` annotation with the defined cluster name.
|
||||
|
||||
If you do not specify the annotation then by default it fetches all defined kubernetes clusters.
|
||||
|
||||
To apply, update your `catalog-info.yaml`as follows:
|
||||
|
||||
```diff
|
||||
metadata:
|
||||
annotations:
|
||||
'backstage.io/kubernetes-id': dice-roller
|
||||
'backstage.io/kubernetes-namespace': dice-space
|
||||
+ 'backstage.io/kubernetes-cluster': dice-cluster
|
||||
'backstage.io/kubernetes-label-selector': 'app=my-app,component=front-end'
|
||||
```
|
||||
|
||||
Contributed by [@deepan10](https://github.com/deepan10) in [#20954](https://github.com/backstage/backstage/pull/20954)
|
||||
|
||||
### BREAKING: Repo tools generated API Reports path changes
|
||||
|
||||
API Reports generated for sub-path exports now place the name as a suffix rather than prefix, for example `api-report-alpha.md` instead of `alpha-api-report.md`. When upgrading to this version you'll need to re-create any such API reports and delete the old ones.
|
||||
|
||||
### PagerDuty plugin changes home 🏡
|
||||
|
||||
The [PagerDuty](https://www.pagerduty.com/) plugin has been marked as deprecated in favor of [pagerduty/backstage-plugin](https://github.com/pagerduty/backstage-plugin) which is maintained by PagerDuty themselves! We encourage you to [migrate to @pagerduty/backstage-plugin](https://pagerduty.github.io/backstage-plugin-docs/migration/) in order to receive future updates.
|
||||
|
||||
Congrats to the PagerDuty folks for taking ownership of the plugin 👏
|
||||
|
||||
Contributed by [@t1agob](https://github.com/t1agob) in [#21436](https://github.com/backstage/backstage/pull/21436)
|
||||
|
||||
## Security Fixes
|
||||
|
||||
This release does not contain any security fixes.
|
||||
|
||||
## Upgrade path
|
||||
|
||||
We recommend that you keep your Backstage project up to date with this latest release. For more guidance on how to upgrade, check out the documentation for [keeping Backstage updated](https://backstage.io/docs/getting-started/keeping-backstage-updated).
|
||||
|
||||
## Links and References
|
||||
|
||||
Below you can find a list of links and references to help you learn about and start using this new release.
|
||||
|
||||
- [Backstage official website](https://backstage.io/), [documentation](https://backstage.io/docs/), and [getting started guide](https://backstage.io/docs/getting-started/)
|
||||
- [GitHub repository](https://github.com/backstage/backstage)
|
||||
- Backstage's [versioning and support policy](https://backstage.io/docs/overview/versioning-policy)
|
||||
- [Community Discord](https://discord.gg/backstage-687207715902193673) for discussions and support
|
||||
- [Changelog](https://github.com/backstage/backstage/tree/master/docs/releases/v1.21.0-changelog.md)
|
||||
- Backstage [Demos](https://backstage.io/demos), [Blog](https://backstage.io/blog), [Roadmap](https://backstage.io/docs/overview/roadmap) and [Plugins](https://backstage.io/plugins)
|
||||
|
||||
Sign up for our [newsletter](https://info.backstage.spotify.com/newsletter_subscribe) if you want to be informed about what is happening in the world of Backstage.
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user