Add InitiativesDirectory and InitiativeFile types (#1641)
Based on [forum discussion][1], Initiatives should be tracked in files. The main issue with storing the existing Initiative type as JSON in a file, is there's no natural NodeAddress or URL for a file-based tracker. This type resolves that by using the file name within a directory as a unique reference and requiring a remoteUrl for referencing. (See #1640) [1]: https://discourse.sourcecred.io/t/576
This commit is contained in:
parent
84e658143e
commit
f924521fdd
|
@ -0,0 +1,63 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import {type ReferenceDetector} from "../../core/references";
|
||||||
|
import {type Compatible, fromCompat, toCompat} from "../../util/compat";
|
||||||
|
import {type InitiativeRepository, type URL} from "./initiative";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents an Initiatives directory.
|
||||||
|
*
|
||||||
|
* Initiative directories contain a set of InitiativeFiles in a `*.json` pattern.
|
||||||
|
* Where the file name is the ID of that Initiative.
|
||||||
|
* Additionally we require a `remoteUrl` for this directory. We expect this directory
|
||||||
|
* to be something you can browse online. This allows us to create a ReferenceDetector.
|
||||||
|
*/
|
||||||
|
export type InitiativesDirectory = {|
|
||||||
|
+localPath: string,
|
||||||
|
+remoteUrl: string,
|
||||||
|
|};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opaque because we only want this file's functions to create these load results.
|
||||||
|
* However we do allow anyone to consume it's properties.
|
||||||
|
*/
|
||||||
|
export opaque type LoadedInitiativesDirectory: {|
|
||||||
|
+referenceDetector: ReferenceDetector,
|
||||||
|
+initiatives: InitiativeRepository,
|
||||||
|
|} = {|
|
||||||
|
+referenceDetector: ReferenceDetector,
|
||||||
|
+initiatives: InitiativeRepository,
|
||||||
|
|};
|
||||||
|
|
||||||
|
// Adding below signature to help clarity of this commit.
|
||||||
|
// TODO: @beanow will implement this in a follow-up.
|
||||||
|
type _unused_loadDirectoryFunction = (InitiativesDirectory) => Promise<LoadedInitiativesDirectory>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a single Initiative using a file as source.
|
||||||
|
*
|
||||||
|
* Note: The file name will be used to derive the InitiativeId. So it doesn't
|
||||||
|
* make sense to use this outside of the context of an InitiativesDirectory.
|
||||||
|
*/
|
||||||
|
export type InitiativeFile = {|
|
||||||
|
+title: string,
|
||||||
|
+timestampIso: ISOTimestamp,
|
||||||
|
+completed: boolean,
|
||||||
|
+dependencies: $ReadOnlyArray<URL>,
|
||||||
|
+references: $ReadOnlyArray<URL>,
|
||||||
|
+contributions: $ReadOnlyArray<URL>,
|
||||||
|
+champions: $ReadOnlyArray<URL>,
|
||||||
|
|};
|
||||||
|
|
||||||
|
// Note: setting this to opaque forces us to convert it to timestampMs.
|
||||||
|
opaque type ISOTimestamp = string;
|
||||||
|
|
||||||
|
const COMPAT_INFO = {type: "sourcecred/initiativeFile", version: "0.1.0"};
|
||||||
|
|
||||||
|
export function fromJSON(j: Compatible<any>): InitiativeFile {
|
||||||
|
return fromCompat(COMPAT_INFO, j);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toJSON(m: InitiativeFile): Compatible<InitiativeFile> {
|
||||||
|
return toCompat(COMPAT_INFO, m);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
|
import {type InitiativeFile, fromJSON, toJSON} from "./initiativesDirectory";
|
||||||
|
|
||||||
|
const exampleInitiativeFile = (): InitiativeFile => ({
|
||||||
|
title: "Sample initiative",
|
||||||
|
timestampIso: ("2020-01-08T22:01:57.766Z": any),
|
||||||
|
completed: false,
|
||||||
|
champions: ["http://foo.bar/champ"],
|
||||||
|
contributions: ["http://foo.bar/contrib"],
|
||||||
|
dependencies: ["http://foo.bar/dep"],
|
||||||
|
references: ["http://foo.bar/ref"],
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("plugins/initiatives/initiativesDirectory", () => {
|
||||||
|
describe("toJSON/fromJSON", () => {
|
||||||
|
it("should handle an example round-trip", () => {
|
||||||
|
// Given
|
||||||
|
const initiativeFile = exampleInitiativeFile();
|
||||||
|
|
||||||
|
// When
|
||||||
|
const actual = fromJSON(toJSON(initiativeFile));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
expect(actual).toEqual(initiativeFile);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue