@Override @SuppressWarnings("unchecked") public <TParams> Command<TState, TParams, TEvent> fromOperation( final Operation<? super TState, ? super TParams, ? extends TEvent> operation) { // HACK: We know this case is safe based on how we use these interfaces, having the // wildcard types bubble through everywhere makes the code very ugly however - this is the // point where we convert from operation types that may use other concrete types for state // and parameters, into the one we know is applicable to our aggregate repository. Operation<TState, TParams, TEvent> typeHackedOperation = (Operation<TState, TParams, TEvent>) operation; Command<TState, TParams, TEvent> command = new DefaultCommand<>(repository, typeHackedOperation); for (CommandPostProcessor postProcessor : postProcessors) { postProcessor.postProcessCommand(command); } return command; } }
public Command<TState, TParams, TEvent> validate() { if (aggregateId == null) { throw new InvalidCommandException("No aggregate id specified"); } if (operation.requiresArguments() && arguments == null) { throw new InvalidCommandException( "No arguments specified to command that requires arguments"); } // Try to calculate effective expected version - will validate combinations getEffectiveExpectedVersion(expectedVersion, operation.expectedVersion()); // TODO: Add more validation! return this; }
@Test @SuppressWarnings("unchecked") public void idempotentCreateWorksIfConflictOnSave() { Operation operation = new OperationHandlerOperation( (x, y) -> ImmutableList.of("hello"), false, false, ExpectedVersion.notCreated()); when(repository.load(any())) .thenReturn(DefaultImmutableAggregate.fromExisting( mock(AggregateProjection.class), AGGREGATE_ID, 42, new TestState("test"))); DefaultCommand command = new DefaultCommand(repository, operation); command.setAggregateId(AGGREGATE_ID); command.setIdempotentCreate(true); command.setAtomic(false); when(repository.append(any(), any(), any(), any())) .thenThrow(new UnexpectedVersionException(42, ExpectedVersion.notCreated())); CommandResult commandResult = command.run(); Assert.assertTrue(commandResult.getEvents().isEmpty()); }
ExpectedVersion.any()); DefaultCommand command = new DefaultCommand(repository, operation); command.addMetadata(ImmutableMap.of("key2", "value2direct")); command.addMetadataDecorator(new MetadataDecorator() { @Override public Map<String, String> getMetadata() { when(repository.append(any(), any(), any(), any())).thenReturn(newVersion); command.setAggregateId(AGGREGATE_ID); command.run();
@Test(expected = InvalidCommandException.class) @SuppressWarnings("unchecked") public void conflictingVersionsGiveInvalidCommand() { PowerMockito.mockStatic(ExpectedVersion.class); PowerMockito.when(ExpectedVersion.merge(any(), any())) .thenThrow(new ConflictingExpectedVersionsException( "error", null, null)); Operation operation = new OperationHandlerOperation( (x, y) -> null, true, false, ExpectedVersion.exactly(42)); DefaultCommand command = new DefaultCommand(repository, operation); command.setAggregateId(AGGREGATE_ID); command.run(); }
@Override public CommandResult<TEvent> run() { logger.debug("Running command on {}", aggregateId); validate(); getEffectiveExpectedVersion(expectedVersion, operation.expectedVersion()); logger.debug("Expected version set as {}", effectiveExpectedVersion); if (operation.requiresState() || atomic) { logger.debug("Reading aggregate record from stream"); aggregate = readAndValidateAggregate(effectiveExpectedVersion); logger.debug( "Current state of aggregate is {}",
@Test @SuppressWarnings("unchecked") public void idempotentCreateMeansNoOpIfExisting() { Operation operation = new OperationHandlerOperation( (x, y) -> ImmutableList.of(new TestEvent("xxx")), false, false, ExpectedVersion.notCreated()); when(repository.load(any())) .thenReturn(DefaultImmutableAggregate.fromExisting( mock(AggregateProjection.class), AGGREGATE_ID, 42, new TestState("test"))); DefaultCommand command = new DefaultCommand(repository, operation); command.setAggregateId(AGGREGATE_ID); command.setIdempotentCreate(true); CommandResult commandResult = command.run(); Assert.assertTrue(commandResult.getEvents().isEmpty()); }
@Test @SuppressWarnings("unchecked") public void aggregateReadWhenStateRequired() { Operation operation = new OperationHandlerOperation( (x, y) -> ImmutableList.of(new TestEvent("test")), true, false, ExpectedVersion.any()); DefaultCommand command = new DefaultCommand(repository, operation); when(repository.load(any())) .thenReturn(DefaultImmutableAggregate.fromExisting( mock(AggregateProjection.class), AGGREGATE_ID, 42, new TestState("test"))); command.setAggregateId(AGGREGATE_ID); command.run(); verify(repository).load(AGGREGATE_ID); }
@Override public CommandResult<TEvent> run() { logger.debug("Running command on {}", aggregateId); validate(); getEffectiveExpectedVersion(expectedVersion, operation.expectedVersion()); logger.debug("Expected version set as {}", effectiveExpectedVersion); if (operation.requiresState() || atomic) { logger.debug("Reading aggregate record from stream"); aggregate = readAndValidateAggregate(effectiveExpectedVersion); logger.debug( "Current state of aggregate is {}",
when(repository.load(any())).thenReturn(sourceAggregate); when(repository.append(any(), any(), any(), any())).thenReturn(newVersion); DefaultCommand command = new DefaultCommand(repository, operation); command.setAggregateId(AGGREGATE_ID); command.setIdempotentCreate(true); CommandResult commandResult = command.run();
ExpectedVersion.any()); DefaultCommand command = new DefaultCommand(repository, operation); when(repository.load(any())) .thenReturn(DefaultImmutableAggregate.fromExisting( when(repository.append(any(), any(), any(), any())).thenReturn(newVersion); command.setAggregateId(AGGREGATE_ID); CommandResult commandResult = command.run();
@Override @SuppressWarnings("unchecked") public <TParams> Command<TState, TParams, TEvent> fromOperation( final Operation<? super TState, ? super TParams, ? extends TEvent> operation) { // HACK: We know this case is safe based on how we use these interfaces, having the // wildcard types bubble through everywhere makes the code very ugly however - this is the // point where we convert from operation types that may use other concrete types for state // and parameters, into the one we know is applicable to our aggregate repository. Operation<TState, TParams, TEvent> typeHackedOperation = (Operation<TState, TParams, TEvent>) operation; Command<TState, TParams, TEvent> command = new DefaultCommand<>(repository, typeHackedOperation); for (CommandPostProcessor postProcessor : postProcessors) { postProcessor.postProcessCommand(command); } return command; } }
public Command<TState, TParams, TEvent> validate() { if (aggregateId == null) { throw new InvalidCommandException("No aggregate id specified"); } if (operation.requiresArguments() && arguments == null) { throw new InvalidCommandException( "No arguments specified to command that requires arguments"); } // Try to calculate effective expected version - will validate combinations getEffectiveExpectedVersion(expectedVersion, operation.expectedVersion()); // TODO: Add more validation! return this; }