Merge branch 'backstage:master' into custom_auth_connector
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
---
|
||||
'@backstage/kubernetes-react': patch
|
||||
'@backstage/plugin-kubernetes-react': patch
|
||||
---
|
||||
|
||||
Fixed bug in string-to-integer conversion to properly handle decimal values with BigInt.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Bumped create-app version.
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-kubernetes-cluster': patch
|
||||
'@backstage/plugin-kubernetes-react': patch
|
||||
---
|
||||
|
||||
Improved rendering of Kubernetes resources' metadata.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Added `nestedValuesAsYaml` option to `StructuredMetadataTable` to render data as yaml.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Ignore benign database conflict errors during stitching, now logged with debug level instead.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
Fixed an issue where invalid expressions or non-object values in `step.each` caused an error.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/canon': minor
|
||||
---
|
||||
|
||||
This is the first alpha release for Canon. As part of this release we are introducing 5 layout components and 7 components. All theming is done through CSS variables.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Add missing route ref to the `/alpha` entity content extension.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-app-api': patch
|
||||
---
|
||||
|
||||
The log message written when plugins fail to initialize now includes the error that caused the plugin startup to fail.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Fixed the file path pattern of many static assets output as part of the frontend build process, where there was an extra `.` before the extension, leading to names like `image-af7946b..png`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-ldap': patch
|
||||
---
|
||||
|
||||
Make `ldapOrg` config key optional
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-defaults': minor
|
||||
---
|
||||
|
||||
This change introduces the `auditor` service implementation details.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-plugin-api': minor
|
||||
---
|
||||
|
||||
This change introduces the `auditor` service definition.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/backend-test-utils': minor
|
||||
---
|
||||
|
||||
This change introduces mocks for the `auditor` service.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': minor
|
||||
---
|
||||
|
||||
This change integrates the `auditor` service into the Catalog plugin.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': minor
|
||||
---
|
||||
|
||||
This change integrates the `auditor` service into the Scaffolder plugin.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-node': minor
|
||||
---
|
||||
|
||||
This change introduces an optional `taskId` property to `TaskContext`.
|
||||
+41
-1
@@ -196,5 +196,45 @@
|
||||
"@backstage/plugin-user-settings-backend": "0.2.29",
|
||||
"@backstage/plugin-user-settings-common": "0.0.1"
|
||||
},
|
||||
"changesets": []
|
||||
"changesets": [
|
||||
"afraid-experts-explain",
|
||||
"afraid-kids-jog",
|
||||
"beige-dingos-destroy",
|
||||
"bigint-convert",
|
||||
"clean-squids-build",
|
||||
"create-app-1737468383",
|
||||
"curly-humans-prove",
|
||||
"curvy-ways-play",
|
||||
"cyan-grapes-confess",
|
||||
"dry-flies-rhyme",
|
||||
"dry-horses-report",
|
||||
"eleven-mice-sleep",
|
||||
"fair-mangos-sleep",
|
||||
"few-shrimps-kiss",
|
||||
"gentle-actors-sleep",
|
||||
"green-jokes-provide",
|
||||
"hungry-mirrors-sniff",
|
||||
"loud-walls-build",
|
||||
"old-moons-end",
|
||||
"old-phones-rest",
|
||||
"olive-boxes-hide-backend-defaults",
|
||||
"olive-boxes-hide-backend-plugin-api",
|
||||
"olive-boxes-hide-backend-test-utils",
|
||||
"olive-boxes-hide-catalog-backend",
|
||||
"olive-boxes-hide-scaffolder-backend",
|
||||
"olive-boxes-hide-scaffolder-node",
|
||||
"orange-brooms-lick",
|
||||
"proud-dryers-act",
|
||||
"proud-hornets-cheer",
|
||||
"quiet-phones-sell",
|
||||
"sharp-vans-protect",
|
||||
"sharp-years-drive",
|
||||
"short-vans-reflect",
|
||||
"soft-planets-mate",
|
||||
"tall-scissors-sip",
|
||||
"tame-hats-shout",
|
||||
"twelve-eyes-stare",
|
||||
"warm-masks-ring",
|
||||
"wise-apes-juggle"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
'@backstage/plugin-scaffolder-react': patch
|
||||
---
|
||||
|
||||
Add schema output return type to the `makeFieldSchema` function return
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': patch
|
||||
---
|
||||
|
||||
Allow route references to be installed in multiple app instances as long as their name is the same.
|
||||
@@ -228,6 +228,7 @@ LocalStack
|
||||
lockdown
|
||||
lockfile
|
||||
lookbehind
|
||||
lowercased
|
||||
lunr
|
||||
Luxon
|
||||
magiclink
|
||||
|
||||
@@ -27,10 +27,10 @@ example below on how to set your light and dark mode.
|
||||
lang="css"
|
||||
code={`/** Light theme **/
|
||||
[data-theme='light'] {
|
||||
--canon-accent: #1ed760;
|
||||
--canon-bg-accent: #1ed760;
|
||||
--canon-bg: #fff;
|
||||
--canon-surface-1: #f5f5f5;
|
||||
--canon-surface-2: #000;
|
||||
--canon-bg-elevated: #f5f5f5;
|
||||
--canon-bg: #000;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-text-primary: #f0f0f0;
|
||||
@@ -42,10 +42,10 @@ example below on how to set your light and dark mode.
|
||||
|
||||
/** Dark theme **/
|
||||
[data-theme='dark'] {
|
||||
--canon-accent: #1ed760;
|
||||
--canon-bg-accent: #1ed760;
|
||||
--canon-bg: #000;
|
||||
--canon-bg-elevated: #f5f5f5;
|
||||
--canon-bg: #000;
|
||||
--canon-surface-1: #f5f5f5;
|
||||
--canon-surface-2: #000;
|
||||
--canon-outline: #666;
|
||||
--canon-outline-focus: #ccc;
|
||||
--canon-text-primary: #fff;
|
||||
@@ -73,7 +73,7 @@ application to match your brand.
|
||||
<Table.Body>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-accent</Chip>
|
||||
<Chip head>--canon-bg-accent</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The accent color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
@@ -85,13 +85,13 @@ application to match your brand.
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-surface-1</Chip>
|
||||
<Chip head>--canon-bg-elevated</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The first surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Chip head>--canon-surface-2</Chip>
|
||||
<Chip head>--canon-bg</Chip>
|
||||
</Table.Cell>
|
||||
<Table.Cell>The second surface color for the theme.</Table.Cell>
|
||||
</Table.Row>
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
.breakpointContent {
|
||||
height: 100%;
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--canon-border-base);
|
||||
background-color: var(--canon-background-base);
|
||||
border: 1px solid var(--canon-border);
|
||||
background-color: var(--canon-bg);
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
background-color: var(--canon-background);
|
||||
background-color: var(--canon-bg);
|
||||
color: var(--canon-text-primary);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
@@ -41,8 +41,8 @@ iframe {
|
||||
}
|
||||
|
||||
.ͼ2 .cm-gutters {
|
||||
background-color: var(--canon-surface-2);
|
||||
border-right: 1px solid var(--canon-border-base);
|
||||
background-color: var(--canon-bg);
|
||||
border-right: 1px solid var(--canon-border);
|
||||
transition: background-color 0.2s ease-in-out, border-color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.codeBlock {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border: 1px solid var(--canon-border);
|
||||
position: relative;
|
||||
background: transparent;
|
||||
overflow-x: auto;
|
||||
@@ -15,7 +15,7 @@
|
||||
}
|
||||
|
||||
.title {
|
||||
border-bottom: 1px solid var(--canon-border-base);
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
padding: 12px 20px;
|
||||
font-size: 0.875rem;
|
||||
color: var(--canon-text-secondary);
|
||||
|
||||
@@ -11,13 +11,13 @@ import { createTheme } from '@uiw/codemirror-themes';
|
||||
import { tags as t } from '@lezer/highlight';
|
||||
|
||||
const defaultTheme = `:root {
|
||||
--canon-accent: #000;
|
||||
--canon-bg-accent: #000;
|
||||
}`;
|
||||
|
||||
const myTheme = createTheme({
|
||||
theme: 'light',
|
||||
settings: {
|
||||
background: 'var(--canon-surface-1)',
|
||||
background: 'var(--canon-bg-elevated)',
|
||||
backgroundImage: '',
|
||||
foreground: '#6182B8',
|
||||
caret: '#5d00ff',
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
right: 16px;
|
||||
width: 240px;
|
||||
height: 47px;
|
||||
background-color: var(--canon-surface-1);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
border-radius: 0.375rem;
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border: 1px solid var(--canon-border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
@@ -31,8 +31,8 @@
|
||||
.header {
|
||||
height: 46px;
|
||||
flex-shrink: 0;
|
||||
border-bottom: 1px solid var(--canon-border-base);
|
||||
background-color: var(--canon-surface-1);
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
@@ -65,7 +65,7 @@
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
color: #fff;
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
color: var(--canon-text-primary);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
border-radius: 0.25rem;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
.container {
|
||||
display: flex;
|
||||
background-color: var(--canon-surface-1);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
padding: var(--canon-space-6);
|
||||
border-radius: var(--canon-border-radius-xs);
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border-radius: var(--canon-radius-2);
|
||||
border: 1px solid var(--canon-border);
|
||||
margin-bottom: var(--canon-space-6);
|
||||
gap: var(--canon-space-6);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
@@ -29,14 +29,14 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
background-color: var(--canon-surface-2);
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border-radius: var(--canon-border-radius-xs);
|
||||
background-color: var(--canon-bg);
|
||||
border: 1px solid var(--canon-border);
|
||||
border-radius: var(--canon-radius-2);
|
||||
padding: 0 var(--canon-space-3);
|
||||
height: 28px;
|
||||
border-radius: 100px;
|
||||
margin-top: var(--canon-space-3);
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
color: var(--canon-text-primary);
|
||||
gap: var(--canon-space-2);
|
||||
}
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
height: 80px;
|
||||
border: 1px solid #d3d3d3;
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--canon-background);
|
||||
background-color: var(--canon-bg);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--canon-surface-1);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
width: 300px;
|
||||
height: 100vh;
|
||||
color: var(--canon-text-primary);
|
||||
background-color: var(--canon-surface-1);
|
||||
border-right: 1px solid var(--canon-border-base);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
border-right: 1px solid var(--canon-border);
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
@@ -44,7 +44,7 @@
|
||||
|
||||
.sectionTitle {
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
padding: 12px 0;
|
||||
}
|
||||
@@ -63,23 +63,23 @@
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
}
|
||||
}
|
||||
|
||||
.line.active {
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
}
|
||||
|
||||
.lineTitle {
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-primary);
|
||||
}
|
||||
|
||||
.lineStatus {
|
||||
font-family: var(--docs-font);
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
color: var(--canon-text-secondary);
|
||||
}
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
|
||||
.preview {
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-base);
|
||||
background-color: var(--canon-background);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
background-color: var(--canon-bg);
|
||||
transition: all 0.2s ease-in-out;
|
||||
padding: 1px;
|
||||
position: relative;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
|
||||
.wrapper {
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border: 1px solid var(--canon-border);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 1rem;
|
||||
@@ -33,7 +33,7 @@
|
||||
padding: 12px 16px !important;
|
||||
border: none !important;
|
||||
text-align: left;
|
||||
background-color: var(--canon-surface-1) !important;
|
||||
background-color: var(--canon-bg-elevated) !important;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
@@ -43,14 +43,14 @@
|
||||
}
|
||||
|
||||
.tableHeaderCell {
|
||||
border-bottom: 1px solid var(--canon-border-base) !important;
|
||||
border-bottom: 1px solid var(--canon-border) !important;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tableRow {
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--canon-border-base);
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
@@ -59,7 +59,7 @@
|
||||
.tableChip {
|
||||
display: inline-block;
|
||||
font-size: 14px !important;
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border: 1px solid var(--canon-border);
|
||||
border-radius: 6px;
|
||||
padding: 0px 6px;
|
||||
height: 24px;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
.list {
|
||||
display: flex;
|
||||
gap: var(--canon-space-6);
|
||||
border-bottom: 1px solid var(--canon-border-base);
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
position: relative;
|
||||
margin-bottom: var(--canon-space-6);
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
.tabsTheme {
|
||||
width: 142px;
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
height: 60px;
|
||||
font-family: var(--docs-font);
|
||||
color: var(--canon-text-secondary);
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
cursor: pointer;
|
||||
transition: color 0.2s ease-in-out;
|
||||
@@ -53,7 +53,7 @@
|
||||
position: absolute;
|
||||
inset: 0.25rem 0;
|
||||
border-radius: 0.25rem;
|
||||
outline: 2px solid var(--canon-surface-1);
|
||||
outline: 2px solid var(--canon-bg-elevated);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 10;
|
||||
background-color: var(--canon-surface-1);
|
||||
border-bottom: 1px solid var(--canon-border-base);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
border-bottom: 1px solid var(--canon-border);
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
color: var(--color-gray-900);
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
transition: background-color 0.2s ease-in-out;
|
||||
|
||||
&:focus-visible {
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
|
||||
.SelectValue {
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
}
|
||||
|
||||
@@ -43,9 +43,9 @@
|
||||
box-sizing: border-box;
|
||||
padding-block: 0.25rem;
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--canon-surface-1);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
color: var(--color-gray-900);
|
||||
border: 1px solid var(--canon-border-base);
|
||||
border: 1px solid var(--canon-border);
|
||||
padding-inline: 0.25rem;
|
||||
transform-origin: var(--transform-origin);
|
||||
transition: transform 150ms, opacity 150ms;
|
||||
@@ -82,7 +82,7 @@
|
||||
padding-block: 0.5rem;
|
||||
padding-left: 0.625rem;
|
||||
padding-right: 1rem;
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
display: grid;
|
||||
gap: 0.5rem;
|
||||
@@ -104,7 +104,7 @@
|
||||
z-index: 0;
|
||||
position: relative;
|
||||
color: var(--color-gray-50);
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
}
|
||||
|
||||
&[data-highlighted]::before {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
.tabs {
|
||||
border-radius: 0.375rem;
|
||||
width: 100%;
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.tabsTheme {
|
||||
width: 100px;
|
||||
border-radius: 0.375rem;
|
||||
background-color: var(--canon-surface-2);
|
||||
background-color: var(--canon-bg);
|
||||
transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
position: absolute;
|
||||
inset: 0.25rem 0;
|
||||
border-radius: 0.25rem;
|
||||
outline: 2px solid var(--canon-surface-1);
|
||||
outline: 2px solid var(--canon-bg-elevated);
|
||||
outline-offset: -1px;
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@
|
||||
width: var(--active-tab-width);
|
||||
height: 1.5rem;
|
||||
border-radius: 0.25rem;
|
||||
background-color: var(--canon-surface-1);
|
||||
background-color: var(--canon-bg-elevated);
|
||||
transition-property: translate, width, background-color;
|
||||
transition-duration: 200ms;
|
||||
transition-timing-function: ease-in-out;
|
||||
|
||||
@@ -77,11 +77,11 @@ export function useMDXComponents(components: MDXComponents): MDXComponents {
|
||||
<code
|
||||
style={{
|
||||
fontFamily: 'var(--canon-font-monospace)',
|
||||
backgroundColor: 'var(--canon-surface-1)',
|
||||
backgroundColor: 'var(--canon-bg-elevated)',
|
||||
padding: '0.2rem 0.375rem',
|
||||
borderRadius: '0.25rem',
|
||||
color: 'var(--canon-text-secondary)',
|
||||
border: '1px solid var(--canon-border-base)',
|
||||
border: '1px solid var(--canon-border)',
|
||||
fontSize: '0.875rem',
|
||||
}}
|
||||
>
|
||||
|
||||
@@ -925,11 +925,11 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:^20":
|
||||
version: 20.17.12
|
||||
resolution: "@types/node@npm:20.17.12"
|
||||
version: 20.17.16
|
||||
resolution: "@types/node@npm:20.17.16"
|
||||
dependencies:
|
||||
undici-types: ~6.19.2
|
||||
checksum: 0c0dbeb4e1480a23071ec38e97bb3d776e0ae9828174bf45f9edcf277caa955945cb10d31d84f7a73c66aaf92ae2e022be6331bd00a4bed1f2ad9639a411d17e
|
||||
checksum: dd19c04c592b602f20448187b4b6ab0293f22173a8bbe53aa3b559188f078b5261d748a107b84ee0580e2074cda760049b13d6e210f9850039bead01cb666a3f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -5368,22 +5368,22 @@ __metadata:
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:^5":
|
||||
version: 5.7.2
|
||||
resolution: "typescript@npm:5.7.2"
|
||||
version: 5.7.3
|
||||
resolution: "typescript@npm:5.7.3"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: b55300c4cefee8ee380d14fa9359ccb41ff8b54c719f6bc49b424899d662a5ce62ece390ce769568c7f4d14af844085255e63788740084444eb12ef423b13433
|
||||
checksum: 6c38b1e989918e576f0307e6ee013522ea480dfce5f3ca85c9b2d8adb1edeffd37f4f30cd68de0c38a44563d12ba922bdb7e36aa2dac9c51de5d561e6e9a2e9c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@^5#~builtin<compat/typescript>":
|
||||
version: 5.7.2
|
||||
resolution: "typescript@patch:typescript@npm%3A5.7.2#~builtin<compat/typescript>::version=5.7.2&hash=5adc0c"
|
||||
version: 5.7.3
|
||||
resolution: "typescript@patch:typescript@npm%3A5.7.3#~builtin<compat/typescript>::version=5.7.3&hash=5adc0c"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: 803430c6da2ba73c25a21880d8d4f08a56d9d2444e6db2ea949ac4abceeece8e4a442b7b9b585db7d8a0b47ebda2060e45fe8ee8b8aca23e27ec1d4844987ee6
|
||||
checksum: 633cd749d6cd7bc842c6b6245847173bba99742a60776fae3c0fbcc0d1733cd51a733995e5f4dadd8afb0e64e57d3c7dbbeae953a072ee303940eca69e22f311
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ There are however some ways to get this to work without too much effort.
|
||||
yarn start
|
||||
```
|
||||
|
||||
The default for `global-agent` is to have a prefix on the variable names, hence the need for specifying it twice.
|
||||
The default for `global-agent` is to have a prefix on the variable names, hence the need for specifying it twice. For further information about `HTTP(S)_PROXY` and `NO_PROXY` excludes, see [the global-agent documentation](https://github.com/gajus/global-agent) and [undici documentation](https://github.com/nodejs/undici).
|
||||
|
||||
## Configuration
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
---
|
||||
id: auditor
|
||||
title: Auditor Service
|
||||
sidebar_label: Auditor
|
||||
description: Documentation for the Auditor service
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the Auditor Service, a software service designed to record and report on security-relevant events within an application. This service utilizes the `winston` library for logging and provides a flexible way to capture and format audit events.
|
||||
|
||||
## Key Features
|
||||
|
||||
- Provides a standardized way to capture security events.
|
||||
- Allows categorization of events by severity level.
|
||||
- Supports detailed metadata for each event.
|
||||
- Offers success/failure reporting for events.
|
||||
- Integrates with authentication and plugin services for enhanced context.
|
||||
- Uses `winston` for flexible log formatting and transport.
|
||||
- Provides a service factory for easy integration with Backstage plugins.
|
||||
- Supports configurable log transports (console, file).
|
||||
|
||||
## How it Works
|
||||
|
||||
The Auditor Service defines a core class, `Auditor`, which implements the `AuditorService` interface. This class uses `winston` to log audit events with varying levels of severity and associated metadata. It also integrates with authentication and plugin services to capture actor details and plugin context.
|
||||
|
||||
The `auditorServiceFactory` creates an `Auditor` instance for the root context and provides a factory function for creating child loggers for individual plugins. This allows each plugin to have its own logger with inherited and additional metadata.
|
||||
|
||||
## Usage Guidance
|
||||
|
||||
The Auditor Service is designed for recording security-relevant events that require special attention or are subject to compliance regulations. These events often involve actions like:
|
||||
|
||||
- User session management
|
||||
- Data access and modification
|
||||
- System configuration changes
|
||||
|
||||
For general application logging that is not security-critical, you should use the standard `LoggerService` provided by Backstage. This helps to keep your audit logs focused and relevant.
|
||||
|
||||
## Using the Service
|
||||
|
||||
The Auditor Service can be accessed via dependency injection in your Backstage plugin. Here's an example of how to access the service and create an audit event within an Express route handler:
|
||||
|
||||
```typescript
|
||||
export async function createRouter(
|
||||
options: RouterOptions,
|
||||
): Promise<express.Router> {
|
||||
const { auditor } = options;
|
||||
|
||||
const router = Router();
|
||||
router.use(express.json());
|
||||
|
||||
router.post('/my-endpoint', async (req, res) => {
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'my-endpoint-call',
|
||||
request: req,
|
||||
meta: {
|
||||
// ... metadata about the request
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
// ... process the request
|
||||
|
||||
await auditorEvent.success();
|
||||
res.status(200).json({ message: 'Succeeded!' });
|
||||
} catch (error) {
|
||||
await auditorEvent.fail({ error });
|
||||
res.status(500).json({ message: 'Failed!' });
|
||||
throw error;
|
||||
}
|
||||
});
|
||||
|
||||
return router;
|
||||
}
|
||||
```
|
||||
|
||||
In this example, an audit event is created for each request to `/my-endpoint`. The `success` or `fail` methods are called based on the outcome of processing the request.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
When defining `eventId` and `subEventId` for your audit events, follow these guidelines:
|
||||
|
||||
- Use kebab-case (e.g., `user-login`, `file-download`, `fetch`, `entity-create`, `entity-update`).
|
||||
- The `eventId` represents a logical group of similar events or operations. For example, "fetch" could be used as an `eventId` encompassing various fetch methods like `by-id` or `by-location`.
|
||||
- Use `subEventId` to further categorize events within a logical group. For example, if the `eventId` is "fetch", the `subEventId` could be "by-id" or "by-location" to specify the method used for fetching.
|
||||
- Avoid redundant prefixes related to the plugin ID, as that context is already provided.
|
||||
- Choose names that clearly and concisely describe the event being audited.
|
||||
@@ -36,7 +36,13 @@ additional encoding:
|
||||
The name is always required. Depending on the context, you may be able to leave
|
||||
out the kind and/or namespace. If you do, it is contextual what values will be
|
||||
used, and the relevant documentation should specify which rule applies where.
|
||||
All strings are case insensitive.
|
||||
|
||||
Entity ref strings are frequently passed between systems as identifiers of
|
||||
entities. In those cases the refs should always be complete (have all three
|
||||
parts). The sender should ensure that the refs are always lowercased in an
|
||||
`en-US` locale, preferably by using [the `stringifyEntityRef` function](https://backstage.io/docs/reference/catalog-model.stringifyentityref/)
|
||||
which does this automatically. The receiver should treat incoming refs case
|
||||
insensitively to avoid problems with senders who do not obey this rule.
|
||||
|
||||
```yaml
|
||||
# Example:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -515,6 +515,7 @@ The Backstage tooling supports [ECMAScript modules (ESM)](https://nodejs.org/doc
|
||||
|
||||
- To enable support for native ESM in tests, you need to run the tests with the `--experimental-vm-modules` flag enabled, typically via `NODE_OPTIONS='--experimental-vm-modules'`.
|
||||
- Declaring a package as `"type": "module"` in `package.json` is supported, but in tests it will cause all local transitive dependencies to also be treated as ESM, regardless of whether they declare `"type": "module"` or not.
|
||||
- When running tests with coverage enabled the default `babel` coverage provider can mess with the hoisting of named exports. This can be worked around by using the `v8` provider instead by setting `"coverageProvider": "v8"` in the Jest configuration, although note that the `v8` provider is a fair bit slower than the `babel` one.
|
||||
- Node.js has an [ESM interoperability layer with CommonJS](https://nodejs.org/docs/latest-v22.x/api/esm.html#interoperability-with-commonjs) that allows for imports from ESM to identify named exports in CommonJS packages. This interoperability layer is **only** enabled when importing packages with a `.cts` or `.cjs` extension. This is because the interoperability layer is not fully compatible with the NPM ecosystem, and would break package if it was enabled for `.js` files.
|
||||
- Dynamic imports of CommonJS packages will vary in shape depending on the runtime, i.e. test vs local development, etc. It is therefore recommended to avoid dynamic imports of CommonJS packages and instead use `require`, or to use the explicit CommonJS extensions as mentioned above. If you do need to dynamically import CommonJS packages, avoid using `default` exports, as the shape of them vary across different environments and you would otherwise need to manually unwrap the import based on the shape of the module object.
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ author: digital.ai
|
||||
authorUrl: https://digital.ai/
|
||||
category: CI/CD
|
||||
description: The plugin offers integration with Digital.ai Deploy and backstage components and services. It provide access to deployments and reports.
|
||||
documentation: https://docs.digital.ai/bundle/devops-deploy-version-v.24.1/page/deploy/concept/xl-deploy-backstage-overview.html
|
||||
documentation: https://docs.digital.ai/deploy/docs/concept/xl-deploy-backstage-overview
|
||||
iconUrl: /img/digital.ai-deploy.svg
|
||||
npmPackageName: '@digital.ai/plugin-dai-deploy'
|
||||
tags:
|
||||
|
||||
@@ -4,7 +4,7 @@ author: digital.ai
|
||||
authorUrl: https://digital.ai/
|
||||
category: CI/CD
|
||||
description: The plugin offers integration with Digital.ai Release and backstage. It provide access to active releases, templates and manage workflow executions.
|
||||
documentation: https://docs.digital.ai/bundle/devops-release-version-v.24.1/page/release/concept/release-backstage-overview.html
|
||||
documentation: https://docs.digital.ai/release/docs/concept/release-backstage-overview
|
||||
iconUrl: /img/digital.ai-release.svg
|
||||
npmPackageName: '@digital-ai/plugin-dai-release'
|
||||
tags:
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "root",
|
||||
"version": "1.35.0",
|
||||
"version": "1.36.0-next.0",
|
||||
"private": true,
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# app-next-example-plugin
|
||||
|
||||
## 0.0.20-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/frontend-plugin-api@0.9.5-next.0
|
||||
- @backstage/core-components@0.16.3
|
||||
|
||||
## 0.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "app-next-example-plugin",
|
||||
"version": "0.0.19",
|
||||
"version": "0.0.20-next.0",
|
||||
"description": "Backstage internal example plugin",
|
||||
"backstage": {
|
||||
"role": "frontend-plugin",
|
||||
|
||||
@@ -1,5 +1,50 @@
|
||||
# example-app-next
|
||||
|
||||
## 0.0.20-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/cli@0.30.0-next.0
|
||||
- @backstage/plugin-notifications@0.5.2-next.0
|
||||
- @backstage/plugin-search-react@1.8.6-next.0
|
||||
- @backstage/plugin-search@1.4.23-next.0
|
||||
- @backstage/plugin-techdocs@1.12.2-next.0
|
||||
- @backstage/plugin-scaffolder@1.27.5-next.0
|
||||
- @backstage/plugin-scaffolder-react@1.14.4-next.0
|
||||
- @backstage/frontend-plugin-api@0.9.5-next.0
|
||||
- @backstage/frontend-app-api@0.10.5-next.0
|
||||
- @backstage/app-defaults@1.5.16
|
||||
- @backstage/catalog-model@1.7.3
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/core-app-api@1.15.4
|
||||
- @backstage/core-compat-api@0.3.6-next.0
|
||||
- @backstage/core-components@0.16.3
|
||||
- @backstage/core-plugin-api@1.10.3
|
||||
- @backstage/frontend-defaults@0.1.6-next.0
|
||||
- @backstage/integration-react@1.2.3
|
||||
- @backstage/theme@0.6.3
|
||||
- @backstage/plugin-api-docs@0.12.4-next.0
|
||||
- @backstage/plugin-app@0.1.6-next.0
|
||||
- @backstage/plugin-app-visualizer@0.1.16-next.0
|
||||
- @backstage/plugin-auth-react@0.1.11
|
||||
- @backstage/plugin-catalog@1.26.2-next.0
|
||||
- @backstage/plugin-catalog-common@1.1.3
|
||||
- @backstage/plugin-catalog-graph@0.4.16-next.0
|
||||
- @backstage/plugin-catalog-import@0.12.10-next.0
|
||||
- @backstage/plugin-catalog-react@1.15.2-next.0
|
||||
- @backstage/plugin-catalog-unprocessed-entities@0.2.13
|
||||
- @backstage/plugin-home@0.8.5-next.0
|
||||
- @backstage/plugin-kubernetes@0.12.4-next.0
|
||||
- @backstage/plugin-kubernetes-cluster@0.0.22-next.0
|
||||
- @backstage/plugin-org@0.6.36-next.0
|
||||
- @backstage/plugin-permission-react@0.4.30
|
||||
- @backstage/plugin-search-common@1.2.17
|
||||
- @backstage/plugin-signals@0.0.15
|
||||
- @backstage/plugin-techdocs-module-addons-contrib@1.1.20
|
||||
- @backstage/plugin-techdocs-react@1.2.13
|
||||
- @backstage/plugin-user-settings@0.8.19-next.0
|
||||
|
||||
## 0.0.19
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-app-next",
|
||||
"version": "0.0.19",
|
||||
"version": "0.0.20-next.0",
|
||||
"backstage": {
|
||||
"role": "frontend"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,46 @@
|
||||
# example-app
|
||||
|
||||
## 0.2.106-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/cli@0.30.0-next.0
|
||||
- @backstage/plugin-notifications@0.5.2-next.0
|
||||
- @backstage/plugin-search-react@1.8.6-next.0
|
||||
- @backstage/plugin-search@1.4.23-next.0
|
||||
- @backstage/plugin-techdocs@1.12.2-next.0
|
||||
- @backstage/plugin-scaffolder@1.27.5-next.0
|
||||
- @backstage/plugin-scaffolder-react@1.14.4-next.0
|
||||
- @backstage/frontend-app-api@0.10.5-next.0
|
||||
- @backstage/app-defaults@1.5.16
|
||||
- @backstage/catalog-model@1.7.3
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/core-app-api@1.15.4
|
||||
- @backstage/core-components@0.16.3
|
||||
- @backstage/core-plugin-api@1.10.3
|
||||
- @backstage/integration-react@1.2.3
|
||||
- @backstage/theme@0.6.3
|
||||
- @backstage/plugin-api-docs@0.12.4-next.0
|
||||
- @backstage/plugin-auth-react@0.1.11
|
||||
- @backstage/plugin-catalog@1.26.2-next.0
|
||||
- @backstage/plugin-catalog-common@1.1.3
|
||||
- @backstage/plugin-catalog-graph@0.4.16-next.0
|
||||
- @backstage/plugin-catalog-import@0.12.10-next.0
|
||||
- @backstage/plugin-catalog-react@1.15.2-next.0
|
||||
- @backstage/plugin-catalog-unprocessed-entities@0.2.13
|
||||
- @backstage/plugin-devtools@0.1.24-next.0
|
||||
- @backstage/plugin-home@0.8.5-next.0
|
||||
- @backstage/plugin-kubernetes@0.12.4-next.0
|
||||
- @backstage/plugin-kubernetes-cluster@0.0.22-next.0
|
||||
- @backstage/plugin-org@0.6.36-next.0
|
||||
- @backstage/plugin-permission-react@0.4.30
|
||||
- @backstage/plugin-search-common@1.2.17
|
||||
- @backstage/plugin-signals@0.0.15
|
||||
- @backstage/plugin-techdocs-module-addons-contrib@1.1.20
|
||||
- @backstage/plugin-techdocs-react@1.2.13
|
||||
- @backstage/plugin-user-settings@0.8.19-next.0
|
||||
|
||||
## 0.2.105
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.2.105",
|
||||
"version": "0.2.106-next.0",
|
||||
"backstage": {
|
||||
"role": "frontend"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,19 @@
|
||||
# @backstage/backend-app-api
|
||||
|
||||
## 1.1.2-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-permission-node@0.8.8-next.0
|
||||
- @backstage/config-loader@1.9.6-next.0
|
||||
- @backstage/backend-plugin-api@1.2.0-next.0
|
||||
- @backstage/cli-common@0.1.15
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/errors@1.2.7
|
||||
- @backstage/types@1.2.1
|
||||
- @backstage/plugin-auth-node@0.5.7-next.0
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/backend-app-api",
|
||||
"version": "1.1.1",
|
||||
"version": "1.1.2-next.0",
|
||||
"description": "Core API used by Backstage backend apps",
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
|
||||
@@ -34,7 +34,7 @@ import type {
|
||||
} from '../../../backend-plugin-api/src/wiring/types';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import type { InternalServiceFactory } from '../../../backend-plugin-api/src/services/system/types';
|
||||
import { ForwardedError, ConflictError } from '@backstage/errors';
|
||||
import { ForwardedError, ConflictError, assertError } from '@backstage/errors';
|
||||
import {
|
||||
instanceMetadataServiceRef,
|
||||
featureDiscoveryServiceRef,
|
||||
@@ -405,8 +405,9 @@ export class BackendInitializer {
|
||||
// Once the plugin and all modules have been initialized, we can signal that the plugin has stared up successfully
|
||||
const lifecycleService = await this.#getPluginLifecycleImpl(pluginId);
|
||||
await lifecycleService.startup();
|
||||
} catch (error) {
|
||||
initLogger.onPluginFailed(pluginId);
|
||||
} catch (error: unknown) {
|
||||
assertError(error);
|
||||
initLogger.onPluginFailed(pluginId, error);
|
||||
throw error;
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -27,7 +27,7 @@ export function createInitializationLogger(
|
||||
rootLogger?: RootLoggerService,
|
||||
): {
|
||||
onPluginStarted(pluginId: string): void;
|
||||
onPluginFailed(pluginId: string): void;
|
||||
onPluginFailed(pluginId: string, error: Error): void;
|
||||
onAllStarted(): void;
|
||||
} {
|
||||
const logger = rootLogger?.child({ type: 'initialization' });
|
||||
@@ -68,14 +68,15 @@ export function createInitializationLogger(
|
||||
starting.delete(pluginId);
|
||||
started.add(pluginId);
|
||||
},
|
||||
onPluginFailed(pluginId: string) {
|
||||
onPluginFailed(pluginId: string, error: Error) {
|
||||
starting.delete(pluginId);
|
||||
const status =
|
||||
starting.size > 0
|
||||
? `, waiting for ${starting.size} other plugins to finish before shutting down the process`
|
||||
: '';
|
||||
logger?.error(
|
||||
`Plugin '${pluginId}' threw an error during startup${status}`,
|
||||
`Plugin '${pluginId}' threw an error during startup${status}.`,
|
||||
error,
|
||||
);
|
||||
},
|
||||
onAllStarted() {
|
||||
|
||||
@@ -1,5 +1,31 @@
|
||||
# @backstage/backend-defaults
|
||||
|
||||
## 0.8.0-next.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- a4aa244: This change introduces the `auditor` service implementation details.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f866b86: Internal refactor to use explicit `require` for lazy-loading dependency.
|
||||
- a19cb2b: Added default implementation for the new `PermissionsRegistryService`.
|
||||
- Updated dependencies
|
||||
- @backstage/cli-node@0.2.13-next.0
|
||||
- @backstage/plugin-permission-node@0.8.8-next.0
|
||||
- @backstage/config-loader@1.9.6-next.0
|
||||
- @backstage/backend-plugin-api@1.2.0-next.0
|
||||
- @backstage/backend-app-api@1.1.2-next.0
|
||||
- @backstage/backend-dev-utils@0.1.5
|
||||
- @backstage/cli-common@0.1.15
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/errors@1.2.7
|
||||
- @backstage/integration@1.16.1
|
||||
- @backstage/integration-aws-node@0.1.15
|
||||
- @backstage/types@1.2.1
|
||||
- @backstage/plugin-auth-node@0.5.7-next.0
|
||||
- @backstage/plugin-events-node@0.4.8-next.0
|
||||
|
||||
## 0.7.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/backend-defaults",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0-next.0",
|
||||
"description": "Backend defaults used by Backstage backend apps",
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
@@ -20,6 +20,7 @@
|
||||
"license": "Apache-2.0",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./auditor": "./src/entrypoints/auditor/index.ts",
|
||||
"./auth": "./src/entrypoints/auth/index.ts",
|
||||
"./cache": "./src/entrypoints/cache/index.ts",
|
||||
"./database": "./src/entrypoints/database/index.ts",
|
||||
@@ -44,6 +45,9 @@
|
||||
"types": "src/index.ts",
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"auditor": [
|
||||
"src/entrypoints/auditor/index.ts"
|
||||
],
|
||||
"auth": [
|
||||
"src/entrypoints/auth/index.ts"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
## API Report File for "@backstage/backend-defaults"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
import { AuditorService } from '@backstage/backend-plugin-api';
|
||||
import type { AuditorServiceCreateEventOptions } from '@backstage/backend-plugin-api';
|
||||
import type { AuditorServiceEvent } from '@backstage/backend-plugin-api';
|
||||
import type { AuditorServiceEventSeverityLevel } from '@backstage/backend-plugin-api';
|
||||
import type { AuthService } from '@backstage/backend-plugin-api';
|
||||
import type { Format } from 'logform';
|
||||
import type { HttpAuthService } from '@backstage/backend-plugin-api';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import type { PluginMetadataService } from '@backstage/backend-plugin-api';
|
||||
import type { Request as Request_2 } from 'express';
|
||||
import { ServiceFactory } from '@backstage/backend-plugin-api';
|
||||
import * as winston from 'winston';
|
||||
|
||||
// @public
|
||||
export type AuditorEvent = {
|
||||
plugin: string;
|
||||
eventId: string;
|
||||
severityLevel: AuditorServiceEventSeverityLevel;
|
||||
actor: AuditorEventActorDetails;
|
||||
meta?: JsonObject;
|
||||
request?: AuditorEventRequest;
|
||||
} & AuditorEventStatus;
|
||||
|
||||
// @public (undocumented)
|
||||
export type AuditorEventActorDetails = {
|
||||
actorId?: string;
|
||||
ip?: string;
|
||||
hostname?: string;
|
||||
userAgent?: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuditorEventOptions<TMeta extends JsonObject> = {
|
||||
eventId: string;
|
||||
severityLevel?: AuditorServiceEventSeverityLevel;
|
||||
request?: Request_2<any, any, any, any, any>;
|
||||
meta?: TMeta;
|
||||
} & AuditorEventStatus;
|
||||
|
||||
// @public (undocumented)
|
||||
export type AuditorEventRequest = {
|
||||
url: string;
|
||||
method: string;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type AuditorEventStatus =
|
||||
| {
|
||||
status: 'initiated';
|
||||
}
|
||||
| {
|
||||
status: 'succeeded';
|
||||
}
|
||||
| {
|
||||
status: 'failed';
|
||||
error: string;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuditorLogFunction = (event: AuditorEvent) => void | Promise<void>;
|
||||
|
||||
// @public
|
||||
export const auditorServiceFactory: ServiceFactory<
|
||||
AuditorService,
|
||||
'plugin',
|
||||
'singleton'
|
||||
>;
|
||||
|
||||
// @public
|
||||
export class DefaultAuditorService implements AuditorService {
|
||||
static create(
|
||||
logFn: AuditorLogFunction,
|
||||
deps: {
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
plugin: PluginMetadataService;
|
||||
},
|
||||
): DefaultAuditorService;
|
||||
// (undocumented)
|
||||
createEvent(
|
||||
options: AuditorServiceCreateEventOptions,
|
||||
): Promise<AuditorServiceEvent>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export class WinstonRootAuditorService {
|
||||
static create(
|
||||
options?: WinstonRootAuditorServiceOptions,
|
||||
): WinstonRootAuditorService;
|
||||
// (undocumented)
|
||||
forPlugin(deps: {
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
plugin: PluginMetadataService;
|
||||
}): AuditorService;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type WinstonRootAuditorServiceOptions = {
|
||||
meta?: JsonObject;
|
||||
format?: Format;
|
||||
transports?: winston.transport[];
|
||||
};
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
import { Backend, createSpecializedBackend } from '@backstage/backend-app-api';
|
||||
import { auditorServiceFactory } from '@backstage/backend-defaults/auditor';
|
||||
import { authServiceFactory } from '@backstage/backend-defaults/auth';
|
||||
import { cacheServiceFactory } from '@backstage/backend-defaults/cache';
|
||||
import { databaseServiceFactory } from '@backstage/backend-defaults/database';
|
||||
@@ -36,6 +37,7 @@ import { userInfoServiceFactory } from '@backstage/backend-defaults/userInfo';
|
||||
import { eventsServiceFactory } from '@backstage/plugin-events-node';
|
||||
|
||||
export const defaultServiceFactories = [
|
||||
auditorServiceFactory,
|
||||
authServiceFactory,
|
||||
cacheServiceFactory,
|
||||
rootConfigServiceFactory,
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
import { DefaultAuditorService } from './DefaultAuditorService';
|
||||
|
||||
const mockDeps = {
|
||||
auth: mockServices.auth.mock(),
|
||||
httpAuth: mockServices.httpAuth.mock(),
|
||||
plugin: {
|
||||
getId: () => 'test',
|
||||
},
|
||||
};
|
||||
|
||||
describe('DefaultAuditorService', () => {
|
||||
it('creates a auditor instance with default options', () => {
|
||||
const auditor = DefaultAuditorService.create(jest.fn(), mockDeps);
|
||||
expect(auditor).toBeInstanceOf(DefaultAuditorService);
|
||||
});
|
||||
|
||||
it('should log a status "initiated" using createEvent', async () => {
|
||||
const logFn = jest.fn();
|
||||
const auditor = DefaultAuditorService.create(logFn, mockDeps);
|
||||
|
||||
await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
});
|
||||
|
||||
expect(logFn).toHaveBeenCalledWith({
|
||||
eventId: 'test-event',
|
||||
status: 'initiated',
|
||||
plugin: 'test',
|
||||
severityLevel: 'low',
|
||||
actor: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log a status "succeeded" using createEvent', async () => {
|
||||
const logFn = jest.fn();
|
||||
const auditor = DefaultAuditorService.create(logFn, mockDeps);
|
||||
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
});
|
||||
|
||||
await auditorEvent.success();
|
||||
|
||||
expect(logFn).toHaveBeenCalledTimes(2);
|
||||
expect(logFn).toHaveBeenLastCalledWith({
|
||||
eventId: 'test-event',
|
||||
status: 'succeeded',
|
||||
plugin: 'test',
|
||||
severityLevel: 'low',
|
||||
actor: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should log a status "failed"', async () => {
|
||||
const logFn = jest.fn();
|
||||
const auditor = DefaultAuditorService.create(logFn, mockDeps);
|
||||
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
});
|
||||
|
||||
const error = new Error('error');
|
||||
await auditorEvent.fail({ error });
|
||||
|
||||
expect(logFn).toHaveBeenCalledTimes(2);
|
||||
expect(logFn).toHaveBeenLastCalledWith({
|
||||
eventId: 'test-event',
|
||||
status: 'failed',
|
||||
error: error.toString(),
|
||||
plugin: 'test',
|
||||
severityLevel: 'low',
|
||||
actor: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should use root meta', async () => {
|
||||
const logFn = jest.fn();
|
||||
const auditor = DefaultAuditorService.create(logFn, mockDeps);
|
||||
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
await auditorEvent.success({ meta: { succeeded: 'test' } });
|
||||
|
||||
const error = new Error('error');
|
||||
await auditorEvent.fail({ error, meta: { failed: 'test' } });
|
||||
|
||||
expect(logFn).toHaveBeenCalledTimes(3);
|
||||
expect(logFn).toHaveBeenNthCalledWith(1, {
|
||||
eventId: 'test-event',
|
||||
status: 'initiated',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
},
|
||||
plugin: 'test',
|
||||
severityLevel: 'low',
|
||||
actor: {},
|
||||
});
|
||||
expect(logFn).toHaveBeenNthCalledWith(2, {
|
||||
eventId: 'test-event',
|
||||
status: 'succeeded',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
succeeded: 'test',
|
||||
},
|
||||
plugin: 'test',
|
||||
severityLevel: 'low',
|
||||
actor: {},
|
||||
});
|
||||
expect(logFn).toHaveBeenNthCalledWith(3, {
|
||||
eventId: 'test-event',
|
||||
status: 'failed',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
failed: 'test',
|
||||
},
|
||||
error: error.toString(),
|
||||
plugin: 'test',
|
||||
severityLevel: 'low',
|
||||
actor: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type {
|
||||
AuditorService,
|
||||
AuditorServiceCreateEventOptions,
|
||||
AuditorServiceEvent,
|
||||
AuditorServiceEventSeverityLevel,
|
||||
AuthService,
|
||||
BackstageCredentials,
|
||||
HttpAuthService,
|
||||
PluginMetadataService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { ForwardedError } from '@backstage/errors';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import type { Request } from 'express';
|
||||
|
||||
/** @public */
|
||||
export type AuditorEventActorDetails = {
|
||||
actorId?: string;
|
||||
ip?: string;
|
||||
hostname?: string;
|
||||
userAgent?: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type AuditorEventRequest = {
|
||||
url: string;
|
||||
method: string;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type AuditorEventStatus =
|
||||
| { status: 'initiated' }
|
||||
| { status: 'succeeded' }
|
||||
| {
|
||||
status: 'failed';
|
||||
error: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Options for creating an auditor event.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuditorEventOptions<TMeta extends JsonObject> = {
|
||||
/**
|
||||
* Use kebab-case to name audit events (e.g., "user-login", "file-download").
|
||||
*
|
||||
* The `pluginId` already provides plugin/module context, so avoid redundant prefixes in the `eventId`.
|
||||
*/
|
||||
eventId: string;
|
||||
|
||||
severityLevel?: AuditorServiceEventSeverityLevel;
|
||||
|
||||
/** (Optional) The associated HTTP request, if applicable. */
|
||||
request?: Request<any, any, any, any, any>;
|
||||
|
||||
/** (Optional) Additional metadata relevant to the event, structured as a JSON object. */
|
||||
meta?: TMeta;
|
||||
} & AuditorEventStatus;
|
||||
|
||||
/**
|
||||
* Common fields of an audit event.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type AuditorEvent = {
|
||||
plugin: string;
|
||||
eventId: string;
|
||||
severityLevel: AuditorServiceEventSeverityLevel;
|
||||
actor: AuditorEventActorDetails;
|
||||
meta?: JsonObject;
|
||||
request?: AuditorEventRequest;
|
||||
} & AuditorEventStatus;
|
||||
|
||||
/**
|
||||
* Logging function used by the auditor.
|
||||
* @public
|
||||
*/
|
||||
export type AuditorLogFunction = (event: AuditorEvent) => void | Promise<void>;
|
||||
|
||||
/**
|
||||
* A {@link @backstage/backend-plugin-api#AuditorService} implementation that logs events using a provided callback.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* export const auditorServiceFactory = createServiceFactory({
|
||||
* service: coreServices.auditor,
|
||||
* deps: {
|
||||
* logger: coreServices.logger,
|
||||
* auth: coreServices.auth,
|
||||
* httpAuth: coreServices.httpAuth,
|
||||
* plugin: coreServices.pluginMetadata,
|
||||
* },
|
||||
* factory({ logger, plugin, auth, httpAuth }) {
|
||||
* const auditLogger = logger.child({ isAuditEvent: true });
|
||||
* return DefaultAuditorService.create(
|
||||
* event => auditLogger.info(`${event.plugin}.${event.eventId}`, event),
|
||||
* { plugin, auth, httpAuth },
|
||||
* );
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class DefaultAuditorService implements AuditorService {
|
||||
private readonly logFn: AuditorLogFunction;
|
||||
private readonly auth: AuthService;
|
||||
private readonly httpAuth: HttpAuthService;
|
||||
private readonly plugin: PluginMetadataService;
|
||||
|
||||
private constructor(
|
||||
logFn: AuditorLogFunction,
|
||||
deps: {
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
plugin: PluginMetadataService;
|
||||
},
|
||||
) {
|
||||
this.logFn = logFn;
|
||||
this.auth = deps.auth;
|
||||
this.httpAuth = deps.httpAuth;
|
||||
this.plugin = deps.plugin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link DefaultAuditorService} instance.
|
||||
*/
|
||||
static create(
|
||||
logFn: AuditorLogFunction,
|
||||
deps: {
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
plugin: PluginMetadataService;
|
||||
},
|
||||
): DefaultAuditorService {
|
||||
return new DefaultAuditorService(logFn, deps);
|
||||
}
|
||||
|
||||
private async log<TMeta extends JsonObject>(
|
||||
options: AuditorEventOptions<TMeta>,
|
||||
): Promise<void> {
|
||||
const { eventId, severityLevel = 'low', request, meta, ...rest } = options;
|
||||
|
||||
await this.logFn({
|
||||
plugin: this.plugin.getId(),
|
||||
eventId,
|
||||
severityLevel,
|
||||
actor: {
|
||||
actorId: await this.getActorId(request),
|
||||
ip: request?.ip,
|
||||
hostname: request?.hostname,
|
||||
userAgent: request?.get('user-agent'),
|
||||
},
|
||||
request: request
|
||||
? {
|
||||
url: request?.originalUrl,
|
||||
method: request?.method,
|
||||
}
|
||||
: undefined,
|
||||
meta: Object.keys(meta ?? {}).length === 0 ? undefined : meta,
|
||||
...rest,
|
||||
});
|
||||
}
|
||||
|
||||
async createEvent(
|
||||
options: AuditorServiceCreateEventOptions,
|
||||
): Promise<AuditorServiceEvent> {
|
||||
await this.log({ ...options, status: 'initiated' });
|
||||
|
||||
return {
|
||||
success: async params => {
|
||||
await this.log({
|
||||
...options,
|
||||
meta: { ...options.meta, ...params?.meta },
|
||||
status: 'succeeded',
|
||||
});
|
||||
},
|
||||
fail: async params => {
|
||||
await this.log({
|
||||
...options,
|
||||
...params,
|
||||
error: String(params.error),
|
||||
meta: { ...options.meta, ...params?.meta },
|
||||
status: 'failed',
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private async getActorId(
|
||||
request?: Request<any, any, any, any, any>,
|
||||
): Promise<string | undefined> {
|
||||
let credentials: BackstageCredentials =
|
||||
await this.auth.getOwnServiceCredentials();
|
||||
|
||||
if (request) {
|
||||
try {
|
||||
credentials = await this.httpAuth.credentials(request);
|
||||
} catch (error) {
|
||||
throw new ForwardedError('Could not resolve credentials', error);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.auth.isPrincipal(credentials, 'user')) {
|
||||
return credentials.principal.userEntityRef;
|
||||
}
|
||||
|
||||
if (this.auth.isPrincipal(credentials, 'service')) {
|
||||
return credentials.principal.subject;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { mockServices } from '@backstage/backend-test-utils';
|
||||
import { WinstonRootAuditorService } from './WinstonRootAuditorService';
|
||||
import { DefaultAuditorService } from './DefaultAuditorService';
|
||||
|
||||
describe('WinstonRootAuditorService', () => {
|
||||
it('creates a auditor instance with default options', () => {
|
||||
const auditor = WinstonRootAuditorService.create();
|
||||
expect(auditor).toBeInstanceOf(WinstonRootAuditorService);
|
||||
});
|
||||
|
||||
it('creates a child logger', () => {
|
||||
const auditor = WinstonRootAuditorService.create();
|
||||
const childLogger = auditor.forPlugin({
|
||||
auth: mockServices.auth.mock(),
|
||||
httpAuth: mockServices.httpAuth.mock(),
|
||||
plugin: {
|
||||
getId: () => 'test-plugin',
|
||||
},
|
||||
});
|
||||
expect(childLogger).toBeInstanceOf(DefaultAuditorService);
|
||||
});
|
||||
|
||||
it('should log a status "initiated" using createEvent', async () => {
|
||||
const pluginId = 'test-plugin';
|
||||
|
||||
const auditor = WinstonRootAuditorService.create().forPlugin({
|
||||
auth: mockServices.auth.mock(),
|
||||
httpAuth: mockServices.httpAuth.mock(),
|
||||
plugin: {
|
||||
getId: () => pluginId,
|
||||
},
|
||||
});
|
||||
// workaround to spy on private method
|
||||
const auditorSpy = jest.spyOn(auditor as any, 'log');
|
||||
|
||||
await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
});
|
||||
|
||||
expect(auditorSpy).toHaveBeenCalledWith({
|
||||
eventId: 'test-event',
|
||||
status: 'initiated',
|
||||
});
|
||||
});
|
||||
|
||||
it('should log a status "succeeded" using createEvent', async () => {
|
||||
const pluginId = 'test-plugin';
|
||||
|
||||
const auditor = WinstonRootAuditorService.create().forPlugin({
|
||||
auth: mockServices.auth.mock(),
|
||||
httpAuth: mockServices.httpAuth.mock(),
|
||||
plugin: {
|
||||
getId: () => pluginId,
|
||||
},
|
||||
});
|
||||
// workaround to spy on private method
|
||||
const auditorSpy = jest.spyOn(auditor as any, 'log');
|
||||
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
});
|
||||
|
||||
await auditorEvent.success();
|
||||
|
||||
expect(auditorSpy).toHaveBeenCalledTimes(2);
|
||||
expect(auditorSpy).toHaveBeenLastCalledWith({
|
||||
eventId: 'test-event',
|
||||
meta: {},
|
||||
status: 'succeeded',
|
||||
});
|
||||
});
|
||||
|
||||
it('should log a status "failed"', async () => {
|
||||
const pluginId = 'test-plugin';
|
||||
|
||||
const auditor = WinstonRootAuditorService.create().forPlugin({
|
||||
auth: mockServices.auth.mock(),
|
||||
httpAuth: mockServices.httpAuth.mock(),
|
||||
plugin: {
|
||||
getId: () => pluginId,
|
||||
},
|
||||
});
|
||||
// workaround to spy on private method
|
||||
const auditorSpy = jest.spyOn(auditor as any, 'log');
|
||||
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
});
|
||||
|
||||
const error = new Error('error');
|
||||
await auditorEvent.fail({ error });
|
||||
|
||||
expect(auditorSpy).toHaveBeenCalledTimes(2);
|
||||
expect(auditorSpy).toHaveBeenLastCalledWith({
|
||||
eventId: 'test-event',
|
||||
meta: {},
|
||||
status: 'failed',
|
||||
error: error.toString(),
|
||||
});
|
||||
});
|
||||
|
||||
it('should use root meta', async () => {
|
||||
const pluginId = 'test-plugin';
|
||||
|
||||
const auditor = WinstonRootAuditorService.create().forPlugin({
|
||||
auth: mockServices.auth.mock(),
|
||||
httpAuth: mockServices.httpAuth.mock(),
|
||||
plugin: {
|
||||
getId: () => pluginId,
|
||||
},
|
||||
});
|
||||
// workaround to spy on private method
|
||||
const auditorSpy = jest.spyOn(auditor as any, 'log');
|
||||
|
||||
const auditorEvent = await auditor.createEvent({
|
||||
eventId: 'test-event',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
},
|
||||
});
|
||||
|
||||
await auditorEvent.success({ meta: { succeeded: 'test' } });
|
||||
|
||||
const error = new Error('error');
|
||||
await auditorEvent.fail({ error, meta: { failed: 'test' } });
|
||||
|
||||
expect(auditorSpy).toHaveBeenCalledTimes(3);
|
||||
expect(auditorSpy).toHaveBeenNthCalledWith(1, {
|
||||
eventId: 'test-event',
|
||||
status: 'initiated',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
},
|
||||
});
|
||||
expect(auditorSpy).toHaveBeenNthCalledWith(2, {
|
||||
eventId: 'test-event',
|
||||
status: 'succeeded',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
succeeded: 'test',
|
||||
},
|
||||
});
|
||||
expect(auditorSpy).toHaveBeenNthCalledWith(3, {
|
||||
eventId: 'test-event',
|
||||
status: 'failed',
|
||||
meta: {
|
||||
initiated: 'test',
|
||||
failed: 'test',
|
||||
},
|
||||
error: error.toString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type {
|
||||
AuditorService,
|
||||
AuthService,
|
||||
HttpAuthService,
|
||||
PluginMetadataService,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import type { Format } from 'logform';
|
||||
import * as winston from 'winston';
|
||||
import { WinstonLogger } from '../rootLogger';
|
||||
import { DefaultAuditorService } from './DefaultAuditorService';
|
||||
|
||||
/** @public */
|
||||
export const defaultFormatter = winston.format.combine(
|
||||
winston.format.timestamp({
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
}),
|
||||
winston.format.errors({ stack: true }),
|
||||
winston.format.splat(),
|
||||
winston.format.json(),
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds `isAuditEvent` field
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const auditorFieldFormat = winston.format(info => {
|
||||
return { ...info, isAuditEvent: true };
|
||||
})();
|
||||
|
||||
/**
|
||||
* Options for creating a {@link WinstonRootAuditorService}.
|
||||
* @public
|
||||
*/
|
||||
export type WinstonRootAuditorServiceOptions = {
|
||||
meta?: JsonObject;
|
||||
format?: Format;
|
||||
transports?: winston.transport[];
|
||||
};
|
||||
|
||||
/**
|
||||
* An implementation of the {@link @backstage/backend-plugin-api#AuditorService} that logs events using a separate winston logger.
|
||||
*
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* export const auditorServiceFactory = createServiceFactory({
|
||||
* service: coreServices.auditor,
|
||||
* deps: {
|
||||
* auth: coreServices.auth,
|
||||
* httpAuth: coreServices.httpAuth,
|
||||
* plugin: coreServices.pluginMetadata,
|
||||
* },
|
||||
* createRootContext() {
|
||||
* return WinstonRootAuditorService.create();
|
||||
* },
|
||||
* factory({ plugin, auth, httpAuth }, root) {
|
||||
* return root.forPlugin({ plugin, auth, httpAuth });
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
export class WinstonRootAuditorService {
|
||||
private constructor(private readonly winstonLogger: WinstonLogger) {}
|
||||
|
||||
/**
|
||||
* Creates a {@link WinstonRootAuditorService} instance.
|
||||
*/
|
||||
static create(
|
||||
options?: WinstonRootAuditorServiceOptions,
|
||||
): WinstonRootAuditorService {
|
||||
let winstonLogger = WinstonLogger.create({
|
||||
meta: {
|
||||
service: 'backstage',
|
||||
},
|
||||
level: 'info',
|
||||
format: winston.format.combine(
|
||||
auditorFieldFormat,
|
||||
options?.format ?? defaultFormatter,
|
||||
),
|
||||
transports: options?.transports,
|
||||
});
|
||||
|
||||
if (options?.meta) {
|
||||
winstonLogger = winstonLogger.child(options.meta) as WinstonLogger;
|
||||
}
|
||||
|
||||
return new WinstonRootAuditorService(winstonLogger);
|
||||
}
|
||||
|
||||
forPlugin(deps: {
|
||||
auth: AuthService;
|
||||
httpAuth: HttpAuthService;
|
||||
plugin: PluginMetadataService;
|
||||
}): AuditorService {
|
||||
return DefaultAuditorService.create(
|
||||
e => this.winstonLogger.info(`${e.plugin}.${e.eventId}`, e),
|
||||
deps,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
coreServices,
|
||||
createServiceFactory,
|
||||
} from '@backstage/backend-plugin-api';
|
||||
import { DefaultAuditorService } from './DefaultAuditorService';
|
||||
|
||||
/**
|
||||
* Plugin-level auditing.
|
||||
*
|
||||
* See {@link @backstage/code-plugin-api#AuditorService}
|
||||
* and {@link https://backstage.io/docs/backend-system/core-services/auditor | the service docs}
|
||||
* for more information.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const auditorServiceFactory = createServiceFactory({
|
||||
service: coreServices.auditor,
|
||||
deps: {
|
||||
logger: coreServices.logger,
|
||||
auth: coreServices.auth,
|
||||
httpAuth: coreServices.httpAuth,
|
||||
plugin: coreServices.pluginMetadata,
|
||||
},
|
||||
factory({ logger, plugin, auth, httpAuth }) {
|
||||
const auditLogger = logger.child({ isAuditEvent: true });
|
||||
return DefaultAuditorService.create(
|
||||
event => auditLogger.info(`${event.plugin}.${event.eventId}`, event),
|
||||
{ plugin, auth, httpAuth },
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export type {
|
||||
AuditorEvent,
|
||||
AuditorEventActorDetails,
|
||||
AuditorEventOptions,
|
||||
AuditorEventRequest,
|
||||
AuditorEventStatus,
|
||||
AuditorLogFunction,
|
||||
} from './DefaultAuditorService';
|
||||
export { DefaultAuditorService } from './DefaultAuditorService';
|
||||
export { auditorServiceFactory } from './auditorServiceFactory';
|
||||
export { WinstonRootAuditorService } from './WinstonRootAuditorService';
|
||||
export type { WinstonRootAuditorServiceOptions } from './WinstonRootAuditorService';
|
||||
@@ -1,5 +1,31 @@
|
||||
# @backstage/backend-dynamic-feature-service
|
||||
|
||||
## 0.5.4-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 96c20cd: Make sure changes are successfully tracked before starting up scanner.
|
||||
- Updated dependencies
|
||||
- @backstage/cli-node@0.2.13-next.0
|
||||
- @backstage/plugin-permission-node@0.8.8-next.0
|
||||
- @backstage/backend-defaults@0.8.0-next.0
|
||||
- @backstage/config-loader@1.9.6-next.0
|
||||
- @backstage/plugin-catalog-backend@1.31.0-next.0
|
||||
- @backstage/backend-plugin-api@1.2.0-next.0
|
||||
- @backstage/plugin-scaffolder-node@0.7.0-next.0
|
||||
- @backstage/backend-app-api@1.1.2-next.0
|
||||
- @backstage/cli-common@0.1.15
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/errors@1.2.7
|
||||
- @backstage/types@1.2.1
|
||||
- @backstage/plugin-app-node@0.1.30-next.0
|
||||
- @backstage/plugin-auth-node@0.5.7-next.0
|
||||
- @backstage/plugin-events-backend@0.4.2-next.0
|
||||
- @backstage/plugin-events-node@0.4.8-next.0
|
||||
- @backstage/plugin-permission-common@0.8.4
|
||||
- @backstage/plugin-search-backend-node@1.3.8-next.0
|
||||
- @backstage/plugin-search-common@1.2.17
|
||||
|
||||
## 0.5.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/backend-dynamic-feature-service",
|
||||
"version": "0.5.3",
|
||||
"version": "0.5.4-next.0",
|
||||
"description": "Backstage dynamic feature service",
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
|
||||
@@ -1,5 +1,45 @@
|
||||
# example-backend-legacy
|
||||
|
||||
## 0.2.107-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-permission-node@0.8.8-next.0
|
||||
- @backstage/backend-defaults@0.8.0-next.0
|
||||
- @backstage/plugin-catalog-backend@1.31.0-next.0
|
||||
- @backstage/plugin-kubernetes-backend@0.19.3-next.0
|
||||
- @backstage/plugin-catalog-backend-module-unprocessed@0.5.5-next.0
|
||||
- @backstage/plugin-catalog-node@1.15.2-next.0
|
||||
- @backstage/backend-plugin-api@1.2.0-next.0
|
||||
- @backstage/plugin-scaffolder-backend@1.30.0-next.0
|
||||
- @backstage/plugin-search-backend-module-catalog@0.3.1-next.0
|
||||
- @backstage/plugin-permission-backend@0.5.54-next.0
|
||||
- @backstage/plugin-search-backend@1.8.2-next.0
|
||||
- @backstage/catalog-client@1.9.1
|
||||
- @backstage/catalog-model@1.7.3
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/integration@1.16.1
|
||||
- @backstage/plugin-app-backend@0.4.5-next.0
|
||||
- @backstage/plugin-auth-backend@0.24.3-next.0
|
||||
- @backstage/plugin-auth-node@0.5.7-next.0
|
||||
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.0
|
||||
- @backstage/plugin-events-backend@0.4.2-next.0
|
||||
- @backstage/plugin-events-node@0.4.8-next.0
|
||||
- @backstage/plugin-permission-common@0.8.4
|
||||
- @backstage/plugin-proxy-backend@0.5.11-next.0
|
||||
- @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.3.6-next.0
|
||||
- @backstage/plugin-scaffolder-backend-module-gitlab@0.7.2-next.0
|
||||
- @backstage/plugin-scaffolder-backend-module-rails@0.5.6-next.0
|
||||
- @backstage/plugin-search-backend-module-elasticsearch@1.6.5-next.0
|
||||
- @backstage/plugin-search-backend-module-explore@0.2.8-next.0
|
||||
- @backstage/plugin-search-backend-module-pg@0.5.41-next.0
|
||||
- @backstage/plugin-search-backend-module-techdocs@0.3.6-next.0
|
||||
- @backstage/plugin-search-backend-node@1.3.8-next.0
|
||||
- @backstage/plugin-signals-backend@0.3.1-next.0
|
||||
- @backstage/plugin-signals-node@0.1.17-next.0
|
||||
- @backstage/plugin-techdocs-backend@1.11.6-next.0
|
||||
|
||||
## 0.2.106
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend-legacy",
|
||||
"version": "0.2.106",
|
||||
"version": "0.2.107-next.0",
|
||||
"backstage": {
|
||||
"role": "backend"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/backend-openapi-utils
|
||||
|
||||
## 0.4.2-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/backend-plugin-api@1.2.0-next.0
|
||||
- @backstage/errors@1.2.7
|
||||
- @backstage/types@1.2.1
|
||||
|
||||
## 0.4.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/backend-openapi-utils",
|
||||
"version": "0.4.1",
|
||||
"version": "0.4.2-next.0",
|
||||
"description": "OpenAPI typescript support.",
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# @backstage/backend-plugin-api
|
||||
|
||||
## 1.2.0-next.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- a4aa244: This change introduces the `auditor` service definition.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 9ddfd94: Added new `PermissionsRegistryService` that is used by plugins to register permissions, resource types, and rules into the permission system. This replaces the existing `createPermissionIntegrationRouter` from `@backstage/plugin-permission-node`.
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-permission-node@0.8.8-next.0
|
||||
- @backstage/cli-common@0.1.15
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/errors@1.2.7
|
||||
- @backstage/types@1.2.1
|
||||
- @backstage/plugin-auth-node@0.5.7-next.0
|
||||
- @backstage/plugin-permission-common@0.8.4
|
||||
|
||||
## 1.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/backend-plugin-api",
|
||||
"version": "1.1.1",
|
||||
"version": "1.2.0-next.0",
|
||||
"description": "Core API used by Backstage backend plugins",
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
|
||||
@@ -26,6 +26,35 @@ import { Readable } from 'stream';
|
||||
import type { Request as Request_2 } from 'express';
|
||||
import type { Response as Response_2 } from 'express';
|
||||
|
||||
// @public
|
||||
export interface AuditorService {
|
||||
// (undocumented)
|
||||
createEvent(
|
||||
options: AuditorServiceCreateEventOptions,
|
||||
): Promise<AuditorServiceEvent>;
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type AuditorServiceCreateEventOptions = {
|
||||
eventId: string;
|
||||
severityLevel?: AuditorServiceEventSeverityLevel;
|
||||
request?: Request_2<any, any, any, any, any>;
|
||||
meta?: JsonObject;
|
||||
};
|
||||
|
||||
// @public (undocumented)
|
||||
export type AuditorServiceEvent = {
|
||||
success(options?: { meta?: JsonObject }): Promise<void>;
|
||||
fail(options: { meta?: JsonObject; error: Error }): Promise<void>;
|
||||
};
|
||||
|
||||
// @public
|
||||
export type AuditorServiceEventSeverityLevel =
|
||||
| 'low'
|
||||
| 'medium'
|
||||
| 'high'
|
||||
| 'critical';
|
||||
|
||||
// @public
|
||||
export interface AuthService {
|
||||
authenticate(
|
||||
@@ -185,6 +214,7 @@ export namespace coreServices {
|
||||
const httpRouter: ServiceRef<HttpRouterService, 'plugin', 'singleton'>;
|
||||
const lifecycle: ServiceRef<LifecycleService, 'plugin', 'singleton'>;
|
||||
const logger: ServiceRef<LoggerService, 'plugin', 'singleton'>;
|
||||
const auditor: ServiceRef<AuditorService, 'plugin', 'singleton'>;
|
||||
const permissions: ServiceRef<PermissionsService, 'plugin', 'singleton'>;
|
||||
const permissionsRegistry: ServiceRef<
|
||||
PermissionsRegistryService,
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2024 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import type { JsonObject } from '@backstage/types';
|
||||
import type { Request } from 'express';
|
||||
|
||||
/**
|
||||
* low (default): normal usage
|
||||
* medium: accessing write endpoints
|
||||
* high: non-root permission changes
|
||||
* critical: root permission changes
|
||||
* @public
|
||||
*/
|
||||
export type AuditorServiceEventSeverityLevel =
|
||||
| 'low'
|
||||
| 'medium'
|
||||
| 'high'
|
||||
| 'critical';
|
||||
|
||||
/** @public */
|
||||
export type AuditorServiceCreateEventOptions = {
|
||||
/**
|
||||
* Use kebab-case to name audit events (e.g., "user-login", "file-download", "fetch"). Represents a logical group of similar events or operations. For example, "fetch" could be used as an eventId encompassing various fetch methods like "by-id" or "by-location".
|
||||
*
|
||||
* The `pluginId` already provides plugin/module context, so avoid redundant prefixes in the `eventId`.
|
||||
*/
|
||||
eventId: string;
|
||||
|
||||
/** (Optional) The severity level for the audit event. */
|
||||
severityLevel?: AuditorServiceEventSeverityLevel;
|
||||
|
||||
/** (Optional) The associated HTTP request, if applicable. */
|
||||
request?: Request<any, any, any, any, any>;
|
||||
|
||||
/**
|
||||
* (Optional) Additional metadata relevant to the event, structured as a JSON object.
|
||||
* This could include a `queryType` field, using kebab-case, for variations within the main event (e.g., "by-id", "by-user").
|
||||
* For example, if the `eventId` is "fetch", the `queryType` in `meta` could be "by-id" or "by-location".
|
||||
*/
|
||||
meta?: JsonObject;
|
||||
};
|
||||
|
||||
/** @public */
|
||||
export type AuditorServiceEvent = {
|
||||
success(options?: { meta?: JsonObject }): Promise<void>;
|
||||
fail(options: { meta?: JsonObject; error: Error }): Promise<void>;
|
||||
};
|
||||
|
||||
/**
|
||||
* A service that provides an auditor facility.
|
||||
*
|
||||
* See the {@link https://backstage.io/docs/backend-system/core-services/auditor | service documentation} for more details.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export interface AuditorService {
|
||||
createEvent(
|
||||
options: AuditorServiceCreateEventOptions,
|
||||
): Promise<AuditorServiceEvent>;
|
||||
}
|
||||
@@ -161,6 +161,19 @@ export namespace coreServices {
|
||||
import('./LoggerService').LoggerService
|
||||
>({ id: 'core.logger' });
|
||||
|
||||
/**
|
||||
* Plugin-level auditing.
|
||||
*
|
||||
* See {@link AuditorService}
|
||||
* and {@link https://backstage.io/docs/backend-system/core-services/auditor | the service docs}
|
||||
* for more information.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const auditor = createServiceRef<
|
||||
import('./AuditorService').AuditorService
|
||||
>({ id: 'core.auditor' });
|
||||
|
||||
/**
|
||||
* Permission system integration for authorization of user actions.
|
||||
*
|
||||
|
||||
@@ -14,36 +14,39 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { coreServices } from './coreServices';
|
||||
export type {
|
||||
AuditorService,
|
||||
AuditorServiceCreateEventOptions,
|
||||
AuditorServiceEvent,
|
||||
AuditorServiceEventSeverityLevel,
|
||||
} from './AuditorService';
|
||||
export type {
|
||||
AuthService,
|
||||
BackstageCredentials,
|
||||
BackstageUserPrincipal,
|
||||
BackstageServicePrincipal,
|
||||
BackstageNonePrincipal,
|
||||
BackstagePrincipalAccessRestrictions,
|
||||
BackstagePrincipalTypes,
|
||||
BackstageNonePrincipal,
|
||||
BackstageServicePrincipal,
|
||||
BackstageUserPrincipal,
|
||||
} from './AuthService';
|
||||
export type {
|
||||
CacheService,
|
||||
CacheServiceOptions,
|
||||
CacheServiceSetOptions,
|
||||
} from './CacheService';
|
||||
export type { RootConfigService } from './RootConfigService';
|
||||
export type { DatabaseService } from './DatabaseService';
|
||||
export type { DiscoveryService } from './DiscoveryService';
|
||||
export type { RootHealthService } from './RootHealthService';
|
||||
export type { HttpAuthService } from './HttpAuthService';
|
||||
export type {
|
||||
HttpRouterService,
|
||||
HttpRouterServiceAuthPolicy,
|
||||
} from './HttpRouterService';
|
||||
export type { HttpAuthService } from './HttpAuthService';
|
||||
export type {
|
||||
LifecycleService,
|
||||
LifecycleServiceStartupHook,
|
||||
LifecycleServiceStartupOptions,
|
||||
LifecycleServiceShutdownHook,
|
||||
LifecycleServiceShutdownOptions,
|
||||
LifecycleServiceStartupHook,
|
||||
LifecycleServiceStartupOptions,
|
||||
} from './LifecycleService';
|
||||
export type { LoggerService } from './LoggerService';
|
||||
export type {
|
||||
@@ -55,6 +58,8 @@ export type {
|
||||
PermissionsRegistryServiceAddResourceTypeOptions,
|
||||
} from './PermissionsRegistryService';
|
||||
export type { PluginMetadataService } from './PluginMetadataService';
|
||||
export type { RootConfigService } from './RootConfigService';
|
||||
export type { RootHealthService } from './RootHealthService';
|
||||
export type { RootHttpRouterService } from './RootHttpRouterService';
|
||||
export type { RootLifecycleService } from './RootLifecycleService';
|
||||
export type { RootLoggerService } from './RootLoggerService';
|
||||
@@ -69,15 +74,16 @@ export type {
|
||||
SchedulerServiceTaskScheduleDefinitionConfig,
|
||||
} from './SchedulerService';
|
||||
export type {
|
||||
UrlReaderService,
|
||||
UrlReaderServiceReadTreeOptions,
|
||||
UrlReaderServiceReadTreeResponse,
|
||||
UrlReaderServiceReadTreeResponseDirOptions,
|
||||
UrlReaderServiceReadTreeResponseFile,
|
||||
UrlReaderServiceReadUrlResponse,
|
||||
UrlReaderServiceReadUrlOptions,
|
||||
UrlReaderServiceReadUrlResponse,
|
||||
UrlReaderServiceSearchOptions,
|
||||
UrlReaderServiceSearchResponse,
|
||||
UrlReaderServiceSearchResponseFile,
|
||||
UrlReaderService,
|
||||
} from './UrlReaderService';
|
||||
export type { BackstageUserInfo, UserInfoService } from './UserInfoService';
|
||||
export { coreServices } from './coreServices';
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
# @backstage/backend-test-utils
|
||||
|
||||
## 1.3.0-next.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- dd05a97: Added mocks for the new `PermissionsRegistryService`.
|
||||
- a4aa244: This change introduces mocks for the `auditor` service.
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- f866b86: Internal refactor to use explicit `require` for lazy-loading dependency.
|
||||
- fb051f2: Sync feature installation compatibility logic with `@backstage/backend-app-api`.
|
||||
- Updated dependencies
|
||||
- @backstage/backend-defaults@0.8.0-next.0
|
||||
- @backstage/backend-plugin-api@1.2.0-next.0
|
||||
- @backstage/backend-app-api@1.1.2-next.0
|
||||
- @backstage/config@1.3.2
|
||||
- @backstage/errors@1.2.7
|
||||
- @backstage/types@1.2.1
|
||||
- @backstage/plugin-auth-node@0.5.7-next.0
|
||||
- @backstage/plugin-events-node@0.4.8-next.0
|
||||
|
||||
## 1.2.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/backend-test-utils",
|
||||
"version": "1.2.1",
|
||||
"version": "1.3.0-next.0",
|
||||
"description": "Test helpers library for Backstage backends",
|
||||
"backstage": {
|
||||
"role": "node-library"
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
/// <reference types="node" />
|
||||
/// <reference types="qs" />
|
||||
|
||||
import { AuditorService } from '@backstage/backend-plugin-api';
|
||||
import { AuthService } from '@backstage/backend-plugin-api';
|
||||
import { Backend } from '@backstage/backend-app-api';
|
||||
import { BackendFeature } from '@backstage/backend-plugin-api';
|
||||
@@ -153,6 +154,15 @@ export function mockErrorHandler(): ErrorRequestHandler<
|
||||
|
||||
// @public
|
||||
export namespace mockServices {
|
||||
// (undocumented)
|
||||
export namespace auditor {
|
||||
const // (undocumented)
|
||||
factory: () => ServiceFactory<AuditorService, 'plugin', 'singleton'>;
|
||||
const // (undocumented)
|
||||
mock: (
|
||||
partialImpl?: Partial<AuditorService> | undefined,
|
||||
) => ServiceMock<AuditorService>;
|
||||
}
|
||||
// (undocumented)
|
||||
export function auth(options?: {
|
||||
pluginId?: string;
|
||||
|
||||
@@ -21,6 +21,7 @@ import { httpRouterServiceFactory } from '@backstage/backend-defaults/httpRouter
|
||||
import { lifecycleServiceFactory } from '@backstage/backend-defaults/lifecycle';
|
||||
import { loggerServiceFactory } from '@backstage/backend-defaults/logger';
|
||||
import { permissionsServiceFactory } from '@backstage/backend-defaults/permissions';
|
||||
import { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';
|
||||
import { rootHealthServiceFactory } from '@backstage/backend-defaults/rootHealth';
|
||||
import { rootHttpRouterServiceFactory } from '@backstage/backend-defaults/rootHttpRouter';
|
||||
import { rootLifecycleServiceFactory } from '@backstage/backend-defaults/rootLifecycle';
|
||||
@@ -47,13 +48,13 @@ import {
|
||||
eventsServiceRef,
|
||||
} from '@backstage/plugin-events-node';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import { Knex } from 'knex';
|
||||
import { MockAuthService } from './MockAuthService';
|
||||
import { MockHttpAuthService } from './MockHttpAuthService';
|
||||
import { MockRootLoggerService } from './MockRootLoggerService';
|
||||
import { MockUserInfoService } from './MockUserInfoService';
|
||||
import { mockCredentials } from './mockCredentials';
|
||||
import { Knex } from 'knex';
|
||||
import { permissionsRegistryServiceFactory } from '@backstage/backend-defaults/permissionsRegistry';
|
||||
import { auditorServiceFactory } from '@backstage/backend-defaults/auditor';
|
||||
|
||||
/** @internal */
|
||||
function createLoggerMock() {
|
||||
@@ -221,6 +222,19 @@ export namespace mockServices {
|
||||
}));
|
||||
}
|
||||
|
||||
export namespace auditor {
|
||||
export const factory = () => auditorServiceFactory;
|
||||
|
||||
export const mock = simpleMock(coreServices.auditor, () => ({
|
||||
createEvent: jest.fn(async _ => {
|
||||
return {
|
||||
success: jest.fn(),
|
||||
fail: jest.fn(),
|
||||
};
|
||||
}),
|
||||
}));
|
||||
}
|
||||
|
||||
export function auth(options?: {
|
||||
pluginId?: string;
|
||||
disableDefaultAuthPolicy?: boolean;
|
||||
|
||||
@@ -67,6 +67,7 @@ export interface TestBackend extends Backend {
|
||||
|
||||
export const defaultServiceFactories = [
|
||||
mockServices.auth.factory(),
|
||||
mockServices.auditor.factory(),
|
||||
mockServices.cache.factory(),
|
||||
mockServices.rootConfig.factory(),
|
||||
mockServices.database.factory(),
|
||||
|
||||
@@ -1,5 +1,43 @@
|
||||
# example-backend
|
||||
|
||||
## 0.0.35-next.0
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-permission-node@0.8.8-next.0
|
||||
- @backstage/plugin-notifications-backend@0.5.2-next.0
|
||||
- @backstage/backend-defaults@0.8.0-next.0
|
||||
- @backstage/plugin-catalog-backend@1.31.0-next.0
|
||||
- @backstage/plugin-kubernetes-backend@0.19.3-next.0
|
||||
- @backstage/plugin-catalog-backend-module-unprocessed@0.5.5-next.0
|
||||
- @backstage/backend-plugin-api@1.2.0-next.0
|
||||
- @backstage/plugin-scaffolder-backend@1.30.0-next.0
|
||||
- @backstage/plugin-catalog-backend-module-openapi@0.2.7-next.0
|
||||
- @backstage/plugin-search-backend-module-catalog@0.3.1-next.0
|
||||
- @backstage/plugin-devtools-backend@0.5.2-next.0
|
||||
- @backstage/plugin-permission-backend@0.5.54-next.0
|
||||
- @backstage/plugin-permission-backend-module-allow-all-policy@0.2.5-next.0
|
||||
- @backstage/plugin-search-backend@1.8.2-next.0
|
||||
- @backstage/catalog-model@1.7.3
|
||||
- @backstage/plugin-app-backend@0.4.5-next.0
|
||||
- @backstage/plugin-auth-backend@0.24.3-next.0
|
||||
- @backstage/plugin-auth-backend-module-github-provider@0.2.5-next.0
|
||||
- @backstage/plugin-auth-backend-module-guest-provider@0.2.5-next.0
|
||||
- @backstage/plugin-auth-node@0.5.7-next.0
|
||||
- @backstage/plugin-catalog-backend-module-backstage-openapi@0.4.5-next.0
|
||||
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.5-next.0
|
||||
- @backstage/plugin-events-backend@0.4.2-next.0
|
||||
- @backstage/plugin-permission-common@0.8.4
|
||||
- @backstage/plugin-proxy-backend@0.5.11-next.0
|
||||
- @backstage/plugin-scaffolder-backend-module-github@0.5.6-next.0
|
||||
- @backstage/plugin-scaffolder-backend-module-notifications@0.1.7-next.0
|
||||
- @backstage/plugin-search-backend-module-explore@0.2.8-next.0
|
||||
- @backstage/plugin-search-backend-module-techdocs@0.3.6-next.0
|
||||
- @backstage/plugin-search-backend-node@1.3.8-next.0
|
||||
- @backstage/plugin-signals-backend@0.3.1-next.0
|
||||
- @backstage/plugin-techdocs-backend@1.11.6-next.0
|
||||
|
||||
## 0.0.34
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.0.34",
|
||||
"version": "0.0.35-next.0",
|
||||
"backstage": {
|
||||
"role": "backend"
|
||||
},
|
||||
|
||||
@@ -29,43 +29,43 @@ const preview: Preview = {
|
||||
},
|
||||
viewport: {
|
||||
viewports: {
|
||||
xs: {
|
||||
name: 'XSmall',
|
||||
initial: {
|
||||
name: 'Initial',
|
||||
styles: {
|
||||
width: '320px',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
small: {
|
||||
name: 'Small',
|
||||
xs: {
|
||||
name: 'Extra Small',
|
||||
styles: {
|
||||
width: '640px',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
medium: {
|
||||
name: 'Medium',
|
||||
sm: {
|
||||
name: 'Small',
|
||||
styles: {
|
||||
width: '768px',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
large: {
|
||||
name: 'Large',
|
||||
md: {
|
||||
name: 'Medium',
|
||||
styles: {
|
||||
width: '1024px',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
xlarge: {
|
||||
name: 'XLarge',
|
||||
lg: {
|
||||
name: 'Large',
|
||||
styles: {
|
||||
width: '1280px',
|
||||
height: '100%',
|
||||
},
|
||||
},
|
||||
'2xl': {
|
||||
name: '2XL',
|
||||
xl: {
|
||||
name: 'Extra Large',
|
||||
styles: {
|
||||
width: '1536px',
|
||||
height: '100%',
|
||||
@@ -85,12 +85,11 @@ const preview: Preview = {
|
||||
defaultTheme: 'Light',
|
||||
}),
|
||||
Story => {
|
||||
document.body.style.backgroundColor = 'var(--canon-background)';
|
||||
document.body.style.backgroundColor = 'var(--canon-bg)';
|
||||
|
||||
const docsStoryElements = document.getElementsByClassName('docs-story');
|
||||
Array.from(docsStoryElements).forEach(element => {
|
||||
(element as HTMLElement).style.backgroundColor =
|
||||
'var(--canon-background)';
|
||||
(element as HTMLElement).style.backgroundColor = 'var(--canon-bg)';
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -54,35 +54,35 @@
|
||||
|
||||
[data-theme-name='legacy'][data-theme='light'] {
|
||||
/* Colors */
|
||||
--canon-accent: #2e77d0;
|
||||
--canon-background: #f8f8f8;
|
||||
--canon-surface-1: #fff;
|
||||
--canon-surface-2: #f4f4f4;
|
||||
--canon-bg-accent: #2e77d0;
|
||||
--canon-bg: #f8f8f8;
|
||||
--canon-bg-elevated: #fff;
|
||||
--canon-bg: #f4f4f4;
|
||||
|
||||
/* Text colors */
|
||||
--canon-text-primary: #000;
|
||||
--canon-text-primary-on-accent: #fff;
|
||||
--canon-fg-accent: #fff;
|
||||
--canon-text-secondary: #646464;
|
||||
}
|
||||
|
||||
[data-theme-name='legacy'][data-theme='dark'] {
|
||||
/* Colors */
|
||||
--canon-accent: #fff;
|
||||
--canon-background: #000;
|
||||
--canon-surface-1: #121212;
|
||||
--canon-surface-2: #1a1a1a;
|
||||
--canon-bg-accent: #fff;
|
||||
--canon-bg: #000;
|
||||
--canon-bg-elevated: #121212;
|
||||
--canon-bg: #1a1a1a;
|
||||
|
||||
/* Borders */
|
||||
--canon-border-base: rgba(255, 255, 255, 0.2);
|
||||
--canon-border: rgba(255, 255, 255, 0.2);
|
||||
--canon-border-warning: #f50000;
|
||||
--canon-border-error: #f87503;
|
||||
--canon-border-selected: #25d262;
|
||||
--canon-border-danger: #f87503;
|
||||
--canon-border-focus: #25d262;
|
||||
|
||||
/* States - Add more states */
|
||||
--canon-error: #f50000;
|
||||
--canon-fg-danger: #f50000;
|
||||
|
||||
/* Text colors */
|
||||
--canon-text-primary: #fff;
|
||||
--canon-text-primary-on-accent: #000;
|
||||
--canon-fg-accent: #000;
|
||||
--canon-text-secondary: #b3b3b3;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# @backstage/canon
|
||||
|
||||
## 0.1.0-next.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 65f4acc: This is the first alpha release for Canon. As part of this release we are introducing 5 layout components and 7 components. All theming is done through CSS variables.
|
||||
@@ -1,6 +1,6 @@
|
||||
# @backstage/canon
|
||||
|
||||
_This package was created through the Backstage CLI_.
|
||||
Canon is a UI component library for Backstage.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -10,3 +10,9 @@ Install the package via Yarn:
|
||||
cd <package-dir> # if within a monorepo
|
||||
yarn add @backstage/canon
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
- [Canon Documentation](https://canon.backstage.io)
|
||||
- [Backstage Readme](https://github.com/backstage/backstage/blob/master/README.md)
|
||||
- [Backstage Documentation](https://backstage.io/docs)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/canon",
|
||||
"version": "0.0.0",
|
||||
"version": "0.1.0-next.0",
|
||||
"backstage": {
|
||||
"role": "web-library"
|
||||
},
|
||||
@@ -9,11 +9,10 @@
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"private": true,
|
||||
"keywords": [
|
||||
"backstage"
|
||||
],
|
||||
"homepage": "https://backstage.io",
|
||||
"homepage": "https://canon.backstage.io",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/backstage/backstage",
|
||||
@@ -28,9 +27,9 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "yarn build:app && yarn build:css",
|
||||
"build-storybook": "storybook build",
|
||||
"build:app": "backstage-cli package build",
|
||||
"build:css": "node scripts/build-css.mjs",
|
||||
"build-storybook": "storybook build",
|
||||
"clean": "backstage-cli package clean",
|
||||
"lint": "backstage-cli package lint",
|
||||
"prepack": "backstage-cli package prepack",
|
||||
|
||||
@@ -68,7 +68,7 @@ export interface BoxProps extends UtilityProps {
|
||||
}
|
||||
|
||||
// @public (undocumented)
|
||||
export type Breakpoint = 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
|
||||
export type Breakpoint = 'initial' | 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
||||
|
||||
// @public (undocumented)
|
||||
export const Button: React_2.ForwardRefExoticComponent<
|
||||
@@ -101,12 +101,6 @@ export interface ButtonProps {
|
||||
|
||||
// @public (undocumented)
|
||||
export interface CanonContextProps {
|
||||
// (undocumented)
|
||||
breakpoint: Breakpoint;
|
||||
// (undocumented)
|
||||
getResponsiveValue: (
|
||||
value: string | Partial<Record<Breakpoint, string>>,
|
||||
) => string;
|
||||
// (undocumented)
|
||||
icons: IconMap;
|
||||
}
|
||||
|
||||
@@ -382,7 +382,7 @@ export const BorderRadius: Story = {
|
||||
export const Border: Story = {
|
||||
args: {
|
||||
style: {
|
||||
background: 'var(--canon-surface-1)',
|
||||
background: 'var(--canon-bg-elevated)',
|
||||
color: 'var(--canon-text-primary)',
|
||||
padding: '4px 8px',
|
||||
width: '80px',
|
||||
|
||||
@@ -114,7 +114,7 @@ export const Responsive: Story = {
|
||||
args: {
|
||||
children: 'Button',
|
||||
variant: {
|
||||
xs: 'primary',
|
||||
initial: 'primary',
|
||||
sm: 'secondary',
|
||||
md: 'tertiary',
|
||||
},
|
||||
|
||||
@@ -18,9 +18,10 @@
|
||||
|
||||
import React, { forwardRef } from 'react';
|
||||
import { Icon } from '../Icon';
|
||||
import { ButtonProps } from './types';
|
||||
import { useCanon } from '../../contexts/canon';
|
||||
import clsx from 'clsx';
|
||||
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
|
||||
|
||||
import type { ButtonProps } from './types';
|
||||
|
||||
/** @public */
|
||||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
@@ -37,11 +38,9 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const { getResponsiveValue } = useCanon();
|
||||
|
||||
// Get the responsive value for the variant
|
||||
const responsiveSize = getResponsiveValue(size);
|
||||
const responsiveVariant = getResponsiveValue(variant);
|
||||
const responsiveSize = useResponsiveValue(size);
|
||||
const responsiveVariant = useResponsiveValue(variant);
|
||||
|
||||
return (
|
||||
<button
|
||||
|
||||
@@ -22,16 +22,16 @@
|
||||
user-select: none;
|
||||
font-family: var(--canon-font-regular);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
font-size: var(--canon-font-size-body);
|
||||
font-size: var(--canon-font-size-3);
|
||||
padding: 0;
|
||||
transition: all 150ms ease;
|
||||
cursor: pointer;
|
||||
border-radius: 8px;
|
||||
border-radius: var(--canon-radius-2);
|
||||
}
|
||||
|
||||
.canon-Button--variant-primary {
|
||||
background-color: var(--canon-accent);
|
||||
color: var(--canon-text-primary-on-accent);
|
||||
background-color: var(--canon-bg-accent);
|
||||
color: var(--canon-fg-accent);
|
||||
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
@@ -42,7 +42,7 @@
|
||||
|
||||
.canon-Button--variant-secondary {
|
||||
background-color: transparent;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-base);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
color: var(--canon-text-primary);
|
||||
|
||||
&:hover {
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
justify-content: center;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border-base);
|
||||
box-shadow: inset 0 0 0 1px var(--canon-border);
|
||||
cursor: pointer;
|
||||
border-radius: 2px;
|
||||
transition: all 0.2s ease-in-out;
|
||||
|
||||
&[data-checked] {
|
||||
background-color: var(--canon-accent);
|
||||
background-color: var(--canon-bg-accent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--canon-spacing-xs);
|
||||
gap: var(--canon-space-2);
|
||||
font-size: var(--canon-font-size-xs);
|
||||
font-family: var(--canon-font-regular);
|
||||
color: var(--canon-text-primary);
|
||||
@@ -37,5 +37,5 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--canon-text-primary-on-accent);
|
||||
color: var(--canon-fg-accent);
|
||||
}
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
}
|
||||
|
||||
.canon-FieldLabel {
|
||||
font-size: var(--canon-font-size-caption);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-primary);
|
||||
margin-bottom: var(--canon-space-1_5);
|
||||
}
|
||||
|
||||
.canon-FieldDescription {
|
||||
font-size: var(--canon-font-size-caption);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-secondary);
|
||||
margin: 0;
|
||||
@@ -37,15 +37,15 @@
|
||||
}
|
||||
|
||||
.canon-FieldError {
|
||||
font-size: var(--canon-font-size-caption);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-error);
|
||||
color: var(--canon-fg-danger);
|
||||
margin: 0;
|
||||
padding-top: var(--canon-space-1_5);
|
||||
}
|
||||
|
||||
.canon-FieldValidity {
|
||||
font-size: var(--canon-font-size-caption);
|
||||
font-size: var(--canon-font-size-2);
|
||||
font-weight: var(--canon-font-weight-regular);
|
||||
color: var(--canon-text-secondary);
|
||||
margin: 0;
|
||||
|
||||
@@ -17,9 +17,10 @@
|
||||
'use client';
|
||||
|
||||
import React, { forwardRef } from 'react';
|
||||
import { HeadingProps } from './types';
|
||||
import { useCanon } from '../../contexts/canon';
|
||||
import clsx from 'clsx';
|
||||
import { useResponsiveValue } from '../../hooks/useResponsiveValue';
|
||||
|
||||
import type { HeadingProps } from './types';
|
||||
|
||||
/** @public */
|
||||
export const Heading = forwardRef<HTMLHeadingElement, HeadingProps>(
|
||||
@@ -31,10 +32,9 @@ export const Heading = forwardRef<HTMLHeadingElement, HeadingProps>(
|
||||
className,
|
||||
...restProps
|
||||
} = props;
|
||||
const { getResponsiveValue } = useCanon();
|
||||
|
||||
// Get the responsive value for the variant
|
||||
const responsiveVariant = getResponsiveValue(variant);
|
||||
const responsiveVariant = useResponsiveValue(variant);
|
||||
|
||||
// Determine the component to render based on the variant
|
||||
let Component = as;
|
||||
|
||||
@@ -23,22 +23,22 @@
|
||||
}
|
||||
|
||||
.canon-Heading--variant-display {
|
||||
font-size: var(--canon-font-size-display);
|
||||
font-size: var(--canon-font-size-10);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading--variant-title1 {
|
||||
font-size: var(--canon-font-size-title1);
|
||||
font-size: var(--canon-font-size-9);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading--variant-title2 {
|
||||
font-size: var(--canon-font-size-title2);
|
||||
font-size: var(--canon-font-size-8);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
.canon-Heading--variant-title3 {
|
||||
font-size: var(--canon-font-size-title3);
|
||||
font-size: var(--canon-font-size-7);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
@@ -48,6 +48,6 @@
|
||||
}
|
||||
|
||||
.canon-Heading--variant-title5 {
|
||||
font-size: var(--canon-font-size-title5);
|
||||
font-size: var(--canon-font-size-5);
|
||||
font-weight: var(--canon-font-weight-bold);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user