From 0e7f256034d110cfc31d084bfa5d123d616ceb79 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 29 Nov 2021 13:53:10 +0100 Subject: [PATCH 1/4] core-app-api: remedy wrong relative route reference resolution Signed-off-by: Patrik Oldsberg --- .changeset/thick-poems-camp.md | 5 ++ .../src/routing/RouteResolver.test.ts | 54 +++++++++++++++---- .../core-app-api/src/routing/RouteResolver.ts | 27 +++++++++- 3 files changed, 74 insertions(+), 12 deletions(-) create mode 100644 .changeset/thick-poems-camp.md diff --git a/.changeset/thick-poems-camp.md b/.changeset/thick-poems-camp.md new file mode 100644 index 0000000000..72056dfb5a --- /dev/null +++ b/.changeset/thick-poems-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +Fixed a bug where `useRouteRef` would fail in situations where relative navigation was needed and the app was is mounted on a sub-path. This would typically show up as a failure to navigate to a tab on an entity page. diff --git a/packages/core-app-api/src/routing/RouteResolver.test.ts b/packages/core-app-api/src/routing/RouteResolver.test.ts index 1b7eb10003..b068bd5e6d 100644 --- a/packages/core-app-api/src/routing/RouteResolver.test.ts +++ b/packages/core-app-api/src/routing/RouteResolver.test.ts @@ -100,23 +100,55 @@ describe('RouteResolver', () => { expect(r.resolve(externalRef4, '/')?.({ x: '6x' })).toBe(undefined); }); - it('should resolve an absolute route and an app base path', () => { + it('should resolve an absolute route and sub route with an app base path', () => { const r = new RouteResolver( - new Map([[ref1, '/my-route']]), - new Map(), - [{ routeRefs: new Set([ref1]), path: '/my-route', ...rest }], + new Map([ + [ref2, '/my-parent/:x'], + [ref1, '/my-route'], + ]), + new Map([[ref1, ref2]]), + [ + { + routeRefs: new Set([ref2]), + path: '/my-parent/:x', + ...rest, + children: [ + MATCH_ALL_ROUTE, + { routeRefs: new Set([ref1]), path: '/my-route', ...rest }, + ], + }, + ], new Map(), '/base', ); - expect(r.resolve(ref1, '/')?.()).toBe('/base/my-route'); - expect(r.resolve(ref2, '/')?.({ x: '1x' })).toBe(undefined); - expect(r.resolve(subRef1, '/')?.()).toBe('/base/my-route/foo'); - expect(r.resolve(subRef2, '/')?.({ a: '2a' })).toBe( - '/base/my-route/foo/2a', + expect(r.resolve(ref1, '/my-parent/1x')?.()).toBe( + '/base/my-parent/1x/my-route', + ); + expect(r.resolve(ref1, '/base/my-parent/1x')?.()).toBe( + '/base/my-parent/1x/my-route', + ); + expect(r.resolve(ref2, '/')?.({ x: '1x' })).toBe('/base/my-parent/1x'); + expect(r.resolve(ref2, '/base')?.({ x: '1x' })).toBe('/base/my-parent/1x'); + expect(r.resolve(ref3, '/')?.({ y: '1y' })).toBe(undefined); + expect(r.resolve(subRef1, '/my-parent/2x')?.()).toBe( + '/base/my-parent/2x/my-route/foo', + ); + expect(r.resolve(subRef1, '/base/my-parent/2x')?.()).toBe( + '/base/my-parent/2x/my-route/foo', + ); + expect(r.resolve(subRef2, '/my-parent/3x')?.({ a: '2a' })).toBe( + '/base/my-parent/3x/my-route/foo/2a', + ); + expect(r.resolve(subRef2, '/base/my-parent/3x')?.({ a: '2a' })).toBe( + '/base/my-parent/3x/my-route/foo/2a', + ); + expect(r.resolve(subRef3, '/')?.({ x: '5x' })).toBe( + '/base/my-parent/5x/bar', + ); + expect(r.resolve(subRef4, '/')?.({ x: '6x', a: '4a' })).toBe( + '/base/my-parent/6x/bar/4a', ); - expect(r.resolve(subRef3, '/')?.({ x: '3x' })).toBe(undefined); - expect(r.resolve(subRef4, '/')?.({ x: '4x', a: '4a' })).toBe(undefined); expect(r.resolve(externalRef1, '/')?.()).toBe(undefined); expect(r.resolve(externalRef2, '/')?.()).toBe(undefined); expect(r.resolve(externalRef3, '/')?.({ x: '5x' })).toBe(undefined); diff --git a/packages/core-app-api/src/routing/RouteResolver.ts b/packages/core-app-api/src/routing/RouteResolver.ts index b5be5f893e..f186b28586 100644 --- a/packages/core-app-api/src/routing/RouteResolver.ts +++ b/packages/core-app-api/src/routing/RouteResolver.ts @@ -207,6 +207,20 @@ export class RouteResolver { return undefined; } + // The location that we get passed in uses the full path, so start by trimming off + // the app base path prefix in case we're running the app on a sub-path. + let relativeSourceLocation: Parameters[1]; + if (typeof sourceLocation === 'string') { + relativeSourceLocation = this.trimPath(sourceLocation); + } else if (sourceLocation.pathname) { + relativeSourceLocation = { + ...sourceLocation, + pathname: this.trimPath(sourceLocation.pathname), + }; + } else { + relativeSourceLocation = sourceLocation; + } + // Next we figure out the base path, which is the combination of the common parent path // between our current location and our target location, as well as the additional path // that is the difference between the parent path and the base of our target location. @@ -214,7 +228,7 @@ export class RouteResolver { this.appBasePath + resolveBasePath( targetRef, - sourceLocation, + relativeSourceLocation, this.routePaths, this.routeParents, this.routeObjects, @@ -225,4 +239,15 @@ export class RouteResolver { }; return routeFunc; } + + private trimPath(targetPath: string) { + if (!targetPath) { + return targetPath; + } + + if (targetPath.startsWith(this.appBasePath)) { + return targetPath.slice(this.appBasePath.length); + } + return targetPath; + } } From 9200a2a858f417986cfe37b43deedbc6c89de716 Mon Sep 17 00:00:00 2001 From: Suzanne Daniels Date: Mon, 29 Nov 2021 14:41:49 +0100 Subject: [PATCH 2/4] Set DB expectations(see #6811) + edits (#7499) Signed-off-by: Suzanne Daniels --- docs/getting-started/index.md | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 5a970ef143..82eb2c1d9b 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -23,7 +23,7 @@ guide to do a repository-based installation. - Access to a Linux-based operating system, such as Linux, MacOS or [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/) -- An account with elevated rights +- An account with elevated rights to install the dependencies - `curl` or `wget` installed - Node.js Active LTS Release installed (currently v14) using one of these methods: @@ -36,15 +36,16 @@ guide to do a repository-based installation. - `yarn` [Installation](https://classic.yarnpkg.com/en/docs/install) - `docker` [installation](https://docs.docker.com/engine/install/) - `git` [installation](https://github.com/git-guides/install-git) -- If the system is not directly accessible over your network, the following - ports need to be opened: 3000, 7007 +- If the system is not directly accessible over your network (uncommon, mostly + when you're installing in a container, VM, remote system), the following ports + need to be opened: 3000, 7000 ### Create your Backstage App To install the Backstage Standalone app, we make use of `npx`, a tool to run -Node executables straight from the registry. Running the command below will -install Backstage. The wizard will create a subdirectory inside your current -working directory. +Node executables straight from the registry. This tool is part of your Node.js +installation. Running the command below will install Backstage. The wizard will +create a subdirectory inside your current working directory. ```bash npx @backstage/create-app @@ -78,12 +79,21 @@ yarn dev It might take a little while, but as soon as the message `[0] webpack compiled successfully` appears, you can open a browser and directly navigate to your freshly installed Backstage portal at `http://localhost:3000`. -You can start exploring the demo immediately. +You can start exploring the demo immediately. Please note that the in-memory +database will be cleared when you restart the app, so you'll most likely will +want to carry on with the database steps.

Screenshot of the Backstage portal.

+The most common next steps are to configure Backstage, add a plugin and moving +to a persistent database: + +- [Setting up Authentication](https://backstage.io/docs/auth/) +- [Switching from SQLite to PostgresQL](https://backstage.io/docs/tutorials/switching-sqlite-postgres) +- [Adding a plugin](https://backstage.io/docs/getting-started/configure-app-with-plugins) + Congratulations! That should be it. Let us know how it went: [on discord](https://discord.gg/EBHEGzX), file issues for any [feature](https://github.com/backstage/backstage/issues/new?labels=help+wanted&template=feature_template.md) @@ -93,10 +103,3 @@ or [bugs](https://github.com/backstage/backstage/issues/new?labels=bug&template=bug_template.md) you have, and feel free to [contribute](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md)! - -The most common next steps are to configure Backstage, add a plugin and moving -to a more persistent database: - -- [Setting up Authentication](https://backstage.io/docs/auth/) -- [Switching from SQLite to PostgresQL](https://backstage.io/docs/tutorials/switching-sqlite-postgres) -- [Adding a plugin](https://backstage.io/docs/getting-started/configure-app-with-plugins) From e9c503638615bc3c66b31c1d0790619c868d88a4 Mon Sep 17 00:00:00 2001 From: Suzanne Daniels Date: Mon, 29 Nov 2021 15:11:11 +0100 Subject: [PATCH 3/4] Editing port back to 7070 Signed-off-by: Suzanne Daniels --- docs/getting-started/index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 82eb2c1d9b..6f0a685e39 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -36,9 +36,9 @@ guide to do a repository-based installation. - `yarn` [Installation](https://classic.yarnpkg.com/en/docs/install) - `docker` [installation](https://docs.docker.com/engine/install/) - `git` [installation](https://github.com/git-guides/install-git) -- If the system is not directly accessible over your network (uncommon, mostly - when you're installing in a container, VM, remote system), the following ports - need to be opened: 3000, 7000 +- If the system is not directly accessible over your network the following ports + need to be opened: 3000, 7007. This is quite uncommon, unless when you're + installing in a container, VM or remote system. ### Create your Backstage App @@ -80,8 +80,8 @@ It might take a little while, but as soon as the message `[0] webpack compiled successfully` appears, you can open a browser and directly navigate to your freshly installed Backstage portal at `http://localhost:3000`. You can start exploring the demo immediately. Please note that the in-memory -database will be cleared when you restart the app, so you'll most likely will -want to carry on with the database steps. +database will be cleared when you restart the app, so you'll most likely want to +carry on with the database steps.

Screenshot of the Backstage portal. From 57abdfc70309be9a9afb045bfafec3787555a391 Mon Sep 17 00:00:00 2001 From: Suzanne Daniels Date: Mon, 29 Nov 2021 15:53:41 +0100 Subject: [PATCH 4/4] Applying @adamdmharvey suggestions from #8275 Signed-off-by: Suzanne Daniels --- docs/getting-started/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 6f0a685e39..02e2e4d51d 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -87,11 +87,11 @@ carry on with the database steps. Screenshot of the Backstage portal.

-The most common next steps are to configure Backstage, add a plugin and moving -to a persistent database: +The most common next steps are to move to a persistent database, configure +authentication, and add a plugin: -- [Setting up Authentication](https://backstage.io/docs/auth/) - [Switching from SQLite to PostgresQL](https://backstage.io/docs/tutorials/switching-sqlite-postgres) +- [Setting up Authentication](https://backstage.io/docs/auth/) - [Adding a plugin](https://backstage.io/docs/getting-started/configure-app-with-plugins) Congratulations! That should be it. Let us know how it went: