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