Add RelationalView.{to,from}JSON (#443)

This adds methods for serializing the GitHub RelationalView.

We have not put in the work to ensure that these methods generate
canonical data. Getting the issues in a different order, or finding
references in a different order, can change the JSON output even if the
resulting repositories are equivalent.

@decentralion think it's not worth putting in the effort, since we may
switch to a SQL database soon anyway.

Test plan: travis

Paired with @wchargin
This commit is contained in:
Dandelion Mané 2018-06-28 18:39:31 -07:00 committed by GitHub
parent 64df5b09c3
commit 6356c5477f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 76 additions and 0 deletions

View File

@ -1,5 +1,6 @@
// @flow
import {toCompat, fromCompat, type Compatible} from "../../util/compat";
import stringify from "json-stable-stringify";
import {parseReferences} from "./parseReferences";
import * as N from "./nodes";
@ -22,6 +23,11 @@ import {
reviewCommentUrlToId,
} from "./urlIdParse";
const COMPAT_INFO = {
type: "sourcecred/github/relationalView",
version: "0.1.0",
};
export class RelationalView {
_repos: Map<N.RawAddress, RepoEntry>;
_issues: Map<N.RawAddress, IssueEntry>;
@ -164,6 +170,34 @@ export class RelationalView {
yield* this.userlikes();
}
toJSON(): RelationalViewJSON {
const rawJSON = {
repos: addressMapToObject(this._repos),
issues: addressMapToObject(this._issues),
pulls: addressMapToObject(this._pulls),
reviews: addressMapToObject(this._reviews),
comments: addressMapToObject(this._comments),
userlikes: addressMapToObject(this._userlikes),
references: addressMapToObject(this._mapReferences),
referencedBy: addressMapToObject(this._mapReferencedBy),
};
return toCompat(COMPAT_INFO, rawJSON);
}
static fromJSON(compatJson: RelationalViewJSON): RelationalView {
const json = fromCompat(COMPAT_INFO, compatJson);
const rv = new RelationalView();
rv._repos = objectToAddressMap(json.repos);
rv._issues = objectToAddressMap(json.issues);
rv._pulls = objectToAddressMap(json.pulls);
rv._reviews = objectToAddressMap(json.reviews);
rv._comments = objectToAddressMap(json.comments);
rv._userlikes = objectToAddressMap(json.userlikes);
rv._mapReferences = objectToAddressMap(json.references);
rv._mapReferencedBy = objectToAddressMap(json.referencedBy);
return rv;
}
_addRepo(json: Q.RepositoryJSON) {
const address: RepoAddress = {
type: N.REPO_TYPE,
@ -736,3 +770,36 @@ export type TextContentEntity = Issue | Pull | Review | Comment;
export type ParentEntity = Repo | Issue | Pull | Review;
export type ChildEntity = Issue | Pull | Review | Comment;
export type ReferentEntity = Repo | Issue | Pull | Review | Comment | Userlike;
function addressMapToObject<T>(
x: Map<N.RawAddress, T>
): AddressEntryMapJSON<T> {
const result: {[N.RawAddress]: T} = {};
for (const [address, entry] of x.entries()) {
result[address] = entry;
}
return result;
}
function objectToAddressMap<T>(
x: AddressEntryMapJSON<T>
): Map<N.RawAddress, T> {
const result = new Map();
for (const key of Object.keys(x)) {
const address: N.RawAddress = (key: any);
result.set(address, x[address]);
}
return result;
}
export opaque type AddressEntryMapJSON<T> = {[N.RawAddress]: T};
export opaque type RelationalViewJSON = Compatible<{|
+repos: AddressEntryMapJSON<RepoEntry>,
+issues: AddressEntryMapJSON<IssueEntry>,
+pulls: AddressEntryMapJSON<PullEntry>,
+reviews: AddressEntryMapJSON<ReviewEntry>,
+comments: AddressEntryMapJSON<CommentEntry>,
+userlikes: AddressEntryMapJSON<UserlikeEntry>,
+references: AddressEntryMapJSON<N.ReferentAddress[]>,
+referencedBy: AddressEntryMapJSON<N.TextContentAddress[]>,
|}>;

View File

@ -223,4 +223,13 @@ describe("plugins/github/relationalView", () => {
// may be fragile
expect(rv1).toEqual(rv2);
});
describe("to/fromJSON", () => {
it("to->from->to is identity", () => {
const json1 = view.toJSON();
const view1 = R.RelationalView.fromJSON(json1);
const json2 = view1.toJSON();
expect(json1).toEqual(json2);
});
});
});