Initiatives: replace trackers with InitiativeIds (#1647)
"Trackers" were an idea to let Initiatives be aware of the medium that declares it. Such as a Discourse topic or file. With Discourse in mind this was really useful. We could add an automatic contribution edge, enhance reference detection to "upgrade" a URL pointing to that Topic to resolve to the Initiative instead, etc. Using files as the only source of Initiatives this becomes less relevant. So in the interest of reducing complexity, we'll remove tracker awareness.
This commit is contained in:
parent
8eb9312277
commit
84e658143e
|
@ -11,8 +11,8 @@ import {
|
|||
} from "../../core/graph";
|
||||
import type {ReferenceDetector, URL} from "../../core/references";
|
||||
import type {Initiative, InitiativeRepository} from "./initiative";
|
||||
import {addressFromId} from "./initiative";
|
||||
import {
|
||||
initiativeNodeType,
|
||||
dependsOnEdgeType,
|
||||
referencesEdgeType,
|
||||
contributesToEdgeType,
|
||||
|
@ -20,10 +20,7 @@ import {
|
|||
} from "./declaration";
|
||||
|
||||
function initiativeAddress(initiative: Initiative): NodeAddressT {
|
||||
return NodeAddress.append(
|
||||
initiativeNodeType.prefix,
|
||||
...NodeAddress.toParts(initiative.tracker)
|
||||
);
|
||||
return addressFromId(initiative.id);
|
||||
}
|
||||
|
||||
function initiativeNode(initiative: Initiative): Node {
|
||||
|
@ -72,9 +69,6 @@ export function createGraph(
|
|||
// Adds the Initiative node.
|
||||
graph.addNode(initiativeNode(initiative));
|
||||
|
||||
// Consider the tracker a contribution.
|
||||
graph.addEdge(contributionEdge(initiative, initiative.tracker));
|
||||
|
||||
// Generic approach to adding edges when the reference detector has a hit.
|
||||
const edgeHandler = (
|
||||
urls: $ReadOnlyArray<URL>,
|
||||
|
|
|
@ -8,7 +8,7 @@ import {
|
|||
} from "../../core/graph";
|
||||
import type {ReferenceDetector, URL} from "../../core/references";
|
||||
import type {Initiative, InitiativeRepository} from "./initiative";
|
||||
import {topicAddress} from "../discourse/address";
|
||||
import {createId} from "./initiative";
|
||||
import {createGraph} from "./createGraph";
|
||||
import {
|
||||
initiativeNodeType,
|
||||
|
@ -32,10 +32,10 @@ class MockInitiativeRepository implements InitiativeRepository {
|
|||
this._counter++;
|
||||
|
||||
const initiative: Initiative = {
|
||||
id: createId("TEST_SUBTYPE", String(num)),
|
||||
title: `Example Initiative ${num}`,
|
||||
timestampMs: 400 + num,
|
||||
completed: false,
|
||||
tracker: topicAddress("https://example.com", num),
|
||||
dependencies: [],
|
||||
references: [],
|
||||
contributions: [],
|
||||
|
@ -80,10 +80,11 @@ function exampleNodeAddress(id: number): NodeAddressT {
|
|||
return NodeAddress.fromParts(["example", String(id)]);
|
||||
}
|
||||
|
||||
function discourseInitiativeAddress(id: number): NodeAddressT {
|
||||
function testInitiativeAddress(num: number): NodeAddressT {
|
||||
return NodeAddress.append(
|
||||
initiativeNodeType.prefix,
|
||||
...NodeAddress.toParts(topicAddress("https://example.com", id))
|
||||
"TEST_SUBTYPE",
|
||||
String(num)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -124,40 +125,12 @@ describe("plugins/initiatives/createGraph", () => {
|
|||
{
|
||||
description: "Example Initiative 1",
|
||||
timestampMs: 401,
|
||||
address: discourseInitiativeAddress(1),
|
||||
address: testInitiativeAddress(1),
|
||||
},
|
||||
{
|
||||
description: "Example Initiative 2",
|
||||
timestampMs: 402,
|
||||
address: discourseInitiativeAddress(2),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it("should add the tracker as a contribution edge", () => {
|
||||
// Given
|
||||
const {repo, refs} = example();
|
||||
const i1 = repo.addInitiative();
|
||||
|
||||
// When
|
||||
const graph = createGraph(repo, refs);
|
||||
|
||||
// Then
|
||||
const contributions = Array.from(
|
||||
graph.edges({
|
||||
addressPrefix: contributesToEdgeType.prefix,
|
||||
showDangling: true,
|
||||
})
|
||||
);
|
||||
expect(contributions).toEqual([
|
||||
{
|
||||
address: contributionEdgeAddress(
|
||||
discourseInitiativeAddress(1),
|
||||
i1.tracker
|
||||
),
|
||||
dst: discourseInitiativeAddress(1),
|
||||
src: i1.tracker,
|
||||
timestampMs: i1.timestampMs,
|
||||
address: testInitiativeAddress(2),
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
@ -251,10 +224,10 @@ describe("plugins/initiatives/createGraph", () => {
|
|||
expect(dependencies).toHaveLength(1);
|
||||
expect(dependencies).toContainEqual({
|
||||
address: dependencyEdgeAddress(
|
||||
discourseInitiativeAddress(1),
|
||||
testInitiativeAddress(1),
|
||||
exampleNodeAddress(1)
|
||||
),
|
||||
src: discourseInitiativeAddress(1),
|
||||
src: testInitiativeAddress(1),
|
||||
dst: exampleNodeAddress(1),
|
||||
timestampMs: 401,
|
||||
});
|
||||
|
@ -282,10 +255,10 @@ describe("plugins/initiatives/createGraph", () => {
|
|||
expect(references).toHaveLength(1);
|
||||
expect(references).toContainEqual({
|
||||
address: referenceEdgeAddress(
|
||||
discourseInitiativeAddress(1),
|
||||
testInitiativeAddress(1),
|
||||
exampleNodeAddress(2)
|
||||
),
|
||||
src: discourseInitiativeAddress(1),
|
||||
src: testInitiativeAddress(1),
|
||||
dst: exampleNodeAddress(2),
|
||||
timestampMs: 401,
|
||||
});
|
||||
|
@ -310,14 +283,14 @@ describe("plugins/initiatives/createGraph", () => {
|
|||
})
|
||||
);
|
||||
expect(refs.addressFromUrl).toHaveBeenCalledTimes(2);
|
||||
expect(contributions).toHaveLength(2);
|
||||
expect(contributions).toHaveLength(1);
|
||||
expect(contributions).toContainEqual({
|
||||
address: contributionEdgeAddress(
|
||||
discourseInitiativeAddress(1),
|
||||
testInitiativeAddress(1),
|
||||
exampleNodeAddress(3)
|
||||
),
|
||||
src: exampleNodeAddress(3),
|
||||
dst: discourseInitiativeAddress(1),
|
||||
dst: testInitiativeAddress(1),
|
||||
timestampMs: 401,
|
||||
});
|
||||
});
|
||||
|
@ -344,11 +317,11 @@ describe("plugins/initiatives/createGraph", () => {
|
|||
expect(champions).toHaveLength(1);
|
||||
expect(champions).toContainEqual({
|
||||
address: championEdgeAddress(
|
||||
discourseInitiativeAddress(1),
|
||||
testInitiativeAddress(1),
|
||||
exampleNodeAddress(4)
|
||||
),
|
||||
src: exampleNodeAddress(4),
|
||||
dst: discourseInitiativeAddress(1),
|
||||
dst: testInitiativeAddress(1),
|
||||
timestampMs: 401,
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
|
||||
import type {Topic, Post, CategoryId, TopicId} from "../discourse/fetch";
|
||||
import type {Initiative, URL, InitiativeRepository} from "./initiative";
|
||||
import {createId} from "./initiative";
|
||||
import {
|
||||
parseCookedHtml,
|
||||
type HtmlTemplateInitiativePartial,
|
||||
} from "./htmlTemplate";
|
||||
import {topicAddress} from "../discourse/address";
|
||||
|
||||
export const DISCOURSE_TOPIC_SUBTYPE = "DISCOURSE_TOPIC";
|
||||
|
||||
/**
|
||||
* A subset of queries we need for our plugin.
|
||||
|
@ -130,11 +132,10 @@ export function initiativeFromDiscourseTracker(
|
|||
);
|
||||
}
|
||||
|
||||
const tracker = topicAddress(serverUrl, topic.id);
|
||||
const partial = parseCookedHtml(openingPost.cooked);
|
||||
return {
|
||||
id: createId(DISCOURSE_TOPIC_SUBTYPE, serverUrl, String(topic.id)),
|
||||
title,
|
||||
tracker,
|
||||
timestampMs,
|
||||
completed: partial.completed,
|
||||
dependencies: absoluteURLs(serverUrl, partial.dependencies),
|
||||
|
|
|
@ -6,10 +6,8 @@ import {
|
|||
DiscourseInitiativeRepository,
|
||||
type DiscourseQueries,
|
||||
} from "./discourse";
|
||||
import {type Initiative} from "./initiative";
|
||||
import type {ReadRepository} from "../discourse/mirrorRepository";
|
||||
import type {Topic, Post, CategoryId, TopicId} from "../discourse/fetch";
|
||||
import {NodeAddress} from "../../core/graph";
|
||||
import dedent from "../../util/dedent";
|
||||
|
||||
function givenParseError(message: string) {
|
||||
|
@ -28,13 +26,6 @@ function mockParseCookedHtml(
|
|||
return jest.fn().mockImplementation(fn);
|
||||
}
|
||||
|
||||
function snapshotInitiative(initiative: Initiative): Object {
|
||||
return {
|
||||
...initiative,
|
||||
tracker: NodeAddress.toParts(initiative.tracker),
|
||||
};
|
||||
}
|
||||
|
||||
function exampleTopic(overrides?: $Shape<Topic>): Topic {
|
||||
return {
|
||||
id: 123,
|
||||
|
@ -241,43 +232,39 @@ describe("plugins/initiatives/discourse", () => {
|
|||
const initiatives = repo.initiatives();
|
||||
|
||||
// Then
|
||||
expect(initiatives.map(snapshotInitiative)).toMatchInlineSnapshot(`
|
||||
expect(initiatives).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
Object {
|
||||
"champions": Array [],
|
||||
"completed": false,
|
||||
"contributions": Array [],
|
||||
"dependencies": Array [],
|
||||
"id": Array [
|
||||
"DISCOURSE_TOPIC",
|
||||
"https://foo.bar",
|
||||
"40",
|
||||
],
|
||||
"references": Array [
|
||||
"https://example.org/references/included",
|
||||
],
|
||||
"timestampMs": 1571498171951,
|
||||
"title": "Example initiative",
|
||||
"tracker": Array [
|
||||
"sourcecred",
|
||||
"discourse",
|
||||
"topic",
|
||||
"https://foo.bar",
|
||||
"40",
|
||||
],
|
||||
},
|
||||
Object {
|
||||
"champions": Array [],
|
||||
"completed": false,
|
||||
"contributions": Array [],
|
||||
"dependencies": Array [],
|
||||
"id": Array [
|
||||
"DISCOURSE_TOPIC",
|
||||
"https://foo.bar",
|
||||
"42",
|
||||
],
|
||||
"references": Array [
|
||||
"https://example.org/references/included",
|
||||
],
|
||||
"timestampMs": 1571498171951,
|
||||
"title": "Example initiative",
|
||||
"tracker": Array [
|
||||
"sourcecred",
|
||||
"discourse",
|
||||
"topic",
|
||||
"https://foo.bar",
|
||||
"42",
|
||||
],
|
||||
},
|
||||
]
|
||||
`);
|
||||
|
@ -397,7 +384,7 @@ describe("plugins/initiatives/discourse", () => {
|
|||
expect(initiative.timestampMs).toEqual(firstPost.timestampMs);
|
||||
});
|
||||
|
||||
it("derives the tracker address from topic ID", () => {
|
||||
it("derives the id from topic ID", () => {
|
||||
// Given
|
||||
const serverUrl = "https://foo.bar";
|
||||
const topic = exampleTopic({
|
||||
|
@ -418,10 +405,8 @@ describe("plugins/initiatives/discourse", () => {
|
|||
);
|
||||
|
||||
// Then
|
||||
expect(NodeAddress.toParts(initiative.tracker)).toEqual([
|
||||
"sourcecred",
|
||||
"discourse",
|
||||
"topic",
|
||||
expect(initiative.id).toEqual([
|
||||
"DISCOURSE_TOPIC",
|
||||
serverUrl,
|
||||
String(topic.id),
|
||||
]);
|
||||
|
|
|
@ -36,10 +36,10 @@ export function addressFromId(id: InitiativeId): NodeAddressT {
|
|||
* See https://discourse.sourcecred.io/t/write-the-initiatives-plugin/269/6
|
||||
*/
|
||||
export type Initiative = {|
|
||||
+id: InitiativeId,
|
||||
+title: string,
|
||||
+timestampMs: number,
|
||||
+completed: boolean,
|
||||
+tracker: NodeAddressT,
|
||||
+dependencies: $ReadOnlyArray<URL>,
|
||||
+references: $ReadOnlyArray<URL>,
|
||||
+contributions: $ReadOnlyArray<URL>,
|
||||
|
|
Loading…
Reference in New Issue