Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
4b440a2
Add `when_updated` field to the `MarketSharesUpdated` event.
Jul 7, 2023
16e9689
Define ID for the `SharePriceMovement` projection.
Jul 7, 2023
0d37be0
Define the `SharePriceMovement` projection.
Jul 7, 2023
9673125
Set the `when_updated` field value in the `MarketDataProvider`.
Jul 7, 2023
636f8f3
Implement the `ProjectionReader`.
Jul 7, 2023
f631abb
Implement the `SharePriceMovementProjection` class.
Jul 7, 2023
733abce
Implement the repository for the `SharePriceMovementProjection`.
Jul 7, 2023
429c2ae
Add `SharePriceMovementRepository` to the `TradingContext`.
Jul 7, 2023
b200283
Add `share` field to `SharePriceMovement` projection.
Jul 9, 2023
42069ce
Extract extensions for time-related types to a separate file.
Jul 9, 2023
40f9db9
Rename the `time_range` field to the `activity_time` in the `SharePri…
Jul 9, 2023
282880c
Rename the `SharePriceMovement` to the `SharePriceMovementPerMinute`.
Jul 9, 2023
261758b
Implement `isActive` extension for the `SharePriceMovementPerMinute` …
Jul 10, 2023
d4c73f5
Add tests for the `SharePriceMovement` projection.
Jul 11, 2023
fb3b8d5
Add documentation to the `SharePriceMovementId`.
Jul 11, 2023
fd29c4c
Write documentation for the `SharePriceMovementPerMinute`.
Jul 11, 2023
fca923f
Write documentation for the `ProjectionReader` class.
Jul 11, 2023
4b99f5e
Write documentation for the `SharePriceMovementPerMinuteRepository`.
Jul 11, 2023
51b3594
Rewrite the `Duration.greaterThan` extension.
Jul 11, 2023
f6547d8
Add documentation to the `SharePriceMovementPerMinuteProjection`.
Jul 11, 2023
aa15d55
Rename the `MovementPoint` to the `PriceAtTime`.
Jul 11, 2023
46d3ff4
Introduce a constant for the number of nanoseconds in one second in t…
Jul 11, 2023
c7b1689
Rewrite all Kotlin files in the `server` module to Java.
Jul 13, 2023
7f91e96
Rename the `price_at_time` field in the `SharePriceMovementPerMinute`…
Jul 13, 2023
fc7a14b
Rename the `time` field in the `PriceAtTime` entity to the `when`.
Jul 13, 2023
53ef674
Implement the `SharePriceMovement` mixin.
Jul 13, 2023
56732c5
Rename the `setTime` to `setWhen` in the builder of the `PriceAtTime`…
Jul 14, 2023
394c55d
Omit the local variables types.
Jul 14, 2023
b7e20cb
Implement the `waitForNewState` method in the `EntitySubscription`.
Jul 17, 2023
3fc8355
Remove the redundant implementation note from the `SharePriceMovement…
Jul 18, 2023
a9ea041
Rewrite documentation for the `SharePriceMovementId`.
Jul 18, 2023
7dd2348
Include the ID and list of shares to the error message which throws t…
Jul 18, 2023
3af24fe
Adjust code style in the `SharePriceMovementPerMinuteRepository`.
Jul 18, 2023
5ba229f
Add `final` modifier to the `ProjectionReader` class.
Jul 18, 2023
a123236
Rename the `waitForNewState` method to the `onceUpdated` in the `Enti…
Jul 18, 2023
d06facc
Implement the `share` method in the `GivenShare` class.
Jul 18, 2023
5938ffa
Import the `ProtoTruth` statically in the `SharePriceMovementProjecti…
Jul 18, 2023
11e252c
Implement the `WithShares` interface.
Jul 18, 2023
f3047d9
Rename the `retrieveShare` method to `find` in the `WithShares` inter…
Jul 18, 2023
23035b5
Rewrite the documentation for the `onceUpdated` method in the `Entity…
Jul 18, 2023
0d6f54f
Implement the `onceUpdatedAfter` method in the `EntitySubscription` c…
Jul 18, 2023
44648d6
Rename the `shareFromId` method to `share` in the `SharePriceMovement…
Jul 18, 2023
1203b24
Remove waiting from the `state` method in the `ObservedEntity` class.
Jul 19, 2023
4964675
Rename the `onceUpdated` method to `waitForUpdate` in the `ObservedEn…
Jul 19, 2023
f595225
Move waiting for the market to release shares to the `SharePurchaseTe…
Jul 19, 2023
3d09eba
Add documentation to the `marketPeriod` method in the `WithServer` cl…
Jul 19, 2023
45a0b53
Create the `testutil-server` module.
Jul 24, 2023
69ebab3
Implement the `AsyncObserver` class.
Jul 24, 2023
fa90950
Add tests for the `AsyncObserver` class.
Jul 24, 2023
5c78e75
Rewrite the `EntitySubscription` class.
Jul 24, 2023
0962ee9
Add `package-info` for the `testing.server.e2e` package.
Jul 24, 2023
ce67399
Add `package-info` for the `testing.server.e2e.given` package.
Jul 24, 2023
8c594a4
Remove unnecessary comments from the `AsyncObserver`.
Jul 24, 2023
64404e0
Use `Objects.requireNonNull` instead of `Preconditions.checkNotNull` …
Jul 24, 2023
23adbbc
Mark nullable fields with `Nullable` annotation in the `AsyncObserver…
Jul 24, 2023
f7e036d
Extract the `waitForFutureToComplete` method in the `AsyncObserver` c…
Jul 25, 2023
a582b6d
Extract `waitForMutate` and `notifyCaller` methods in the `AsyncState…
Jul 25, 2023
e601beb
Rename the values of `ObservationState` enum to `UPDATE_PACKED` and `…
Jul 25, 2023
d29548e
Implement `StateRecipient` and `StateRouter` interfaces.
Jul 26, 2023
7a5cddf
Update the `AsyncStateMutator` to use the `StateRouter` interface.
Jul 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2023, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.examples.shareaware.market;

import com.google.errorprone.annotations.Immutable;
import com.google.protobuf.Duration;
import com.google.protobuf.Timestamp;
import io.spine.annotation.GeneratedMixin;
import io.spine.base.EntityState;
import io.spine.examples.shareaware.ShareId;
import io.spine.examples.shareaware.SharePriceMovementId;

/**
* Common interface for projections that display the movements of the share price.
*/
@Immutable
@GeneratedMixin
public interface SharePriceMovement extends EntityState {

/**
* Returns the ID of the {@code SharePriceMovement} projection.
*/
SharePriceMovementId getId();

/**
* Returns the time when the projection was created.
*/
default Timestamp whenCreated() {
return getId().getWhenCreated();
}

/**
* Returns the activity period of the projection.
*
* <p>The period when it is collecting data about the share price movements.
*/
default Duration activityTime() {
return getId().getActivityTime();
}

/**
* Returns the ID of the share which price movements the projection displays.
*/
default ShareId share() {
return getId().getShare();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright 2023, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.examples.shareaware.market;

import com.google.errorprone.annotations.Immutable;
import io.spine.annotation.GeneratedMixin;
import io.spine.base.EventMessage;
import io.spine.examples.shareaware.ShareId;
import io.spine.examples.shareaware.share.Share;

import java.util.List;

import static io.spine.util.Exceptions.newIllegalArgumentException;

/**
* Common interface for signals which operate with a list of shares.
*/
@Immutable
@GeneratedMixin
public interface WithShares extends EventMessage {

/**
* Returns the list of shares.
*/
List<Share> getShareList();

/**
* Finds the share with provided ID from the shares list.
*
* @throws IllegalArgumentException when the share with provided ID is not found in the list
*/
default Share find(ShareId id) {
List<Share> shares = getShareList();
var optionalShare = shares
.stream()
.filter(share -> share.getId().equals(id))
.findAny();
if (optionalShare.isEmpty()) {
throw newIllegalArgumentException(
"Cannot find the share with the provided ID `%s` in the list of shares `%s`.",
id, shares);
}
return optionalShare.get();
}
}
15 changes: 15 additions & 0 deletions model/src/main/proto/spine_examples/shareaware/identifiers.proto
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ option java_outer_classname = "IdentifiersProto";
option java_multiple_files = true;

import "spine/core/user_id.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";

// Identifies a watchlist.
message WatchlistId {
Expand Down Expand Up @@ -119,3 +121,16 @@ message ReplenishmentOperationId {
SaleId sale = 2;
}
}

// Identifies the share price movement view.
message SharePriceMovementId {

// The ID of share, for which the movements are collected.
ShareId share = 1;

// The duration during which the share price movements are collected.
google.protobuf.Duration activity_time = 2 [(required) = true];

// The time when the share price movement view was created.
google.protobuf.Timestamp when_created = 3 [(required) = true];
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ option java_multiple_files = true;
import "spine_examples/shareaware/identifiers.proto";
import "spine/money/money.proto";
import "spine_examples/shareaware/share.proto";
import "google/protobuf/timestamp.proto";

// Shares have been obtained from the market.
message SharesObtained {
Expand Down Expand Up @@ -90,10 +91,14 @@ message MarketClosed {

// Shares on market have been updated.
message MarketSharesUpdated {
option (is).java_type = "io.spine.examples.shareaware.market.WithShares";

// The ID of the shares market.
MarketId market = 1;

// Updated shares.
repeated Share share = 2 [(required) = true];

// Time when shares were updated.
google.protobuf.Timestamp when_updated = 3 [(required) = true];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright 2023, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

syntax = "proto3";

package spine_examples.shareaware.market;

import "spine/options.proto";

option (type_url_prefix) = "type.shareaware.spine.io";
option java_package = "io.spine.examples.shareaware.market";
option java_outer_classname = "SharesPriceMovementPerMinuteProto";
option java_multiple_files = true;

import "spine_examples/shareaware/identifiers.proto";
import "google/protobuf/timestamp.proto";
import "spine/money/money.proto";

// Displays the share price movement per minute on a timeline.
message SharePriceMovementPerMinute {
option (entity) = {kind: PROJECTION};
option (is).java_type = "io.spine.examples.shareaware.market.SharePriceMovement";

// The ID of the share price movement.
SharePriceMovementId id = 1;

// The ID of the share, which price movement to display.
ShareId share = 2 [(required) = true, (column) = true];

// The list of the price-to-time points.
repeated PriceAtTime point = 3;
}

// The share price at a particular time.
message PriceAtTime {

// The share price.
spine.money.Money price = 1 [(required) = true];

// Point in time when the share price was set.
google.protobuf.Timestamp when = 2 [(required) = true];
}
1 change: 1 addition & 0 deletions server/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ spine {
dependencies {
implementation(project(":model"))
testImplementation(project(":model", "test"))
testImplementation(project(":testutil-server"))
}

application {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import io.spine.examples.shareaware.server.investment.SharesSaleRepository;
import io.spine.examples.shareaware.server.market.AvailableMarketSharesRepository;
import io.spine.examples.shareaware.server.market.MarketProcess;
import io.spine.examples.shareaware.server.market.SharePriceMovementPerMinuteRepository;
import io.spine.examples.shareaware.server.paymentgateway.PaymentGatewayProcess;
import io.spine.examples.shareaware.server.wallet.WalletAggregate;
import io.spine.examples.shareaware.server.wallet.WalletBalanceRepository;
Expand Down Expand Up @@ -75,6 +76,7 @@ public static BoundedContextBuilder newBuilder() {
.add(new SharesPurchaseRepository())
.add(new SharesSaleRepository())
.add(new InvestmentViewRepository())
.add(new AvailableMarketSharesRepository());
.add(new AvailableMarketSharesRepository())
.add(new SharePriceMovementPerMinuteRepository());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.function.Consumer;

import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static io.spine.base.Time.currentTime;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
import static java.util.concurrent.TimeUnit.MILLISECONDS;

Expand Down Expand Up @@ -159,6 +160,7 @@ private MarketSharesUpdated emitEvent() {
.newBuilder()
.setMarket(MarketProcess.ID)
.addAllShare(updatedShares)
.setWhenUpdated(currentTime())
.vBuild();
marketContext.emittedEvent(event, actor);
return event;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2023, TeamDev. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Redistribution and use in source and/or binary forms, with or without
* modification, must retain the above copyright notice and the following
* disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package io.spine.examples.shareaware.server.market;

import io.spine.core.External;
import io.spine.core.Subscribe;
import io.spine.examples.shareaware.SharePriceMovementId;
import io.spine.examples.shareaware.market.PriceAtTime;
import io.spine.examples.shareaware.market.SharePriceMovementPerMinute;
import io.spine.examples.shareaware.market.event.MarketSharesUpdated;
import io.spine.server.projection.Projection;

/**
* The view of the share price movements per minute.
*/
final class SharePriceMovementPerMinuteProjection
extends Projection<SharePriceMovementId,
SharePriceMovementPerMinute,
SharePriceMovementPerMinute.Builder> {

@Subscribe
void on(@External MarketSharesUpdated e) {
var shareId = builder()
.getId()
.getShare();
var price = e.find(shareId)
.getPrice();
var priceAtTime = PriceAtTime
.newBuilder()
.setPrice(price)
.setWhen(e.getWhenUpdated())
.vBuild();
builder().setShare(shareId)
.addPoint(priceAtTime);
}
}
Loading