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