UniRef: add CascadingReferenceDetector implementation (#1510)

The CascadingReferenceDetector is an abstraction we can use for combining multiple ReferenceDetector instances and giving them a priority.
This commit is contained in:
Robin van Boven 2020-01-07 13:39:21 +01:00 committed by GitHub
parent 821be0b46e
commit 953cbfaac2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 109 additions and 0 deletions

View File

@ -0,0 +1,23 @@
//@flow
import type {NodeAddressT} from "../graph";
import type {ReferenceDetector, URL} from "./referenceDetector";
/**
* A class for composing ReferenceDetectors. Calls ReferenceDetectors in the order
* they're given in the constructor, returning the first NodeAddressT it encounters.
*/
export class CascadingReferenceDetector implements ReferenceDetector {
refs: $ReadOnlyArray<ReferenceDetector>;
constructor(refs: $ReadOnlyArray<ReferenceDetector>) {
this.refs = refs;
}
addressFromUrl(url: URL): ?NodeAddressT {
for (const ref of this.refs) {
const addr = ref.addressFromUrl(url);
if (addr) return addr;
}
}
}

View File

@ -0,0 +1,85 @@
// @flow
import {type NodeAddressT, NodeAddress} from "../graph";
import type {URL, ReferenceDetector} from "./referenceDetector";
import {MappedReferenceDetector} from "./mappedReferenceDetector";
import {CascadingReferenceDetector} from "./cascadingReferenceDetector";
const nodeA = NodeAddress.fromParts(["test", "A"]);
const nodeB = NodeAddress.fromParts(["test", "B"]);
const nodeC = NodeAddress.fromParts(["test", "C"]);
function exampleDetector(pairs?: [URL, NodeAddressT][]): ReferenceDetector {
const map: Map<URL, NodeAddressT> = new Map(pairs);
const refs = new MappedReferenceDetector(map);
jest.spyOn(refs, "addressFromUrl");
return refs;
}
describe("core/references/cascadingReferenceDetector", () => {
describe("CascadingReferenceDetector", () => {
it("should implement the ReferenceDetector interface", () => {
const _unused_toReferenceDetector = (
x: CascadingReferenceDetector
): ReferenceDetector => x;
});
it("should try all ReferenceDetectors to look for a hit", () => {
// Given
const refs1 = exampleDetector();
const refs2 = exampleDetector();
const refs3 = exampleDetector();
// When
const refs = new CascadingReferenceDetector([refs1, refs2, refs3]);
const n1 = refs.addressFromUrl("http://foo.bar/miss");
// Then
expect(refs1.addressFromUrl.mock.calls).toEqual([
["http://foo.bar/miss"],
]);
expect(refs2.addressFromUrl.mock.calls).toEqual([
["http://foo.bar/miss"],
]);
expect(refs3.addressFromUrl.mock.calls).toEqual([
["http://foo.bar/miss"],
]);
expect(n1).toEqual(undefined);
});
it("should return the first ReferenceDetector's value that provides a hit", () => {
// Given
const refs1 = exampleDetector([["http://foo.bar/1", nodeA]]);
const refs2 = exampleDetector([
["http://foo.bar/1", nodeB],
["http://foo.bar/2", nodeB],
]);
const refs3 = exampleDetector([
["http://foo.bar/1", nodeC],
["http://foo.bar/2", nodeC],
["http://foo.bar/3", nodeC],
]);
// When
const refs = new CascadingReferenceDetector([refs1, refs2, refs3]);
const n1 = refs.addressFromUrl("http://foo.bar/1");
const n2 = refs.addressFromUrl("http://foo.bar/2");
const n3 = refs.addressFromUrl("http://foo.bar/3");
// Then
expect(refs1.addressFromUrl.mock.calls).toEqual([
["http://foo.bar/1"],
["http://foo.bar/2"],
["http://foo.bar/3"],
]);
expect(refs2.addressFromUrl.mock.calls).toEqual([
["http://foo.bar/2"],
["http://foo.bar/3"],
]);
expect(refs3.addressFromUrl.mock.calls).toEqual([["http://foo.bar/3"]]);
expect(n1).toEqual(nodeA);
expect(n2).toEqual(nodeB);
expect(n3).toEqual(nodeC);
});
});
});

View File

@ -2,3 +2,4 @@
export type {URL, ReferenceDetector} from "./referenceDetector";
export {MappedReferenceDetector} from "./mappedReferenceDetector";
export {CascadingReferenceDetector} from "./cascadingReferenceDetector";