From db28b524c48da657eec00e1deb92affece47a3df Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 30 Nov 2020 13:53:46 +0100 Subject: [PATCH] core-api: remove RouteRefRegistry and SubRouteRefs Co-authored-by: blam --- packages/core-api/src/routing/RouteRef.ts | 52 +----- .../src/routing/RouteRefRegistry.test.ts | 105 ------------ .../core-api/src/routing/RouteRefRegistry.ts | 155 ------------------ packages/core-api/src/routing/index.ts | 2 +- packages/core-api/src/routing/types.ts | 11 -- 5 files changed, 3 insertions(+), 322 deletions(-) delete mode 100644 packages/core-api/src/routing/RouteRefRegistry.test.ts delete mode 100644 packages/core-api/src/routing/RouteRefRegistry.ts diff --git a/packages/core-api/src/routing/RouteRef.ts b/packages/core-api/src/routing/RouteRef.ts index c33335cb38..e7160fdef6 100644 --- a/packages/core-api/src/routing/RouteRef.ts +++ b/packages/core-api/src/routing/RouteRef.ts @@ -14,43 +14,9 @@ * limitations under the License. */ -import { - ConcreteRoute, - routeReference, - ReferencedRoute, - resolveRoute, - RouteRefConfig, -} from './types'; -import { generatePath } from 'react-router-dom'; +import { RouteRefConfig } from './types'; -type SubRouteConfig = { - path: string; -}; - -export class SubRouteRef - implements ReferencedRoute { - constructor( - private readonly parent: ConcreteRoute, - private readonly config: SubRouteConfig, - ) {} - - get [routeReference]() { - return this; - } - - link(...args: Args): ConcreteRoute { - return { - [routeReference]: this, - [resolveRoute]: (path: string) => { - const ownPart = generatePath(this.config.path, args[0] ?? {}); - const parentPart = this.parent[resolveRoute](path); - return parentPart + ownPart; - }, - }; - } -} - -export class AbsoluteRouteRef implements ConcreteRoute { +export class AbsoluteRouteRef { constructor(private readonly config: RouteRefConfig) {} get icon() { @@ -65,20 +31,6 @@ export class AbsoluteRouteRef implements ConcreteRoute { get title() { return this.config.title; } - - createSubRoute( - config: SubRouteConfig, - ) { - return new SubRouteRef(this, config); - } - - get [routeReference]() { - return this; - } - - [resolveRoute](path: string) { - return path; - } } export function createRouteRef(config: RouteRefConfig): AbsoluteRouteRef { diff --git a/packages/core-api/src/routing/RouteRefRegistry.test.ts b/packages/core-api/src/routing/RouteRefRegistry.test.ts deleted file mode 100644 index fa1ef584f1..0000000000 --- a/packages/core-api/src/routing/RouteRefRegistry.test.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { RouteRefRegistry } from './RouteRefRegistry'; -import { createRouteRef } from './RouteRef'; - -const dummyConfig = { path: '/', icon: () => null, title: 'my-title' }; -const ref1 = createRouteRef(dummyConfig); -const ref11 = createRouteRef(dummyConfig); -const ref12 = createRouteRef(dummyConfig); -const ref121 = createRouteRef(dummyConfig); -const ref2 = createRouteRef(dummyConfig); -const ref2a = ref2.createSubRoute({ path: '/a' }); -const ref2b = ref2.createSubRoute<{ id: string }>({ path: '/b/:id' }); - -describe('RouteRefRegistry', () => { - it('should be constructed with a root route', () => { - const registry = new RouteRefRegistry(); - expect(registry.resolveRoute([], [])).toBe(''); - }); - - it('should register and resolve some absolute routes', () => { - const registry = new RouteRefRegistry(); - expect(registry.registerRoute([ref1], '1')).toBe(true); - expect(registry.registerRoute([ref1, ref11], '11')).toBe(true); - expect(registry.registerRoute([ref1, ref12], '12')).toBe(true); - expect(registry.registerRoute([ref1, ref12, ref121], '121')).toBe(true); - expect(registry.registerRoute([ref1, ref12, ref121], 'duplicate')).toBe( - false, - ); - expect(registry.registerRoute([ref1, ref12], 'duplicate')).toBe(false); - expect(registry.registerRoute([ref2], '2')).toBe(true); - expect(registry.registerRoute([ref2], 'duplicate')).toBe(false); - expect(registry.registerRoute([ref2], '2')).toBe(true); - - expect(registry.resolveRoute([], [ref1])).toBe('/1'); - expect(registry.resolveRoute([], [ref11])).toBe(undefined); - expect(registry.resolveRoute([], [ref1, ref11])).toBe('/1/11'); - expect(registry.resolveRoute([ref1], [ref11])).toBe('/1/11'); - expect(registry.resolveRoute([ref1], [ref2])).toBe('/2'); - expect(registry.resolveRoute([ref1, ref12, ref121], [])).toBe('/1/12/121'); - expect(registry.resolveRoute([ref1, ref12, ref121], [ref121])).toBe( - '/1/12/121', - ); - expect(registry.resolveRoute([ref1, ref12, ref121], [ref12, ref121])).toBe( - '/1/12/121', - ); - expect(registry.resolveRoute([ref1, ref12, ref121], [ref12])).toBe('/1/12'); - expect(registry.resolveRoute([ref1, ref12, ref121], [ref1])).toBe('/1'); - }); - - it('should register and resolve with sub routes', () => { - const registry = new RouteRefRegistry(); - expect(registry.registerRoute([ref1], '1')).toBe(true); - expect(registry.registerRoute([ref2], '2')).toBe(true); - expect(registry.registerRoute([ref2a], '2')).toBe(true); - expect(registry.registerRoute([ref2a, ref1], '1')).toBe(true); - expect(registry.registerRoute([ref2a, ref2], '2')).toBe(true); - expect(registry.registerRoute([ref2b], '2')).toBe(true); - expect(registry.registerRoute([ref2b, ref1], '1')).toBe(true); - expect(registry.registerRoute([ref2b, ref2], '2')).toBe(true); - - expect(registry.resolveRoute([], [ref1])).toBe('/1'); - expect(registry.resolveRoute([], [ref2])).toBe('/2'); - expect(registry.resolveRoute([], [ref2a.link(), ref1])).toBe('/2/a/1'); - expect(registry.resolveRoute([], [ref2a.link(), ref2])).toBe('/2/a/2'); - expect(registry.resolveRoute([ref2a.link()], [ref2])).toBe('/2/a/2'); - expect(registry.resolveRoute([ref2a.link(), ref1], [ref2])).toBe('/2/a/2'); - expect(registry.resolveRoute([], [ref2b.link({ id: 'abc' }), ref1])).toBe( - '/2/b/abc/1', - ); - expect(registry.resolveRoute([], [ref2b.link({ id: 'xyz' }), ref2])).toBe( - '/2/b/xyz/2', - ); - expect(registry.resolveRoute([ref2b.link({ id: 'abc' })], [ref2])).toBe( - '/2/b/abc/2', - ); - expect( - registry.resolveRoute([ref2b.link({ id: 'abc' }), ref1], [ref2]), - ).toBe('/2/b/abc/2'); - }); - - it('should throw when registering routes incorrectly', () => { - const registry = new RouteRefRegistry(); - expect(() => { - registry.registerRoute([ref1, ref11], '11'); - }).toThrow('Could not find parent for new routing node'); - expect(() => { - registry.registerRoute([], '11'); - }).toThrow('Must provide at least 1 route to add routing node'); - }); -}); diff --git a/packages/core-api/src/routing/RouteRefRegistry.ts b/packages/core-api/src/routing/RouteRefRegistry.ts deleted file mode 100644 index 7e55cbe8f7..0000000000 --- a/packages/core-api/src/routing/RouteRefRegistry.ts +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2020 Spotify AB - * - * 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 { - ConcreteRoute, - routeReference, - resolveRoute, - ReferencedRoute, -} from './types'; - -const rootRoute: ConcreteRoute = { - get [routeReference]() { - return this; - }, - [resolveRoute]: () => '', -}; - -export type RouteRefResolver = { - resolveRoute(from: ConcreteRoute[], to: ConcreteRoute[]): string; -}; - -class Node { - readonly children = new Map(); - - constructor(readonly path: string, readonly parent: Node | undefined) {} - - /** - * Look up a node in the tree given a path. - */ - findNode(routes: ReferencedRoute[]): Node | undefined { - let node = this as Node | undefined; - - for (let i = 0; i < routes.length; i++) { - node = node?.children.get(routes[i][routeReference]); - } - - return node; - } - - /** - * Assigns a path to a leaf node in the routing tree. All ancestor - * nodes of the new leaf node must already exist, or an error will be thrown. - * - * Returns true if the node was added, or false if the node already existed. - */ - addNode(routes: ReferencedRoute[], path: string): boolean { - if (routes.length === 0) { - throw new Error('Must provide at least 1 route to add routing node'); - } - - const parentNode = this.findNode(routes.slice(0, -1)); - if (!parentNode) { - throw new Error('Could not find parent for new routing node'); - } - - const lastRoute = routes[routes.length - 1]; - const lastRouteRef = lastRoute[routeReference]; - - const existingNode = parentNode.children.get(lastRouteRef); - if (existingNode) { - return existingNode.path === path; - } - - parentNode.children.set(lastRouteRef, new Node(path, parentNode)); - return true; - } - - /** - * Resolve an absolute URL that represents this node in the routing tree, using - * using the supplied concrete routes and ancestors of this node. - * - * The length of the provided routes array must match the depth of - * the routing tree that this node is at, or an error will be thrown. - */ - resolve(routes: ConcreteRoute[]) { - const parts = Array(routes.length); - - let node = this as Node | undefined; - for (let i = routes.length - 1; i >= 0; i--) { - if (!node) { - throw new Error('Route resolve missing required parent'); - } - - const route = routes[i]; - parts[i] = route[resolveRoute](node.path); - - node = node.parent; - } - - if (node) { - throw new Error('Route resolve did not reach root'); - } - - return parts.join('/'); - } -} - -/** - * A registry for resolving route refs into concrete string routes. - */ -export class RouteRefRegistry { - private readonly root = new Node('', undefined); - - /** - * Register a new leaf path for a sequence of routes. All ancestor - * routes must already exist. - */ - registerRoute(routes: ReferencedRoute[], path: string): boolean { - return this.root.addNode(routes, path); - } - - /** - * Resolve an absolute path from a point in the routing tree. - * - * The route referenced by `from` must exist, and is the starting - * point for the search, walking up the tree until a subtree that - * matches the routes reference in `to` are found. - * - * If `from` is empty, the search starts and ends at the root node. - * If `to` is empty, the route referenced by `from` will always be returned. - */ - resolveRoute(from: ConcreteRoute[], to: ConcreteRoute[]): string | undefined { - // Keep track of the `from` routes and pop the last ones as we traverse up - // the routing tree. The list of concrete routes that we're passing to - // `node.resolve()` should only include the ones in the resolve path. - const concreteStack = from.slice(); - - let fromNode = this.root.findNode(from); - while (fromNode) { - const resolvedNode = fromNode.findNode(to); - if (resolvedNode) { - return resolvedNode.resolve([rootRoute].concat(concreteStack, to)); - } - - // Search at this level of the tree failed, move up to parent - concreteStack.pop(); - fromNode = fromNode.parent; - } - - return undefined; - } -} diff --git a/packages/core-api/src/routing/index.ts b/packages/core-api/src/routing/index.ts index 29de34ec42..45bb8975db 100644 --- a/packages/core-api/src/routing/index.ts +++ b/packages/core-api/src/routing/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export type { RouteRef, RouteRefConfig, ConcreteRoute } from './types'; +export type { RouteRef, RouteRefConfig } from './types'; export type { MutableRouteRef, AbsoluteRouteRef } from './RouteRef'; export { createRouteRef } from './RouteRef'; diff --git a/packages/core-api/src/routing/types.ts b/packages/core-api/src/routing/types.ts index 162ac74bde..edb1bba196 100644 --- a/packages/core-api/src/routing/types.ts +++ b/packages/core-api/src/routing/types.ts @@ -16,17 +16,6 @@ import { IconComponent } from '../icons'; -export const resolveRoute = Symbol('resolve-route'); -export const routeReference = Symbol('route-ref'); - -export type ReferencedRoute = { - [routeReference]: unknown; -}; - -export type ConcreteRoute = ReferencedRoute & { - [resolveRoute](path: string): string; -}; - export type RouteRef = { // TODO(Rugvip): Remove path, look up via registry instead path: string;