Skip to content

Commit

Permalink
move redactedMoves into a boolean option in the long form move syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
nicolodavis committed Sep 10, 2019
1 parent 4b202ee commit 8924e84
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 68 deletions.
10 changes: 0 additions & 10 deletions src/core/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,6 @@ import * as logging from './logger';
* Predicate to determine whether a
* particular move is undoable at this
* time.
*
* @param {Array} redactedMoves - List of moves to be redacted
* from the log.
*/
export function Flow({
ctx,
Expand All @@ -66,7 +63,6 @@ export function Flow({
optimisticUpdate,
canMakeMove,
canUndoMove,
redactedMoves,
moveMap,
}) {
if (!ctx) ctx = () => ({});
Expand Down Expand Up @@ -103,7 +99,6 @@ export function Flow({
ctx,
init,
canUndoMove,
redactedMoves,
moveMap,

eventNames: Object.getOwnPropertyNames(events),
Expand Down Expand Up @@ -191,9 +186,6 @@ export function Flow({
* @param {...object} undoableMoves - List of moves that are undoable,
* (default: null, i.e. all moves are undoable).
*
* @param {Array} redactedMoves - List of moves to be redacted
* from the log.
*
* @param {...object} optimisticUpdate - (G, ctx, move) => boolean
* Control whether a move should
* be executed optimistically on
Expand Down Expand Up @@ -249,7 +241,6 @@ export function FlowWithPhases({
endGame,
setActionPlayers,
undoableMoves,
redactedMoves,
optimisticUpdate,
game,
}) {
Expand Down Expand Up @@ -728,7 +719,6 @@ export function FlowWithPhases({
processMove,
canMakeMove,
canUndoMove,
redactedMoves,
moveMap,
});
}
26 changes: 16 additions & 10 deletions src/core/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,30 +102,36 @@ function Game(game) {
game.flow = FlowWithPhases({ game, ...game.flow });
}

const getMove = name => {
if (name in game.moves) {
return game.moves[name];
}

if (name in game.flow.moveMap) {
return game.flow.moveMap[name];
}

return null;
};

return {
...game,

getMove,

moveNames: [
...Object.getOwnPropertyNames(game.moves),
...Object.keys(game.flow.moveMap || []),
],

processMove: (G, action, ctx) => {
let moveFn = null;

if (game.moves.hasOwnProperty(action.type)) {
moveFn = game.moves[action.type];
}

if (action.type in game.flow.moveMap) {
moveFn = game.flow.moveMap[action.type];
}
let moveFn = getMove(action.type);

if (moveFn instanceof Object && moveFn.impl) {
moveFn = moveFn.impl;
}

if (moveFn !== null) {
if (moveFn instanceof Function) {
const ctxWithPlayerID = { ...ctx, playerID: action.playerID };
const args = [G, ctxWithPlayerID].concat(action.args);
const fn = FnWrap(moveFn, game);
Expand Down
8 changes: 7 additions & 1 deletion src/core/reducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -273,14 +273,20 @@ export function CreateGameReducer({ game, multiplayer }) {
return state;
}

const move = game.getMove(action.payload.type);

// Create a log entry for this move.
const logEntry = {
let logEntry = {
action,
_stateID: state._stateID,
turn: state.ctx.turn,
phase: state.ctx.phase,
};

if (move.redact === true) {
logEntry.redact = true;
}

// don't call into events here
const newState = apiCtx.updateAndDetach(
{ ...state, deltalog: [logEntry] },
Expand Down
43 changes: 17 additions & 26 deletions src/master/master.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,37 @@ const GameMetadataKey = gameID => `${gameID}:metadata`;
/**
* Redact the log.
*
* @param {Array} redactedMoves - List of moves to redact.
* @param {Array} log - The game log (or deltalog).
* @param {String} playerID - The playerID that this log is
* to be sent to.
*/
export function redactLog(redactedMoves, log, playerID) {
if (redactedMoves === undefined || log === undefined) {
export function redactLog(log, playerID) {
if (log === undefined) {
return log;
}

return log.map(logEvent => {
// filter for all other players and a spectator
// filter for all other players and spectators.
if (playerID !== null && +playerID === +logEvent.action.payload.playerID) {
return logEvent;
}

// only filter moves
if (logEvent.action.type !== 'MAKE_MOVE') {
if (logEvent.redact !== true) {
return logEvent;
}

const moveName = logEvent.action.payload.type;
let filteredEvent = logEvent;
if (redactedMoves.includes(moveName)) {
const newPayload = {
...filteredEvent.action.payload,
args: undefined,
argsRedacted: true,
};
filteredEvent = {
...filteredEvent,
action: { ...filteredEvent.action, payload: newPayload },
};
}
const payload = {
...logEvent.action.payload,
args: null,
};
const filteredEvent = {
...logEvent,
action: { ...logEvent.action, payload },
};

return filteredEvent;
/* eslint-disable-next-line no-unused-vars */
const { redact, ...remaining } = filteredEvent;
return remaining;
});
}

Expand Down Expand Up @@ -227,11 +222,7 @@ export class Master {
},
};

const log = redactLog(
this.game.flow.redactedMoves,
state.deltalog,
playerID
);
const log = redactLog(state.deltalog, playerID);

return {
type: 'update',
Expand Down Expand Up @@ -302,7 +293,7 @@ export class Master {
},
};

const log = redactLog(this.game.flow.redactedMoves, state.log, playerID);
const log = redactLog(state.log, playerID);

this.transportAPI.send({
playerID,
Expand Down
45 changes: 24 additions & 21 deletions src/master/master.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,29 +312,30 @@ describe('authentication', () => {

describe('redactLog', () => {
test('no redactedMoves', () => {
const logEvents = [ActionCreators.gameEvent('endTurn')];
const result = redactLog(undefined, logEvents, '0');
const logEvents = [{ action: ActionCreators.gameEvent('endTurn') }];
const result = redactLog(logEvents, '0');
expect(result).toMatchObject(logEvents);
});

test('redacted move is only shown with args to the player that made the move', () => {
const rm = ['clickCell'];
const logEvents = [
{ action: ActionCreators.makeMove('clickCell', [1, 2, 3], '0') },
{
action: ActionCreators.makeMove('clickCell', [1, 2, 3], '0'),
redact: true,
},
];

// player that made the move
let result = redactLog(rm, logEvents, '0');
let result = redactLog(logEvents, '0');
expect(result).toMatchObject(logEvents);

// other player
result = redactLog(rm, logEvents, '1');
result = redactLog(logEvents, '1');
expect(result).toMatchObject([
{
action: {
type: 'MAKE_MOVE',
payload: {
argsRedacted: true,
credentials: undefined,
playerID: '0',
type: 'clickCell',
Expand All @@ -345,53 +346,55 @@ describe('redactLog', () => {
});

test('not redacted move is shown to all', () => {
const rm = ['clickCell'];
const logEvents = [
{ action: ActionCreators.makeMove('unclickCell', [1, 2, 3], '0') },
];

// player that made the move
let result = redactLog(rm, logEvents, '0');
let result = redactLog(logEvents, '0');
expect(result).toMatchObject(logEvents);
// other player
result = redactLog(rm, logEvents, '1');
result = redactLog(logEvents, '1');
expect(result).toMatchObject(logEvents);
});

test('can explicitly set showing args to true', () => {
const rm = [];
const logEvents = [
{ action: ActionCreators.makeMove('unclickCell', [1, 2, 3], '0') },
];

// player that made the move
let result = redactLog(rm, logEvents, '0');
let result = redactLog(logEvents, '0');
expect(result).toMatchObject(logEvents);
// other player
result = redactLog(rm, logEvents, '1');
result = redactLog(logEvents, '1');
expect(result).toMatchObject(logEvents);
});

test('events are not redacted', () => {
const rm = ['clickCell'];
const logEvents = [{ action: ActionCreators.gameEvent('endTurn') }];

// player that made the move
let result = redactLog(rm, logEvents, '0');
let result = redactLog(logEvents, '0');
expect(result).toMatchObject(logEvents);
// other player
result = redactLog(rm, logEvents, '1');
result = redactLog(logEvents, '1');
expect(result).toMatchObject(logEvents);
});

test('make sure sync redacts the log', async () => {
const game2 = Game({
moves: { A: G => G, B: G => G },
flow: { redactedMoves: ['B'] },
const game = Game({
moves: {
A: G => G,
B: {
impl: G => G,
redact: true,
},
},
});

const send = jest.fn();
const master = new Master(game2, new InMemory(), TransportAPI(send));
const master = new Master(game, new InMemory(), TransportAPI(send));

const actionA = ActionCreators.makeMove('A', ['not redacted']);
const actionB = ActionCreators.makeMove('B', ['redacted']);
Expand Down Expand Up @@ -419,7 +422,7 @@ describe('redactLog', () => {
type: 'MAKE_MOVE',
payload: {
type: 'B',
argsRedacted: true,
args: null,
},
},
_stateID: 1,
Expand Down

0 comments on commit 8924e84

Please sign in to comment.