Merge branch 'backstage:master' into master

This commit is contained in:
Abhinv Singh Parmar
2023-10-13 15:31:48 +05:30
committed by GitHub
19 changed files with 188 additions and 26 deletions
+1 -1
View File
@@ -2,6 +2,6 @@
'@backstage/cli': minor
---
The new backend start command that used to be enabled by setting `EXPERIMENTAL_BACKEND_START` is now the default. To revert to the old behavior, set `LEGACY_BACKEND_START` instead.
**BREAKING** The new backend start command that used to be enabled by setting `EXPERIMENTAL_BACKEND_START` is now the default. To revert to the old behavior set `LEGACY_BACKEND_START`, which is recommended if you haven't migrated to the new backend system.
This new command is no longer based on Webpack, but instead uses Node.js loaders to transpile on the fly. Rather than hot reloading modules the entire backend is now restarted on change, but the SQLite database state is still maintained across restarts via a parent process.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': minor
---
Added experimental support for declarative integration via the `/alpha` subpath.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/plugin-catalog-backend-module-github': patch
'@backstage/cli': patch
---
Request slightly smaller pages of data from GitHub
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/dev-utils': patch
---
Moving development `dependencies` to `devDependencies`
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-app-api': patch
---
Fixed a bug in `TranslationApi` implementation where in some cases it wouldn't notify subscribers of language changes.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search-backend': patch
---
Set the default length limit to search query to 100. To override it, define `search.maxTermLength` in the config file.
+1 -1
View File
@@ -4,7 +4,7 @@
### Minor Changes
- 7077dbf131: The new backend start command that used to be enabled by setting `EXPERIMENTAL_BACKEND_START` is now the default. To revert to the old behavior, set `LEGACY_BACKEND_START` instead.
- 7077dbf131: **BREAKING** The new backend start command that used to be enabled by setting `EXPERIMENTAL_BACKEND_START` is now the default. To revert to the old behavior set `LEGACY_BACKEND_START`, which is recommended if you haven't migrated to the new backend system.
This new command is no longer based on Webpack, but instead uses Node.js loaders to transpile on the fly. Rather than hot reloading modules the entire backend is now restarted on change, but the SQLite database state is still maintained across restarts via a parent process.
+2
View File
@@ -25,6 +25,7 @@ import {
createPageExtension,
} from '@backstage/frontend-plugin-api';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
import techdocsPlugin from '@backstage/plugin-techdocs/alpha';
/*
@@ -67,6 +68,7 @@ const app = createApp({
graphiqlPlugin,
pagesPlugin,
techRadarPlugin,
techdocsPlugin,
userSettingsPlugin,
createExtensionOverrides({
extensions: [entityPageExtension],
+1 -1
View File
@@ -61,7 +61,7 @@
### Minor Changes
- 7077dbf131: The new backend start command that used to be enabled by setting `EXPERIMENTAL_BACKEND_START` is now the default. To revert to the old behavior, set `LEGACY_BACKEND_START` instead.
- 7077dbf131: **BREAKING** The new backend start command that used to be enabled by setting `EXPERIMENTAL_BACKEND_START` is now the default. To revert to the old behavior set `LEGACY_BACKEND_START`, which is recommended if you haven't migrated to the new backend system.
This new command is no longer based on Webpack, but instead uses Node.js loaders to transpile on the fly. Rather than hot reloading modules the entire backend is now restarted on change, but the SQLite database state is still maintained across restarts via a parent process.
@@ -113,7 +113,7 @@ export class GithubDiscoveryProvider implements Provider {
const query = `query repositories($org: String!, $cursor: String) {
repositoryOwner(login: $org) {
login
repositories(first: 100, after: $cursor) {
repositories(first: 50, after: $cursor) {
nodes {
name
url
@@ -377,8 +377,7 @@ describe('I18nextTranslationApi', () => {
},
});
});
expect(translations).toEqual(['foo', null, 'Föö', null, 'Føø']);
expect(translations).toEqual(['foo', 'Föö', 'Føø']);
});
describe('formatting', () => {
@@ -241,7 +241,6 @@ export class I18nextTranslationApi implements TranslationApi {
return new ObservableImpl<TranslationSnapshot<TMessages>>(subscriber => {
let loadTicket = {}; // To check for stale loads
let lastSnapshotWasReady = false;
const loadResource = () => {
loadTicket = {};
@@ -250,8 +249,7 @@ export class I18nextTranslationApi implements TranslationApi {
() => {
if (ticket === loadTicket) {
const snapshot = this.#createSnapshot(internalRef);
if (snapshot.ready || lastSnapshotWasReady) {
lastSnapshotWasReady = snapshot.ready;
if (snapshot.ready) {
subscriber.next(snapshot);
}
}
@@ -266,12 +264,9 @@ export class I18nextTranslationApi implements TranslationApi {
const onChange = () => {
const snapshot = this.#createSnapshot(internalRef);
if (lastSnapshotWasReady && !snapshot.ready) {
lastSnapshotWasReady = snapshot.ready;
if (snapshot.ready) {
subscriber.next(snapshot);
}
if (!snapshot.ready) {
} else {
loadResource();
}
};
@@ -103,6 +103,7 @@ describe('tasks', () => {
const mockDir = createMockDirectory();
const origCwd = process.cwd();
const realChdir = process.chdir;
// If anyone calls chdir then make it resolve within the tmpdir
const mockChdir = jest.spyOn(process, 'chdir');
@@ -130,6 +131,10 @@ describe('tasks', () => {
mockChdir.mockReset();
});
afterAll(() => {
realChdir(origCwd);
});
describe('checkAppExistsTask', () => {
it('should do nothing if the directory does not exist', async () => {
const dir = 'projects/';
+8 -8
View File
@@ -40,17 +40,11 @@
"@backstage/core-plugin-api": "workspace:^",
"@backstage/integration-react": "workspace:^",
"@backstage/plugin-catalog-react": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@backstage/theme": "workspace:^",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",
"@types/react": "^16.13.1 || ^17.0.0",
"react-use": "^17.2.4",
"zen-observable": "^0.10.0"
"react-use": "^17.2.4"
},
"peerDependencies": {
"react": "^16.13.1 || ^17.0.0",
@@ -58,7 +52,13 @@
"react-router-dom": "6.0.0-beta.0 || ^6.3.0"
},
"devDependencies": {
"@backstage/cli": "workspace:^"
"@backstage/cli": "workspace:^",
"@backstage/test-utils": "workspace:^",
"@testing-library/dom": "^9.0.0",
"@testing-library/jest-dom": "^6.0.0",
"@testing-library/react": "^12.1.3",
"@testing-library/user-event": "^14.0.0",
"zen-observable": "^0.10.0"
},
"files": [
"dist"
@@ -462,7 +462,7 @@ export async function getOrganizationRepositories(
query repositories($org: String!, $catalogPathRef: String!, $cursor: String) {
repositoryOwner(login: $org) {
login
repositories(first: 100, after: $cursor) {
repositories(first: 50, after: $cursor) {
nodes {
name
catalogInfoFile: object(expression: $catalogPathRef) {
+5
View File
@@ -22,6 +22,11 @@ export interface Config {
*/
maxPageLimit?: number;
/**
* Sets the maximum term length for the search string. Defaults to 100.
*/
maxTermLength?: number;
/**
* Options related to the search integration with the Backstage permissions system
*/
@@ -60,7 +60,7 @@ describe('createRouter', () => {
},
config: new ConfigReader({
permissions: { enabled: false },
search: { maxPageLimit: 200 },
search: { maxPageLimit: 200, maxTermLength: 20 },
}),
permissions: mockPermissionEvaluator,
logger,
@@ -162,6 +162,19 @@ describe('createRouter', () => {
});
});
it('should reject term length over configured max', async () => {
const response = await request(app).get(
`/query?term=HelloWorld1234567890!`,
);
expect(response.status).toEqual(400);
expect(response.body).toMatchObject({
error: {
message: /The term length "21" is greater than "20"/i,
},
});
});
it('removes backend-only properties from search documents', async () => {
mockSearchEngine.query.mockResolvedValue({
results: [
+13 -1
View File
@@ -63,6 +63,7 @@ export type RouterOptions = {
};
const defaultMaxPageLimit = 100;
const defaultMaxTermLength = 100;
const allowedLocationProtocols = ['http:', 'https:'];
/**
@@ -77,8 +78,19 @@ export async function createRouter(
const maxPageLimit =
config.getOptionalNumber('search.maxPageLimit') ?? defaultMaxPageLimit;
const maxTermLength =
config.getOptionalNumber('search.maxTermLength') ?? defaultMaxTermLength;
const requestSchema = z.object({
term: z.string().default(''),
term: z
.string()
.refine(
term => term.length <= maxTermLength,
term => ({
message: `The term length "${term.length}" is greater than "${maxTermLength}"`,
}),
)
.default(''),
filters: jsonObjectSchema.optional(),
types: z
.array(z.string().refine(type => Object.keys(types).includes(type)))
+106 -1
View File
@@ -18,8 +18,32 @@ import React from 'react';
import {
createPlugin,
createSchemaFromZod,
createApiExtension,
createPageExtension,
} from '@backstage/frontend-plugin-api';
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react/alpha';
import {
configApiRef,
createApiFactory,
createRouteRef,
discoveryApiRef,
fetchApiRef,
identityApiRef,
} from '@backstage/core-plugin-api';
import {
techdocsApiRef,
techdocsStorageApiRef,
} from '@backstage/plugin-techdocs-react';
import { TechDocsClient, TechDocsStorageClient } from './client';
const rootRouteRef = createRouteRef({
id: 'plugin.techdocs.indexPage',
});
const rootDocsRouteRef = createRouteRef({
id: 'plugin.techdocs.readerPage',
params: ['namespace', 'kind', 'name'],
});
/** @alpha */
export const TechDocsSearchResultListItemExtension =
@@ -44,8 +68,89 @@ export const TechDocsSearchResultListItemExtension =
},
});
/**
* Responsible for rendering the provided router element
*
* @alpha
*/
const TechDocsIndexPage = createPageExtension({
id: 'plugin.techdocs.indexPage',
defaultPath: '/docs',
routeRef: rootRouteRef,
loader: () =>
import('./home/components/TechDocsIndexPage').then(m => (
<m.TechDocsIndexPage />
)),
});
/**
* Component responsible for composing a TechDocs reader page experience
*
* @alpha
*/
const TechDocsReaderPage = createPageExtension({
id: 'plugin.techdocs.readerPage',
loader: () =>
import('./reader/components/TechDocsReaderPage').then(m => (
<m.TechDocsReaderPage />
)),
routeRef: rootDocsRouteRef,
defaultPath: '/docs/:namespace/:kind/:name/*',
});
/** @alpha */
const techDocsStorage = createApiExtension({
api: techdocsStorageApiRef,
factory() {
return createApiFactory({
api: techdocsStorageApiRef,
deps: {
configApi: configApiRef,
discoveryApi: discoveryApiRef,
identityApi: identityApiRef,
fetchApi: fetchApiRef,
},
factory: ({ configApi, discoveryApi, identityApi, fetchApi }) =>
new TechDocsStorageClient({
configApi,
discoveryApi,
identityApi,
fetchApi,
}),
});
},
});
/** @alpha */
const techDocsClient = createApiExtension({
api: techdocsApiRef,
factory() {
return createApiFactory({
api: techdocsApiRef,
deps: {
configApi: configApiRef,
discoveryApi: discoveryApiRef,
fetchApi: fetchApiRef,
},
factory: ({ configApi, discoveryApi, fetchApi }) =>
new TechDocsClient({
configApi,
discoveryApi,
fetchApi,
}),
});
},
});
/** @alpha */
export default createPlugin({
id: 'techdocs',
extensions: [TechDocsSearchResultListItemExtension],
extensions: [
TechDocsIndexPage,
TechDocsReaderPage,
techDocsClient,
techDocsStorage,
TechDocsSearchResultListItemExtension,
],
});