Skip to content

Commit

Permalink
Set up Grain Integration support in Config API
Browse files Browse the repository at this point in the history
  • Loading branch information
topocount committed Aug 16, 2021
1 parent 3d3eb21 commit 2f68004
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 7 deletions.
1 change: 1 addition & 0 deletions packages/sourcecred/.flowconfig
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<PROJECT_ROOT>/node_modules/webpack-cli/lib/utils/__tests__/.*

[include]
../../grainIntegrations/

[libs]
flow-typed
Expand Down
1 change: 1 addition & 0 deletions packages/sourcecred/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
"prettier": "^2.0.1",
"raf": "^3.4.1",
"react-dev-utils": "^11.0.0",
"sc-grainIntegration-csv": "^0.0.0",
"static-site-generator-webpack-plugin": "^3.4.2",
"url-loader": "^4.0.0",
"webpack": "^4.42.0",
Expand Down
36 changes: 36 additions & 0 deletions packages/sourcecred/src/api/bundledGrainIntegrations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// @flow

import type {GrainIntegrationFunction} from "../core/ledger/grainIntegration";
import * as C from "../util/combo";

import {csvIntegration} from "sc-grainIntegration-csv";

export type GrainIntegration = {|
name: string,
function: GrainIntegrationFunction,
|};

type AllowedDeclarations = {[pluginKey: string]: GrainIntegrationFunction};

const allowedDeclarations: AllowedDeclarations = {
"csv": csvIntegration,
};

export function bundledGrainIntegrations(
integrationKey: string
): GrainIntegrationFunction {
const integration = allowedDeclarations[integrationKey];
if (!integration)
throw new Error(
"grain integration not found; enter a valid `integration` value in config/grain.json"
);
return integration;
}

export const parser: C.Parser<GrainIntegration> = C.fmap(
C.exactly(["csv"]),
(integrationKey) => ({
name: integrationKey,
function: bundledGrainIntegrations(integrationKey),
})
);
14 changes: 14 additions & 0 deletions packages/sourcecred/src/api/bundledGrainIntegrations.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @flow

import {bundledGrainIntegrations} from "./bundledGrainIntegrations";
import {csvIntegration} from "sc-grainIntegration-csv";
describe("api/bundledGrainIntegrations", () => {
it("returns the csv parser", () => {
const result = bundledGrainIntegrations("csv");
expect(result).toBe(csvIntegration);
});
it("errors if integration lookup fails", () => {
const thunk = () => bundledGrainIntegrations("badKey");
expect(thunk).toThrow("grain integration not found");
});
});
9 changes: 8 additions & 1 deletion packages/sourcecred/src/api/currencyConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@

import * as C from "../util/combo";
import * as NullUtil from "../util/null";

import {
type Currency as IntegrationCurrency,
currencyParser as integrationCurrencyParser,
} from "../core/ledger/currency";
/**
* Shape of currencyDetails.json on disk
*/
type SerializedCurrencyDetails = {|
+currencyName?: string,
+currencySuffix?: string,
+decimalsToDisplay?: number,
+integrationCurrency?: IntegrationCurrency,
|};

/**
Expand All @@ -19,6 +23,7 @@ export type CurrencyDetails = {|
+name: string,
+suffix: string,
+decimals: number,
+integrationCurrency?: IntegrationCurrency,
|};

export const DEFAULT_NAME = "Grain";
Expand All @@ -42,6 +47,7 @@ function upgrade(c: SerializedCurrencyDetails): CurrencyDetails {
name: NullUtil.orElse(c.currencyName, DEFAULT_NAME),
suffix: NullUtil.orElse(c.currencySuffix, DEFAULT_SUFFIX),
decimals: NullUtil.orElse(c.decimalsToDisplay, DEFAULT_DECIMALS),
integrationCurrency: c.integrationCurrency,
};
}

Expand All @@ -60,6 +66,7 @@ export const parser: C.Parser<CurrencyDetails> = C.fmap(
currencyName: C.string,
currencySuffix: C.string,
decimalsToDisplay: C.number,
integrationCurrency: integrationCurrencyParser,
}
),
upgrade
Expand Down
11 changes: 11 additions & 0 deletions packages/sourcecred/src/api/grainConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ import {
type NonnegativeGrain,
} from "../core/ledger/nonnegativeGrain";
import {toDiscount} from "../core/ledger/policies/recent";
import {type Name, parser as nameParser} from "../core/identity/name";
import {
type GrainIntegration,
parser as bundledGrainIntegrationParser,
} from "./bundledGrainIntegrations";

export type GrainConfig = {|
+immediatePerWeek?: NonnegativeGrain, // (deprecated)
Expand All @@ -22,6 +27,9 @@ export type GrainConfig = {|
+recentWeeklyDecayRate?: number, // (deprecated)
+allocationPolicies?: $ReadOnlyArray<AllocationPolicy>,
+maxSimultaneousDistributions?: number,
+sinkIdentity?: Name,
+processDistributions?: boolean,
+integration?: GrainIntegration,
|};

export const parser: C.Parser<GrainConfig> = C.object(
Expand All @@ -33,6 +41,9 @@ export const parser: C.Parser<GrainConfig> = C.object(
balancedPerWeek: numberOrFloatStringParser,
recentPerWeek: numberOrFloatStringParser,
recentWeeklyDecayRate: C.number,
sinkIdentity: nameParser,
processDistributions: C.boolean,
integration: bundledGrainIntegrationParser,
}
);

Expand Down
5 changes: 5 additions & 0 deletions packages/sourcecred/src/api/grainConfig.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {type BalancedPolicy} from "../core/ledger/policies/balanced";
import {type ImmediatePolicy} from "../core/ledger/policies/immediate";
import {type RecentPolicy} from "../core/ledger/policies/recent";
import {type SpecialPolicy} from "../core/ledger/policies/special";
import {nameFromString} from "../core/identity/name";

const toNonnegativeGrain = (budget: number | string): NonnegativeGrain => {
if (typeof budget === "string") {
Expand Down Expand Up @@ -141,6 +142,8 @@ describe("api/grainConfig", () => {
},
],
maxSimultaneousDistributions: 2,
sinkIdentity: "testName",
processDistributions: true,
};

const expected: GrainConfig = {
Expand All @@ -151,6 +154,8 @@ describe("api/grainConfig", () => {
special(100, "howdy", uuid),
],
maxSimultaneousDistributions: 2,
sinkIdentity: nameFromString("testName"),
processDistributions: true,
};

expect(parser.parseOrThrow(grainConfig)).toEqual(expected);
Expand Down
14 changes: 11 additions & 3 deletions packages/sourcecred/src/core/ledger/grainIntegration.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type IntegrationConfig = {|
// an interface should prompt admin interface users that they haven't
// distributed funds via a configured integration
processDistributions: boolean,
currency: Currency,
|};

/**
Expand All @@ -55,7 +56,10 @@ export type IntegrationConfig = {|
* if accounting is enabled. Otherwise, grain balances will be tracked
* elsewhere.
*/
export type GrainIntegration = (PayoutDistributions, Currency) => PayoutResult;
export type GrainIntegrationFunction = (
PayoutDistributions,
IntegrationConfig
) => PayoutResult;

///////////////////
// Helper functions
Expand All @@ -66,7 +70,7 @@ export type GrainIntegration = (PayoutDistributions, Currency) => PayoutResult;
// sink identity
export function executeGrainIntegration(
ledger: Ledger,
integration: GrainIntegration,
integration: GrainIntegrationFunction,
distribution: Distribution,
currency: Currency,
processDistributions: boolean,
Expand All @@ -85,7 +89,11 @@ export function executeGrainIntegration(
// the fixed-point amount for some reason.
let result;
try {
result = integration(payoutDistributions, currency);
result = integration(payoutDistributions, {
currency,
accountingEnabled,
processDistributions,
});
if (processDistributions) ledger.markDistributionExecuted(distribution.id);
} catch (e) {
throw new Error(`Grain Integration failed: ${e}`);
Expand Down
6 changes: 3 additions & 3 deletions packages/sourcecred/src/core/ledger/grainIntegration.test.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import {
type GrainIntegration,
type GrainIntegrationFunction,
executeGrainIntegration,
} from "./grainIntegration";
import {
Expand Down Expand Up @@ -78,10 +78,10 @@ describe("src/core/ledger/grainIntegration", () => {
currency.chainId
);
});
const getmockIntegration: (?boolean) => GrainIntegration = (
const getmockIntegration: (?boolean) => GrainIntegrationFunction = (
returnTransfers = false,
returnOutput = false
) => (distributions = [], currency) => {
) => (distributions = [], _unused_config) => {
const _ = currency;
const transferResult = distributions.map(([payoutAddress, amount]) => ({
payoutAddress,
Expand Down

0 comments on commit 2f68004

Please sign in to comment.