Cred explorer: display commit short hash + summary (#879)
This modifies how commits are displayed in the cred explorer. Rather than printing the full hash, we now print a short hash followed by the summary. Test plan: Snapshot is updated, also I tested it by running SourceCred on a real repository.
This commit is contained in:
parent
09ed51ed6e
commit
1e5f728e29
|
@ -1,6 +1,7 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
- Display short hash + summary for commits (#879)
|
||||||
- Hyperlink to GitHub entities (#860)
|
- Hyperlink to GitHub entities (#860)
|
||||||
- Add GitHub reactions to the graph (#846)
|
- Add GitHub reactions to the graph (#846)
|
||||||
- Detect references to commits (#833)
|
- Detect references to commits (#833)
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`plugins/git/render commit snapshots as expected 1`] = `"commit 3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f"`;
|
exports[`plugins/git/render commit snapshots as expected 1`] = `"3715ddf: This is an example commit"`;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import * as E from "./edges";
|
||||||
import {description} from "./render";
|
import {description} from "./render";
|
||||||
import type {Assets} from "../../app/assets";
|
import type {Assets} from "../../app/assets";
|
||||||
import type {Repo} from "../../core/repo";
|
import type {Repo} from "../../core/repo";
|
||||||
|
import type {Repository} from "./types";
|
||||||
|
|
||||||
export class StaticPluginAdapter implements IStaticPluginAdapter {
|
export class StaticPluginAdapter implements IStaticPluginAdapter {
|
||||||
name() {
|
name() {
|
||||||
|
@ -42,23 +43,38 @@ export class StaticPluginAdapter implements IStaticPluginAdapter {
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
async load(assets: Assets, repo: Repo): Promise<IDynamicPluginAdapter> {
|
async load(assets: Assets, repo: Repo): Promise<IDynamicPluginAdapter> {
|
||||||
const url = assets.resolve(
|
const baseUrl = `/api/v1/data/data/${repo.owner}/${repo.name}/git/`;
|
||||||
`/api/v1/data/data/${repo.owner}/${repo.name}/git/graph.json`
|
async function loadGraph() {
|
||||||
);
|
const url = assets.resolve(baseUrl + "graph.json");
|
||||||
const response = await fetch(url);
|
const response = await fetch(url);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
return Promise.reject(response);
|
return Promise.reject(response);
|
||||||
|
}
|
||||||
|
const json = await response.json();
|
||||||
|
return Graph.fromJSON(json);
|
||||||
}
|
}
|
||||||
const json = await response.json();
|
async function loadRepository(): Promise<Repository> {
|
||||||
const graph = Graph.fromJSON(json);
|
const url = assets.resolve(baseUrl + "repository.json");
|
||||||
return new DynamicPluginAdapter(graph);
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) {
|
||||||
|
return Promise.reject(response);
|
||||||
|
}
|
||||||
|
return await response.json();
|
||||||
|
}
|
||||||
|
const [graph, repository] = await Promise.all([
|
||||||
|
loadGraph(),
|
||||||
|
loadRepository(),
|
||||||
|
]);
|
||||||
|
return new DynamicPluginAdapter(graph, repository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DynamicPluginAdapter implements IDynamicPluginAdapter {
|
class DynamicPluginAdapter implements IDynamicPluginAdapter {
|
||||||
+_graph: Graph;
|
+_graph: Graph;
|
||||||
constructor(graph: Graph) {
|
+_repository: Repository;
|
||||||
|
constructor(graph: Graph, repository: Repository) {
|
||||||
this._graph = graph;
|
this._graph = graph;
|
||||||
|
this._repository = repository;
|
||||||
}
|
}
|
||||||
graph() {
|
graph() {
|
||||||
return this._graph;
|
return this._graph;
|
||||||
|
@ -67,7 +83,7 @@ class DynamicPluginAdapter implements IDynamicPluginAdapter {
|
||||||
// This cast is unsound, and might throw at runtime, but won't have
|
// This cast is unsound, and might throw at runtime, but won't have
|
||||||
// silent failures or cause problems down the road.
|
// silent failures or cause problems down the road.
|
||||||
const address = N.fromRaw((node: any));
|
const address = N.fromRaw((node: any));
|
||||||
return description(address);
|
return description(address, this._repository);
|
||||||
}
|
}
|
||||||
static() {
|
static() {
|
||||||
return new StaticPluginAdapter();
|
return new StaticPluginAdapter();
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import * as N from "./nodes";
|
import * as N from "./nodes";
|
||||||
|
import type {Repository} from "./types";
|
||||||
|
|
||||||
export function description(address: N.StructuredAddress) {
|
export function description(
|
||||||
|
address: N.StructuredAddress,
|
||||||
|
repository: Repository
|
||||||
|
) {
|
||||||
switch (address.type) {
|
switch (address.type) {
|
||||||
case "COMMIT":
|
case "COMMIT": {
|
||||||
return `commit ${address.hash}`;
|
const hash = address.hash;
|
||||||
case "TREE":
|
const commit = repository.commits[hash];
|
||||||
return `tree ${address.hash}`;
|
if (commit == null) {
|
||||||
case "BLOB":
|
console.error(`Unable to find data for commit ${hash}`);
|
||||||
return `blob ${address.hash}`;
|
return hash;
|
||||||
case "TREE_ENTRY":
|
}
|
||||||
return `entry ${JSON.stringify(address.name)} in tree ${
|
const {shortHash, summary} = commit;
|
||||||
address.treeHash
|
return `${shortHash}: ${summary}`;
|
||||||
}`;
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error(`unknown type: ${(address.type: empty)}`);
|
throw new Error(`unknown type: ${(address.type: empty)}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,15 +2,35 @@
|
||||||
|
|
||||||
import * as GN from "./nodes";
|
import * as GN from "./nodes";
|
||||||
import {description} from "./render";
|
import {description} from "./render";
|
||||||
|
import type {Repository} from "./types";
|
||||||
|
|
||||||
describe("plugins/git/render", () => {
|
describe("plugins/git/render", () => {
|
||||||
const examples = {
|
const exampleHash = "3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f";
|
||||||
commit: (): GN.CommitAddress => ({
|
const exampleCommit: GN.CommitAddress = Object.freeze({
|
||||||
type: GN.COMMIT_TYPE,
|
type: GN.COMMIT_TYPE,
|
||||||
hash: "3715ddfb8d4c4fd2a6f6af75488c82f84c92ec2f",
|
hash: exampleHash,
|
||||||
}),
|
});
|
||||||
};
|
const exampleRepository: Repository = Object.freeze({
|
||||||
|
commits: {
|
||||||
|
[exampleHash]: {
|
||||||
|
hash: exampleHash,
|
||||||
|
shortHash: exampleHash.slice(0, 7),
|
||||||
|
summary: "This is an example commit",
|
||||||
|
parentHashes: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
it("commit snapshots as expected", () => {
|
it("commit snapshots as expected", () => {
|
||||||
expect(description(examples.commit())).toMatchSnapshot();
|
expect(description(exampleCommit, exampleRepository)).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
it("logs an error for a commit not in the repository", () => {
|
||||||
|
const badCommit = {type: GN.COMMIT_TYPE, hash: "1234"};
|
||||||
|
// $ExpectFlowError
|
||||||
|
console.error = jest.fn();
|
||||||
|
expect(description(badCommit, exampleRepository)).toBe("1234");
|
||||||
|
expect(console.error).toHaveBeenCalledWith(
|
||||||
|
"Unable to find data for commit 1234"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue