mirror of
https://github.com/status-im/sourcecred.git
synced 2025-01-26 12:30:20 +00:00
Initiatives: add normalizeNodeEntry implementation (#1754)
This is where most flexibility when hand-writing JSON files is expected to come from. As it makes few assumptions about the formatting but the internal normalized type is still consistent.
This commit is contained in:
parent
d426eb7f06
commit
e6a988b1a5
@ -3,6 +3,7 @@
|
||||
import {type URL} from "../../core/references";
|
||||
import {type NodeWeight} from "../../core/weights";
|
||||
import {type TimestampMs, type TimestampISO} from "../../util/timestamp";
|
||||
import * as Timestamp from "../../util/timestamp";
|
||||
|
||||
/**
|
||||
* Represents an "inline contribution" node. They're called entries and named
|
||||
@ -38,8 +39,53 @@ export type NodeEntryJson = $Shape<{
|
||||
// Key defaults to a url-friendly-slug of the title. Override it if you need
|
||||
// to preserve a specific NodeAddress, or the slug produces duplicate keys.
|
||||
+key: string,
|
||||
// Defaults to an empty array.
|
||||
+contributors: $ReadOnlyArray<URL>,
|
||||
// Timestamp of this node, but in ISO format as it's more human friendly.
|
||||
+timestampIso: TimestampISO,
|
||||
// Defaults to null.
|
||||
+weight: NodeWeight | null,
|
||||
}>;
|
||||
|
||||
/**
|
||||
* Takes a NodeEntryJson and normalizes it to a NodeEntry.
|
||||
*
|
||||
* Will throw when required fields are missing. Otherwise handles default
|
||||
* values and converting ISO timestamps.
|
||||
*/
|
||||
export function normalizeNodeEntry(
|
||||
input: NodeEntryJson,
|
||||
defaultTimestampMs: TimestampMs
|
||||
): NodeEntry {
|
||||
if (!input.title) {
|
||||
throw new TypeError(
|
||||
`Title is required for an entry, received ${JSON.stringify(input)}`
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
key: input.key || _titleSlug(input.title),
|
||||
title: input.title,
|
||||
timestampMs: input.timestampIso
|
||||
? Timestamp.fromISO(input.timestampIso)
|
||||
: defaultTimestampMs,
|
||||
contributors: input.contributors || [],
|
||||
weight: input.weight || null,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a url-friendly-slug from the title of a NodeEntry. Useful for
|
||||
* generating a default key.
|
||||
*
|
||||
* Note: keys are not required to meet the formatting rules of this slug,
|
||||
* this is mostly for predictability and convenience of NodeAddresses.
|
||||
*/
|
||||
export function _titleSlug(title: string): string {
|
||||
return String(title)
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9-_]+/g, "-")
|
||||
.replace(/--+/g, "-")
|
||||
.replace(/^-/, "")
|
||||
.replace(/-$/, "");
|
||||
}
|
||||
|
110
src/plugins/initiatives/nodeEntry.test.js
Normal file
110
src/plugins/initiatives/nodeEntry.test.js
Normal file
@ -0,0 +1,110 @@
|
||||
// @flow
|
||||
|
||||
import {type TimestampMs} from "../../util/timestamp";
|
||||
import * as Timestamp from "../../util/timestamp";
|
||||
import {
|
||||
type NodeEntry,
|
||||
type NodeEntryJson,
|
||||
normalizeNodeEntry,
|
||||
_titleSlug,
|
||||
} from "./nodeEntry";
|
||||
|
||||
describe("plugins/initiatives/nodeEntry", () => {
|
||||
describe("normalizeNodeEntry", () => {
|
||||
it("should throw without a title", () => {
|
||||
const timestampMs: TimestampMs = Timestamp.fromNumber(123);
|
||||
const entry: NodeEntryJson = {key: "no-title"};
|
||||
const f = () => normalizeNodeEntry(entry, timestampMs);
|
||||
expect(f).toThrow(TypeError);
|
||||
});
|
||||
|
||||
it("should handle a minimal entry", () => {
|
||||
const timestampMs: TimestampMs = Timestamp.fromNumber(123);
|
||||
const entry: NodeEntryJson = {title: "Most minimal"};
|
||||
const expected: NodeEntry = {
|
||||
title: "Most minimal",
|
||||
key: "most-minimal",
|
||||
contributors: [],
|
||||
timestampMs,
|
||||
weight: null,
|
||||
};
|
||||
expect(normalizeNodeEntry(entry, timestampMs)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("should handle an entry with weights", () => {
|
||||
const timestampMs: TimestampMs = Timestamp.fromNumber(123);
|
||||
const entry: NodeEntryJson = {title: "Include weight", weight: 42};
|
||||
const expected: NodeEntry = {
|
||||
title: "Include weight",
|
||||
key: "include-weight",
|
||||
contributors: [],
|
||||
timestampMs,
|
||||
weight: 42,
|
||||
};
|
||||
expect(normalizeNodeEntry(entry, timestampMs)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("should handle an entry with contributors", () => {
|
||||
const timestampMs: TimestampMs = Timestamp.fromNumber(123);
|
||||
const entry: NodeEntryJson = {
|
||||
title: "Include contributors",
|
||||
contributors: ["https://foo.bar/u/abc"],
|
||||
};
|
||||
const expected: NodeEntry = {
|
||||
title: "Include contributors",
|
||||
key: "include-contributors",
|
||||
contributors: ["https://foo.bar/u/abc"],
|
||||
timestampMs,
|
||||
weight: null,
|
||||
};
|
||||
expect(normalizeNodeEntry(entry, timestampMs)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("should handle an entry with timestamp", () => {
|
||||
const timestampMs: TimestampMs = Timestamp.fromNumber(123);
|
||||
const entry: NodeEntryJson = {
|
||||
title: "Include timestamp",
|
||||
timestampIso: Timestamp.toISO(Date.parse("2018-02-03T12:34:56.789Z")),
|
||||
};
|
||||
const expected: NodeEntry = {
|
||||
title: "Include timestamp",
|
||||
key: "include-timestamp",
|
||||
contributors: [],
|
||||
timestampMs: Timestamp.fromNumber(
|
||||
Date.parse("2018-02-03T12:34:56.789Z")
|
||||
),
|
||||
weight: null,
|
||||
};
|
||||
expect(normalizeNodeEntry(entry, timestampMs)).toEqual(expected);
|
||||
});
|
||||
|
||||
it("should handle an entry with key", () => {
|
||||
const timestampMs: TimestampMs = Timestamp.fromNumber(123);
|
||||
const entry: NodeEntryJson = {
|
||||
title: "Include key",
|
||||
key: "much-different-key",
|
||||
};
|
||||
const expected: NodeEntry = {
|
||||
title: "Include key",
|
||||
key: "much-different-key",
|
||||
contributors: [],
|
||||
timestampMs,
|
||||
weight: null,
|
||||
};
|
||||
expect(normalizeNodeEntry(entry, timestampMs)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("_titleSlug", () => {
|
||||
it("should handle example titles", () => {
|
||||
const expected: {[string]: string} = {
|
||||
"should-be-lowercased": "Should-be-LowerCased",
|
||||
"special-characters-as-dashes": "Special@$Characters #As$dashes",
|
||||
"no-starting-trailing-dashes": "-No starting / trailing dashes-",
|
||||
"no-duplicate-dashes": "No - Duplicate -%- Dashes",
|
||||
};
|
||||
const actual = Object.keys(expected).map((k) => _titleSlug(expected[k]));
|
||||
expect(actual).toEqual(Object.keys(expected));
|
||||
});
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user