From e3bc8c13f740ce86c83f574c285f5b1b92fbed36 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 13:27:08 +0200 Subject: [PATCH 01/25] Define `UserChats` proto messages. --- .../spine_examples/chatspn/identifiers.proto | 40 ++++++++++++++ .../chatspn/user_chats/events.proto | 44 +++++++++++++++ .../chatspn/user_chats/user_chats.proto | 55 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 model/src/main/proto/spine_examples/chatspn/identifiers.proto create mode 100644 model/src/main/proto/spine_examples/chatspn/user_chats/events.proto create mode 100644 model/src/main/proto/spine_examples/chatspn/user_chats/user_chats.proto diff --git a/model/src/main/proto/spine_examples/chatspn/identifiers.proto b/model/src/main/proto/spine_examples/chatspn/identifiers.proto new file mode 100644 index 00000000..c9d2f9b5 --- /dev/null +++ b/model/src/main/proto/spine_examples/chatspn/identifiers.proto @@ -0,0 +1,40 @@ +/* + * 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.chatspn; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.chatspn.spine.io"; +option java_package = "io.spine.examples.chatspn"; +option java_outer_classname = "IdentifiersProto"; +option java_multiple_files = true; + +// Identifies a chat in the scope of the application. +message ChatId { + string uuid = 1 [(required) = true]; +} diff --git a/model/src/main/proto/spine_examples/chatspn/user_chats/events.proto b/model/src/main/proto/spine_examples/chatspn/user_chats/events.proto new file mode 100644 index 00000000..837a0539 --- /dev/null +++ b/model/src/main/proto/spine_examples/chatspn/user_chats/events.proto @@ -0,0 +1,44 @@ +/* + * 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.chatspn.user_chats; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.chatspn.spine.io"; +option java_package = "io.spine.examples.chatspn.userchats.event"; +option java_outer_classname = "EventsProto"; +option java_multiple_files = true; + +import "spine/core/user_id.proto"; + +// A new user chats has been created. +message UserChatsCreated { + + // The ID of the created user chats owner. + spine.core.UserId owner = 1; +} diff --git a/model/src/main/proto/spine_examples/chatspn/user_chats/user_chats.proto b/model/src/main/proto/spine_examples/chatspn/user_chats/user_chats.proto new file mode 100644 index 00000000..2020b6fb --- /dev/null +++ b/model/src/main/proto/spine_examples/chatspn/user_chats/user_chats.proto @@ -0,0 +1,55 @@ +/* + * 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.chatspn.user_chats; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.chatspn.spine.io"; +option java_package = "io.spine.examples.chatspn.userchats"; +option java_outer_classname = "UserChatsProto"; +option java_multiple_files = true; + +import "spine/core/user_id.proto"; +import "spine_examples/chatspn/identifiers.proto"; + +// Settings and information about chats in which the user is a member. +message UserChats { + option (entity) = { kind: AGGREGATE }; + + // The ID of the user chats owner. + spine.core.UserId owner = 1; + + // The chats in which the owner-user is a member. + repeated ChatId chats = 2; + + // The chats that the owner-user muted. + repeated ChatId muted_chats = 3; + + // The users that the owner-user blocked. + repeated spine.core.UserId blocked_users = 4; +} From c6819534ef7627f162302b380cbb4dd37034cd79 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 15:03:28 +0200 Subject: [PATCH 02/25] Implement `UserChatsAggregate` as user's part. --- .../chatspn/server/user/UserAggregate.java | 14 +++- .../chatspn/server/user/UserRoot.java | 49 ++++++++++++++ .../server/user/chats/UserChatsAggregate.java | 67 +++++++++++++++++++ 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java create mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java index 06c8b6d2..e8e7c79e 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java @@ -30,14 +30,24 @@ import io.spine.examples.chatspn.user.User; import io.spine.examples.chatspn.user.command.RegisterUser; import io.spine.examples.chatspn.user.event.UserRegistered; -import io.spine.server.aggregate.Aggregate; +import io.spine.server.aggregate.AggregatePart; import io.spine.server.aggregate.Apply; import io.spine.server.command.Assign; /** * A registered user of ChatSPN. */ -public final class UserAggregate extends Aggregate { +public final class UserAggregate extends AggregatePart { + + /** + * Creates a new instance of the aggregate part. + * + * @param root + * a root of the aggregate to which this part belongs + */ + private UserAggregate(UserRoot root) { + super(root); + } /** * Handles the command to register a user. diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java new file mode 100644 index 00000000..33a5ef91 --- /dev/null +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java @@ -0,0 +1,49 @@ +/* + * 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.chatspn.server.user; + +import io.spine.core.UserId; +import io.spine.server.BoundedContext; +import io.spine.server.aggregate.AggregateRoot; + +/** + * User's {@link AggregateRoot}. + */ +public class UserRoot extends AggregateRoot { + + /** + * Creates a new instance. + * + * @param context + * the bounded context to which the aggregate belongs + * @param id + * the ID of the aggregate + */ + protected UserRoot(BoundedContext context, UserId id) { + super(context, id); + } +} diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java new file mode 100644 index 00000000..e4e2cd18 --- /dev/null +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java @@ -0,0 +1,67 @@ +/* + * 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.chatspn.server.user.chats; + +import io.spine.core.UserId; +import io.spine.examples.chatspn.server.user.UserRoot; +import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.userchats.UserChats; +import io.spine.examples.chatspn.userchats.event.UserChatsCreated; +import io.spine.server.aggregate.AggregatePart; +import io.spine.server.aggregate.Apply; +import io.spine.server.event.React; + +/** + * The {@code UserChatsAggregate} controls the policy of joining chats + * and settings of chats in which the user is a member. + */ +public class UserChatsAggregate + extends AggregatePart { + + /** + * Creates a new instance of the aggregate part. + * + * @param root + * a root of the aggregate to which this part belongs + */ + protected UserChatsAggregate(UserRoot root) { + super(root); + } + + @React + UserChatsCreated on(UserRegistered e) { + return UserChatsCreated + .newBuilder() + .setOwner(e.getUser()) + .vBuild(); + } + + @Apply + private void apply(UserChatsCreated e) { + builder().setOwner(e.getOwner()); + } +} From 81ed94dca8dc04638fec0739352807428fb028c8 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 15:09:22 +0200 Subject: [PATCH 03/25] Implement `UserChatsRepository`. --- .../examples/chatspn/server/ChatsContext.java | 4 +- .../user/chats/UserChatsRepository.java | 50 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java diff --git a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java index 28bbf36c..d0ee515d 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java @@ -28,6 +28,7 @@ import io.spine.examples.chatspn.server.user.UserAggregate; import io.spine.examples.chatspn.server.user.UserProfileRepository; +import io.spine.examples.chatspn.server.user.chats.UserChatsRepository; import io.spine.server.BoundedContext; import io.spine.server.BoundedContextBuilder; import io.spine.server.DefaultRepository; @@ -53,6 +54,7 @@ public static BoundedContextBuilder newBuilder() { return BoundedContext .singleTenant(NAME) .add(DefaultRepository.of(UserAggregate.class)) - .add(new UserProfileRepository()); + .add(new UserProfileRepository()) + .add(new UserChatsRepository()); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java new file mode 100644 index 00000000..348fab79 --- /dev/null +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java @@ -0,0 +1,50 @@ +/* + * 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.chatspn.server.user.chats; + +import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; +import io.spine.core.UserId; +import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.server.aggregate.AggregateRepository; +import io.spine.server.route.EventRouting; + +import static io.spine.server.route.EventRoute.withId; + +/** + * The repository for managing {@link UserChatsAggregate} instances. + * + *

Routes the users {@link UserRegistered} event to an appropriate {@link UserChatsAggregate}. + */ +public final class UserChatsRepository extends AggregateRepository { + + @OverridingMethodsMustInvokeSuper + @Override + protected void setupEventRouting(EventRouting routing) { + super.setupEventRouting(routing); + routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())); + } +} From ca62ed8848f1e4a355a695170d842121e9f7c1c3 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 15:36:41 +0200 Subject: [PATCH 04/25] Add test on `UserChats` creation. --- .../server/user/chats/UserChatsAggregate.java | 6 +- .../user/chats/UserChatsRepository.java | 8 +- .../server/user/chats/UserChatsTest.java | 75 +++++++++++++++++++ 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 server/src/test/java/io/spine/examples/chatspn/server/user/chats/UserChatsTest.java diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java index e4e2cd18..ee8bb1d6 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java @@ -39,7 +39,7 @@ * The {@code UserChatsAggregate} controls the policy of joining chats * and settings of chats in which the user is a member. */ -public class UserChatsAggregate +public final class UserChatsAggregate extends AggregatePart { /** @@ -48,7 +48,7 @@ public class UserChatsAggregate * @param root * a root of the aggregate to which this part belongs */ - protected UserChatsAggregate(UserRoot root) { + private UserChatsAggregate(UserRoot root) { super(root); } @@ -61,7 +61,7 @@ UserChatsCreated on(UserRegistered e) { } @Apply - private void apply(UserChatsCreated e) { + private void event(UserChatsCreated e) { builder().setOwner(e.getOwner()); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java index 348fab79..e476c972 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java @@ -28,18 +28,18 @@ import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; import io.spine.core.UserId; +import io.spine.examples.chatspn.server.user.UserRoot; import io.spine.examples.chatspn.user.event.UserRegistered; -import io.spine.server.aggregate.AggregateRepository; +import io.spine.server.aggregate.AggregatePartRepository; import io.spine.server.route.EventRouting; import static io.spine.server.route.EventRoute.withId; /** * The repository for managing {@link UserChatsAggregate} instances. - * - *

Routes the users {@link UserRegistered} event to an appropriate {@link UserChatsAggregate}. */ -public final class UserChatsRepository extends AggregateRepository { +public final class UserChatsRepository + extends AggregatePartRepository { @OverridingMethodsMustInvokeSuper @Override diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/chats/UserChatsTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/chats/UserChatsTest.java new file mode 100644 index 00000000..34535359 --- /dev/null +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/chats/UserChatsTest.java @@ -0,0 +1,75 @@ +/* + * 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.chatspn.server.user.chats; + +import io.spine.examples.chatspn.server.ChatsContext; +import io.spine.examples.chatspn.user.command.RegisterUser; +import io.spine.examples.chatspn.userchats.UserChats; +import io.spine.examples.chatspn.userchats.event.UserChatsCreated; +import io.spine.server.BoundedContextBuilder; +import io.spine.testing.core.given.GivenUserId; +import io.spine.testing.server.blackbox.ContextAwareTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static io.spine.testing.TestValues.randomString; + +@DisplayName("`UserChats` should") +class UserChatsTest extends ContextAwareTest { + + @Override + protected BoundedContextBuilder contextBuilder() { + return ChatsContext.newBuilder(); + } + + @Test + @DisplayName("react on `UserRegistered` and emit the `UserChatsCreated` event") + void creation() { + RegisterUser command = RegisterUser + .newBuilder() + .setUser(GivenUserId.generated()) + .setName(randomString()) + .vBuild(); + + context().receivesCommand(command); + + UserChatsCreated expectedEvent = UserChatsCreated + .newBuilder() + .setOwner(command.getUser()) + .build(); + UserChats expectedState = UserChats + .newBuilder() + .setOwner(command.getUser()) + .vBuild(); + + context().assertEvents() + .withType(UserChatsCreated.class) + .hasSize(1); + context().assertEvent(expectedEvent); + context().assertState(command.getUser(), expectedState); + } +} From 72619d322143898b4571deb3d119e5b989478d26 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 18:10:00 +0200 Subject: [PATCH 05/25] Merge `user` and `userchats` into one package. --- .../spine_examples/chatspn/identifiers.proto | 2 +- .../spine_examples/chatspn/user/events.proto | 7 +++++ .../user_blocklist.proto} | 29 ++++++++++++++----- .../{user_chats => user}/user_chats.proto | 4 +-- .../examples/chatspn/server/ChatsContext.java | 2 +- .../user/{chats => }/UserChatsAggregate.java | 2 +- .../user/{chats => }/UserChatsRepository.java | 3 +- .../user/{chats => }/UserChatsTest.java | 2 +- 8 files changed, 36 insertions(+), 15 deletions(-) rename model/src/main/proto/spine_examples/chatspn/{user_chats/events.proto => user/user_blocklist.proto} (67%) rename model/src/main/proto/spine_examples/chatspn/{user_chats => user}/user_chats.proto (95%) rename server/src/main/java/io/spine/examples/chatspn/server/user/{chats => }/UserChatsAggregate.java (97%) rename server/src/main/java/io/spine/examples/chatspn/server/user/{chats => }/UserChatsRepository.java (94%) rename server/src/test/java/io/spine/examples/chatspn/server/user/{chats => }/UserChatsTest.java (98%) diff --git a/model/src/main/proto/spine_examples/chatspn/identifiers.proto b/model/src/main/proto/spine_examples/chatspn/identifiers.proto index c9d2f9b5..938f8528 100644 --- a/model/src/main/proto/spine_examples/chatspn/identifiers.proto +++ b/model/src/main/proto/spine_examples/chatspn/identifiers.proto @@ -36,5 +36,5 @@ option java_multiple_files = true; // Identifies a chat in the scope of the application. message ChatId { - string uuid = 1 [(required) = true]; + string uuid = 1 [(required) = true]; } diff --git a/model/src/main/proto/spine_examples/chatspn/user/events.proto b/model/src/main/proto/spine_examples/chatspn/user/events.proto index 659d21c0..76e06ec4 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/events.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/events.proto @@ -45,3 +45,10 @@ message UserRegistered { // A name of the registered user. string name = 2 [(required) = true]; } + +// A new user chats has been created. +message UserChatsCreated { + + // The ID of the created user chats owner. + spine.core.UserId owner = 1; +} diff --git a/model/src/main/proto/spine_examples/chatspn/user_chats/events.proto b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto similarity index 67% rename from model/src/main/proto/spine_examples/chatspn/user_chats/events.proto rename to model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto index 837a0539..b393d3db 100644 --- a/model/src/main/proto/spine_examples/chatspn/user_chats/events.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto @@ -25,20 +25,35 @@ */ syntax = "proto3"; -package spine_examples.chatspn.user_chats; +package spine_examples.chatspn.user; import "spine/options.proto"; option (type_url_prefix) = "type.chatspn.spine.io"; -option java_package = "io.spine.examples.chatspn.userchats.event"; -option java_outer_classname = "EventsProto"; +option java_package = "io.spine.examples.chatspn.user"; +option java_outer_classname = "UserProfileProto"; option java_multiple_files = true; +import "google/protobuf/timestamp.proto"; import "spine/core/user_id.proto"; -// A new user chats has been created. -message UserChatsCreated { +// A user profile view in the chatting system. +message UserBlocklist { + option (entity) = { kind: PROJECTION }; - // The ID of the created user chats owner. - spine.core.UserId owner = 1; + // The ID of the user who owned blocklist. + spine.core.UserId id = 1; + + // User's blocklist. + repeated BlockedUser blockedUser = 2; + + // Represents a view of a blocked user. + message BlockedUser { + + // The ID of the blocked user. + spine.core.UserId user = 1; + + // The name of the blocked user. + string name = 2 [(required) = true]; + } } diff --git a/model/src/main/proto/spine_examples/chatspn/user_chats/user_chats.proto b/model/src/main/proto/spine_examples/chatspn/user/user_chats.proto similarity index 95% rename from model/src/main/proto/spine_examples/chatspn/user_chats/user_chats.proto rename to model/src/main/proto/spine_examples/chatspn/user/user_chats.proto index 2020b6fb..3b9ece95 100644 --- a/model/src/main/proto/spine_examples/chatspn/user_chats/user_chats.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user_chats.proto @@ -25,12 +25,12 @@ */ syntax = "proto3"; -package spine_examples.chatspn.user_chats; +package spine_examples.chatspn.user; import "spine/options.proto"; option (type_url_prefix) = "type.chatspn.spine.io"; -option java_package = "io.spine.examples.chatspn.userchats"; +option java_package = "io.spine.examples.chatspn.user"; option java_outer_classname = "UserChatsProto"; option java_multiple_files = true; diff --git a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java index d0ee515d..42a1b1da 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java @@ -28,7 +28,7 @@ import io.spine.examples.chatspn.server.user.UserAggregate; import io.spine.examples.chatspn.server.user.UserProfileRepository; -import io.spine.examples.chatspn.server.user.chats.UserChatsRepository; +import io.spine.examples.chatspn.server.user.UserChatsRepository; import io.spine.server.BoundedContext; import io.spine.server.BoundedContextBuilder; import io.spine.server.DefaultRepository; diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java similarity index 97% rename from server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java rename to server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java index ee8bb1d6..e07a29c2 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.examples.chatspn.server.user.chats; +package io.spine.examples.chatspn.server.user; import io.spine.core.UserId; import io.spine.examples.chatspn.server.user.UserRoot; diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java similarity index 94% rename from server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java rename to server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java index e476c972..a1e3b5cf 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/chats/UserChatsRepository.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java @@ -24,11 +24,10 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.examples.chatspn.server.user.chats; +package io.spine.examples.chatspn.server.user; import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; import io.spine.core.UserId; -import io.spine.examples.chatspn.server.user.UserRoot; import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.server.aggregate.AggregatePartRepository; import io.spine.server.route.EventRouting; diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/chats/UserChatsTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java similarity index 98% rename from server/src/test/java/io/spine/examples/chatspn/server/user/chats/UserChatsTest.java rename to server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java index 34535359..f067dedc 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/chats/UserChatsTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java @@ -24,7 +24,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package io.spine.examples.chatspn.server.user.chats; +package io.spine.examples.chatspn.server.user; import io.spine.examples.chatspn.server.ChatsContext; import io.spine.examples.chatspn.user.command.RegisterUser; From 17b199db0820856c0f87f59a4029af833e3ddfc9 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 18:17:39 +0200 Subject: [PATCH 06/25] Make `User` and `UserChats` different aggregates. --- .../chatspn/user/user_blocklist.proto | 3 +- .../chatspn/server/user/UserAggregate.java | 14 +----- .../server/user/UserChatsAggregate.java | 19 ++----- .../server/user/UserChatsRepository.java | 4 +- .../chatspn/server/user/UserRoot.java | 49 ------------------- .../chatspn/server/user/UserChatsTest.java | 4 +- 6 files changed, 11 insertions(+), 82 deletions(-) delete mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java diff --git a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto index b393d3db..d24bee09 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto @@ -31,10 +31,9 @@ import "spine/options.proto"; option (type_url_prefix) = "type.chatspn.spine.io"; option java_package = "io.spine.examples.chatspn.user"; -option java_outer_classname = "UserProfileProto"; +option java_outer_classname = "UserBlocklistProto"; option java_multiple_files = true; -import "google/protobuf/timestamp.proto"; import "spine/core/user_id.proto"; // A user profile view in the chatting system. diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java index e8e7c79e..06c8b6d2 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java @@ -30,24 +30,14 @@ import io.spine.examples.chatspn.user.User; import io.spine.examples.chatspn.user.command.RegisterUser; import io.spine.examples.chatspn.user.event.UserRegistered; -import io.spine.server.aggregate.AggregatePart; +import io.spine.server.aggregate.Aggregate; import io.spine.server.aggregate.Apply; import io.spine.server.command.Assign; /** * A registered user of ChatSPN. */ -public final class UserAggregate extends AggregatePart { - - /** - * Creates a new instance of the aggregate part. - * - * @param root - * a root of the aggregate to which this part belongs - */ - private UserAggregate(UserRoot root) { - super(root); - } +public final class UserAggregate extends Aggregate { /** * Handles the command to register a user. diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java index e07a29c2..3fffafb9 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java @@ -27,11 +27,10 @@ package io.spine.examples.chatspn.server.user; import io.spine.core.UserId; -import io.spine.examples.chatspn.server.user.UserRoot; import io.spine.examples.chatspn.user.event.UserRegistered; -import io.spine.examples.chatspn.userchats.UserChats; -import io.spine.examples.chatspn.userchats.event.UserChatsCreated; -import io.spine.server.aggregate.AggregatePart; +import io.spine.examples.chatspn.user.UserChats; +import io.spine.examples.chatspn.user.event.UserChatsCreated; +import io.spine.server.aggregate.Aggregate; import io.spine.server.aggregate.Apply; import io.spine.server.event.React; @@ -40,17 +39,7 @@ * and settings of chats in which the user is a member. */ public final class UserChatsAggregate - extends AggregatePart { - - /** - * Creates a new instance of the aggregate part. - * - * @param root - * a root of the aggregate to which this part belongs - */ - private UserChatsAggregate(UserRoot root) { - super(root); - } + extends Aggregate { @React UserChatsCreated on(UserRegistered e) { diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java index a1e3b5cf..a4bff9a5 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java @@ -29,7 +29,7 @@ import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; import io.spine.core.UserId; import io.spine.examples.chatspn.user.event.UserRegistered; -import io.spine.server.aggregate.AggregatePartRepository; +import io.spine.server.aggregate.AggregateRepository; import io.spine.server.route.EventRouting; import static io.spine.server.route.EventRoute.withId; @@ -38,7 +38,7 @@ * The repository for managing {@link UserChatsAggregate} instances. */ public final class UserChatsRepository - extends AggregatePartRepository { + extends AggregateRepository { @OverridingMethodsMustInvokeSuper @Override diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java deleted file mode 100644 index 33a5ef91..00000000 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserRoot.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.chatspn.server.user; - -import io.spine.core.UserId; -import io.spine.server.BoundedContext; -import io.spine.server.aggregate.AggregateRoot; - -/** - * User's {@link AggregateRoot}. - */ -public class UserRoot extends AggregateRoot { - - /** - * Creates a new instance. - * - * @param context - * the bounded context to which the aggregate belongs - * @param id - * the ID of the aggregate - */ - protected UserRoot(BoundedContext context, UserId id) { - super(context, id); - } -} diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java index f067dedc..317b49b8 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java @@ -28,8 +28,8 @@ import io.spine.examples.chatspn.server.ChatsContext; import io.spine.examples.chatspn.user.command.RegisterUser; -import io.spine.examples.chatspn.userchats.UserChats; -import io.spine.examples.chatspn.userchats.event.UserChatsCreated; +import io.spine.examples.chatspn.user.UserChats; +import io.spine.examples.chatspn.user.event.UserChatsCreated; import io.spine.server.BoundedContextBuilder; import io.spine.testing.core.given.GivenUserId; import io.spine.testing.server.blackbox.ContextAwareTest; From 409900afb395024635e16dd636351cc978bae1e2 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 18:29:24 +0200 Subject: [PATCH 07/25] Implement `UserBlocklistProjection`. --- .../server/user/UserBlocklistProjection.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java new file mode 100644 index 00000000..c2dbe20c --- /dev/null +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java @@ -0,0 +1,41 @@ +/* + * 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.chatspn.server.user; + +import io.spine.core.Subscribe; +import io.spine.core.UserId; +import io.spine.examples.chatspn.user.UserBlocklist; +import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.server.projection.Projection; + +public class UserBlocklistProjection extends Projection { + + @Subscribe + void on(UserRegistered e) { + builder().setId(e.getUser()); + } +} From f6626f14c29fbed70d9323adceda837fa731f650 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 18:48:48 +0200 Subject: [PATCH 08/25] Add test on `UserBlocklistProjection` creation. --- .../examples/chatspn/server/ChatsContext.java | 4 +- .../server/user/UserBlocklistRepository.java | 50 ++++++++++++++ .../user/UserBlocklistProjectionTest.java | 66 +++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java create mode 100644 server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java diff --git a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java index 42a1b1da..2b64dc55 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java @@ -27,6 +27,7 @@ package io.spine.examples.chatspn.server; import io.spine.examples.chatspn.server.user.UserAggregate; +import io.spine.examples.chatspn.server.user.UserBlocklistRepository; import io.spine.examples.chatspn.server.user.UserProfileRepository; import io.spine.examples.chatspn.server.user.UserChatsRepository; import io.spine.server.BoundedContext; @@ -55,6 +56,7 @@ public static BoundedContextBuilder newBuilder() { .singleTenant(NAME) .add(DefaultRepository.of(UserAggregate.class)) .add(new UserProfileRepository()) - .add(new UserChatsRepository()); + .add(new UserChatsRepository()) + .add(new UserBlocklistRepository()); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java new file mode 100644 index 00000000..0a1cd195 --- /dev/null +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java @@ -0,0 +1,50 @@ +/* + * 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.chatspn.server.user; + +import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; +import io.spine.core.UserId; +import io.spine.examples.chatspn.user.UserBlocklist; +import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.server.projection.ProjectionRepository; +import io.spine.server.route.EventRouting; + +import static io.spine.server.route.EventRoute.withId; + +/** + * The repository for managing {@link UserBlocklistProjection} instances. + */ +public final class UserBlocklistRepository + extends ProjectionRepository { + + @OverridingMethodsMustInvokeSuper + @Override + protected void setupEventRouting(EventRouting routing) { + super.setupEventRouting(routing); + routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())); + } +} diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java new file mode 100644 index 00000000..44dc92e3 --- /dev/null +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java @@ -0,0 +1,66 @@ +/* + * 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.chatspn.server.user; + +import io.spine.examples.chatspn.server.ChatsContext; +import io.spine.examples.chatspn.user.UserBlocklist; +import io.spine.examples.chatspn.user.UserProfile; +import io.spine.examples.chatspn.user.command.RegisterUser; +import io.spine.server.BoundedContextBuilder; +import io.spine.testing.core.given.GivenUserId; +import io.spine.testing.server.blackbox.ContextAwareTest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static io.spine.testing.TestValues.randomString; + +@DisplayName("`UserBlocklistProjection` should") +class UserBlocklistProjectionTest extends ContextAwareTest { + + @Override + protected BoundedContextBuilder contextBuilder() { + return ChatsContext.newBuilder(); + } + + @Test + @DisplayName("display `UserBlocklist`, as soon as `User` registered") + void reactOnRegistration() { + RegisterUser command = RegisterUser + .newBuilder() + .setUser(GivenUserId.generated()) + .setName(randomString()) + .vBuild(); + context().receivesCommand(command); + + UserBlocklist expected = UserBlocklist + .newBuilder() + .setId(command.getUser()) + .vBuild(); + + context().assertState(command.getUser(), expected); + } +} From fbabdc5361ed788a8c641c796d9c44931f6e685f Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 19:05:35 +0200 Subject: [PATCH 09/25] Add `package-info` for packages. --- .../spine/examples/chatspn/package-info.java | 38 +++++++++++++++++++ .../chatspn/user/command/package-info.java | 36 ++++++++++++++++++ .../chatspn/user/event/package-info.java | 36 ++++++++++++++++++ .../examples/chatspn/user/package-info.java | 36 ++++++++++++++++++ .../examples/chatspn/server/package-info.java | 38 +++++++++++++++++++ .../chatspn/server/user/package-info.java | 36 ++++++++++++++++++ 6 files changed, 220 insertions(+) create mode 100644 model/src/main/java/io/spine/examples/chatspn/package-info.java create mode 100644 model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java create mode 100644 model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java create mode 100644 model/src/main/java/io/spine/examples/chatspn/user/package-info.java create mode 100644 server/src/main/java/io/spine/examples/chatspn/server/package-info.java create mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/package-info.java diff --git a/model/src/main/java/io/spine/examples/chatspn/package-info.java b/model/src/main/java/io/spine/examples/chatspn/package-info.java new file mode 100644 index 00000000..79996f1f --- /dev/null +++ b/model/src/main/java/io/spine/examples/chatspn/package-info.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * This package contains basic data types of the ChatSPN application. + */ +@BoundedContext("Chats") +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.examples.chatspn; + +import com.google.errorprone.annotations.CheckReturnValue; +import io.spine.core.BoundedContext; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java new file mode 100644 index 00000000..9d3fa06e --- /dev/null +++ b/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * Provides ChatSPN `User` events and common event interfaces. + */ +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.examples.chatspn.user.command; + +import com.google.errorprone.annotations.CheckReturnValue; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java new file mode 100644 index 00000000..6447bd95 --- /dev/null +++ b/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * Provides ChatSPN `User` commands and common commands interfaces. + */ +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.examples.chatspn.user.event; + +import com.google.errorprone.annotations.CheckReturnValue; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/model/src/main/java/io/spine/examples/chatspn/user/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/package-info.java new file mode 100644 index 00000000..e56eb289 --- /dev/null +++ b/model/src/main/java/io/spine/examples/chatspn/user/package-info.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * This package contains `User` data types of the ChatSPN application. + */ +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.examples.chatspn.user; + +import com.google.errorprone.annotations.CheckReturnValue; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/src/main/java/io/spine/examples/chatspn/server/package-info.java b/server/src/main/java/io/spine/examples/chatspn/server/package-info.java new file mode 100644 index 00000000..1f6be724 --- /dev/null +++ b/server/src/main/java/io/spine/examples/chatspn/server/package-info.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +/** + * Provides server-side code of the ChatSPN application. + */ +@BoundedContext(ChatsContext.NAME) +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.examples.chatspn.server; + +import com.google.errorprone.annotations.CheckReturnValue; +import io.spine.core.BoundedContext; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/package-info.java b/server/src/main/java/io/spine/examples/chatspn/server/user/package-info.java new file mode 100644 index 00000000..fdf84811 --- /dev/null +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/package-info.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * Provides server-side classes for working with User. + */ +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.examples.chatspn.server.user; + +import com.google.errorprone.annotations.CheckReturnValue; + +import javax.annotation.ParametersAreNonnullByDefault; From 217f44211041859ff1acdd1f2262d55b94dcc7fb Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Fri, 10 Mar 2023 19:21:51 +0200 Subject: [PATCH 10/25] Make blocklist creation on the `UserChatsCreated` event. --- .../spine/examples/chatspn/server/ChatsContext.java | 2 +- .../chatspn/server/user/UserBlocklistProjection.java | 11 +++++++---- .../chatspn/server/user/UserBlocklistRepository.java | 4 ++-- .../chatspn/server/user/UserChatsAggregate.java | 2 +- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java index 2b64dc55..09050fcf 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java @@ -28,8 +28,8 @@ import io.spine.examples.chatspn.server.user.UserAggregate; import io.spine.examples.chatspn.server.user.UserBlocklistRepository; -import io.spine.examples.chatspn.server.user.UserProfileRepository; import io.spine.examples.chatspn.server.user.UserChatsRepository; +import io.spine.examples.chatspn.server.user.UserProfileRepository; import io.spine.server.BoundedContext; import io.spine.server.BoundedContextBuilder; import io.spine.server.DefaultRepository; diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java index c2dbe20c..f21024e2 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java @@ -29,13 +29,16 @@ import io.spine.core.Subscribe; import io.spine.core.UserId; import io.spine.examples.chatspn.user.UserBlocklist; -import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.event.UserChatsCreated; import io.spine.server.projection.Projection; -public class UserBlocklistProjection extends Projection { +/** + * Manages instances of {@code UserBlocklist} projections. + */ +public final class UserBlocklistProjection extends Projection { @Subscribe - void on(UserRegistered e) { - builder().setId(e.getUser()); + void on(UserChatsCreated e) { + builder().setId(e.getOwner()); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java index 0a1cd195..89cdd3e5 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java @@ -29,7 +29,7 @@ import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; import io.spine.core.UserId; import io.spine.examples.chatspn.user.UserBlocklist; -import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.event.UserChatsCreated; import io.spine.server.projection.ProjectionRepository; import io.spine.server.route.EventRouting; @@ -45,6 +45,6 @@ public final class UserBlocklistRepository @Override protected void setupEventRouting(EventRouting routing) { super.setupEventRouting(routing); - routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())); + routing.route(UserChatsCreated.class, (event, context) -> withId(event.getOwner())); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java index 3fffafb9..0b711d4e 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java @@ -27,9 +27,9 @@ package io.spine.examples.chatspn.server.user; import io.spine.core.UserId; -import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.examples.chatspn.user.UserChats; import io.spine.examples.chatspn.user.event.UserChatsCreated; +import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.server.aggregate.Aggregate; import io.spine.server.aggregate.Apply; import io.spine.server.event.React; From 10fdb807e215dc1b7a78d99081aaab1e056e7e37 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 00:21:54 +0200 Subject: [PATCH 11/25] Fix events and commands `package-info`s in `model` module. --- .../io/spine/examples/chatspn/user/command/package-info.java | 2 +- .../java/io/spine/examples/chatspn/user/event/package-info.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java index 9d3fa06e..127b8bbf 100644 --- a/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java +++ b/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java @@ -25,7 +25,7 @@ */ /** - * Provides ChatSPN `User` events and common event interfaces. + * Provides ChatSPN `User` commands and common commands interfaces. */ @CheckReturnValue @ParametersAreNonnullByDefault diff --git a/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java index 6447bd95..e642753e 100644 --- a/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java +++ b/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java @@ -25,7 +25,7 @@ */ /** - * Provides ChatSPN `User` commands and common commands interfaces. + * Provides ChatSPN `User` events and common event interfaces. */ @CheckReturnValue @ParametersAreNonnullByDefault From cf538394fd310c7f2b894e1933c39206e23afb00 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 13:38:28 +0200 Subject: [PATCH 12/25] Remove redundant back-ticks in javadocs. --- .../io/spine/examples/chatspn/user/command/package-info.java | 2 +- .../java/io/spine/examples/chatspn/user/event/package-info.java | 2 +- .../main/java/io/spine/examples/chatspn/user/package-info.java | 2 +- .../main/proto/spine_examples/chatspn/user/user_blocklist.proto | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java index 127b8bbf..12966a11 100644 --- a/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java +++ b/model/src/main/java/io/spine/examples/chatspn/user/command/package-info.java @@ -25,7 +25,7 @@ */ /** - * Provides ChatSPN `User` commands and common commands interfaces. + * Provides ChatSPN User commands and common commands interfaces. */ @CheckReturnValue @ParametersAreNonnullByDefault diff --git a/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java index e642753e..2942959b 100644 --- a/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java +++ b/model/src/main/java/io/spine/examples/chatspn/user/event/package-info.java @@ -25,7 +25,7 @@ */ /** - * Provides ChatSPN `User` events and common event interfaces. + * Provides ChatSPN User events and common event interfaces. */ @CheckReturnValue @ParametersAreNonnullByDefault diff --git a/model/src/main/java/io/spine/examples/chatspn/user/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/package-info.java index e56eb289..a434057b 100644 --- a/model/src/main/java/io/spine/examples/chatspn/user/package-info.java +++ b/model/src/main/java/io/spine/examples/chatspn/user/package-info.java @@ -25,7 +25,7 @@ */ /** - * This package contains `User` data types of the ChatSPN application. + * This package contains User data types of the ChatSPN application. */ @CheckReturnValue @ParametersAreNonnullByDefault diff --git a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto index d24bee09..3391ee15 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto @@ -36,7 +36,7 @@ option java_multiple_files = true; import "spine/core/user_id.proto"; -// A user profile view in the chatting system. +// A user's blocklist view in the chatting system. message UserBlocklist { option (entity) = { kind: PROJECTION }; From 324be6872a0c7d593520d1acd40f2ad369bc5f84 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 16:30:40 +0200 Subject: [PATCH 13/25] Merge `UserChats` into `User`. --- .../spine_examples/chatspn/user/events.proto | 7 -- .../spine_examples/chatspn/user/user.proto | 10 +++ .../chatspn/user/user_chats.proto | 55 -------------- .../examples/chatspn/server/ChatsContext.java | 2 - .../server/user/UserBlocklistProjection.java | 6 +- .../server/user/UserBlocklistRepository.java | 4 +- .../server/user/UserChatsAggregate.java | 56 -------------- .../server/user/UserChatsRepository.java | 49 ------------ .../user/UserBlocklistProjectionTest.java | 1 - .../chatspn/server/user/UserChatsTest.java | 75 ------------------- 10 files changed, 15 insertions(+), 250 deletions(-) delete mode 100644 model/src/main/proto/spine_examples/chatspn/user/user_chats.proto delete mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java delete mode 100644 server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java delete mode 100644 server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java diff --git a/model/src/main/proto/spine_examples/chatspn/user/events.proto b/model/src/main/proto/spine_examples/chatspn/user/events.proto index 76e06ec4..659d21c0 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/events.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/events.proto @@ -45,10 +45,3 @@ message UserRegistered { // A name of the registered user. string name = 2 [(required) = true]; } - -// A new user chats has been created. -message UserChatsCreated { - - // The ID of the created user chats owner. - spine.core.UserId owner = 1; -} diff --git a/model/src/main/proto/spine_examples/chatspn/user/user.proto b/model/src/main/proto/spine_examples/chatspn/user/user.proto index ab6c7f3d..559af2a0 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/user.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user.proto @@ -36,6 +36,7 @@ option java_multiple_files = true; import "google/protobuf/timestamp.proto"; import "spine/core/user_id.proto"; +import "spine_examples/chatspn/identifiers.proto"; // A user in the chatting system. message User { @@ -52,4 +53,13 @@ message User { // A time of last user activity. google.protobuf.Timestamp when_last_active = 3; + + // The chats in which the user is a member. + repeated ChatId chats = 4; + + // The chats that the user muted. + repeated ChatId muted_chats = 5; + + // The users that the owner-user blocked. + repeated spine.core.UserId blocked_users = 6; } diff --git a/model/src/main/proto/spine_examples/chatspn/user/user_chats.proto b/model/src/main/proto/spine_examples/chatspn/user/user_chats.proto deleted file mode 100644 index 3b9ece95..00000000 --- a/model/src/main/proto/spine_examples/chatspn/user/user_chats.proto +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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.chatspn.user; - -import "spine/options.proto"; - -option (type_url_prefix) = "type.chatspn.spine.io"; -option java_package = "io.spine.examples.chatspn.user"; -option java_outer_classname = "UserChatsProto"; -option java_multiple_files = true; - -import "spine/core/user_id.proto"; -import "spine_examples/chatspn/identifiers.proto"; - -// Settings and information about chats in which the user is a member. -message UserChats { - option (entity) = { kind: AGGREGATE }; - - // The ID of the user chats owner. - spine.core.UserId owner = 1; - - // The chats in which the owner-user is a member. - repeated ChatId chats = 2; - - // The chats that the owner-user muted. - repeated ChatId muted_chats = 3; - - // The users that the owner-user blocked. - repeated spine.core.UserId blocked_users = 4; -} diff --git a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java index 09050fcf..f0049fb5 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/ChatsContext.java @@ -28,7 +28,6 @@ import io.spine.examples.chatspn.server.user.UserAggregate; import io.spine.examples.chatspn.server.user.UserBlocklistRepository; -import io.spine.examples.chatspn.server.user.UserChatsRepository; import io.spine.examples.chatspn.server.user.UserProfileRepository; import io.spine.server.BoundedContext; import io.spine.server.BoundedContextBuilder; @@ -56,7 +55,6 @@ public static BoundedContextBuilder newBuilder() { .singleTenant(NAME) .add(DefaultRepository.of(UserAggregate.class)) .add(new UserProfileRepository()) - .add(new UserChatsRepository()) .add(new UserBlocklistRepository()); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java index f21024e2..cb3b6ae3 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java @@ -29,7 +29,7 @@ import io.spine.core.Subscribe; import io.spine.core.UserId; import io.spine.examples.chatspn.user.UserBlocklist; -import io.spine.examples.chatspn.user.event.UserChatsCreated; +import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.server.projection.Projection; /** @@ -38,7 +38,7 @@ public final class UserBlocklistProjection extends Projection { @Subscribe - void on(UserChatsCreated e) { - builder().setId(e.getOwner()); + void on(UserRegistered e) { + builder().setId(e.getUser()); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java index 89cdd3e5..0a1cd195 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java @@ -29,7 +29,7 @@ import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; import io.spine.core.UserId; import io.spine.examples.chatspn.user.UserBlocklist; -import io.spine.examples.chatspn.user.event.UserChatsCreated; +import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.server.projection.ProjectionRepository; import io.spine.server.route.EventRouting; @@ -45,6 +45,6 @@ public final class UserBlocklistRepository @Override protected void setupEventRouting(EventRouting routing) { super.setupEventRouting(routing); - routing.route(UserChatsCreated.class, (event, context) -> withId(event.getOwner())); + routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())); } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java deleted file mode 100644 index 0b711d4e..00000000 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsAggregate.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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.chatspn.server.user; - -import io.spine.core.UserId; -import io.spine.examples.chatspn.user.UserChats; -import io.spine.examples.chatspn.user.event.UserChatsCreated; -import io.spine.examples.chatspn.user.event.UserRegistered; -import io.spine.server.aggregate.Aggregate; -import io.spine.server.aggregate.Apply; -import io.spine.server.event.React; - -/** - * The {@code UserChatsAggregate} controls the policy of joining chats - * and settings of chats in which the user is a member. - */ -public final class UserChatsAggregate - extends Aggregate { - - @React - UserChatsCreated on(UserRegistered e) { - return UserChatsCreated - .newBuilder() - .setOwner(e.getUser()) - .vBuild(); - } - - @Apply - private void event(UserChatsCreated e) { - builder().setOwner(e.getOwner()); - } -} diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java deleted file mode 100644 index a4bff9a5..00000000 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserChatsRepository.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * 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.chatspn.server.user; - -import com.google.errorprone.annotations.OverridingMethodsMustInvokeSuper; -import io.spine.core.UserId; -import io.spine.examples.chatspn.user.event.UserRegistered; -import io.spine.server.aggregate.AggregateRepository; -import io.spine.server.route.EventRouting; - -import static io.spine.server.route.EventRoute.withId; - -/** - * The repository for managing {@link UserChatsAggregate} instances. - */ -public final class UserChatsRepository - extends AggregateRepository { - - @OverridingMethodsMustInvokeSuper - @Override - protected void setupEventRouting(EventRouting routing) { - super.setupEventRouting(routing); - routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())); - } -} diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java index 44dc92e3..12bb2302 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java @@ -28,7 +28,6 @@ import io.spine.examples.chatspn.server.ChatsContext; import io.spine.examples.chatspn.user.UserBlocklist; -import io.spine.examples.chatspn.user.UserProfile; import io.spine.examples.chatspn.user.command.RegisterUser; import io.spine.server.BoundedContextBuilder; import io.spine.testing.core.given.GivenUserId; diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java deleted file mode 100644 index 317b49b8..00000000 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserChatsTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.chatspn.server.user; - -import io.spine.examples.chatspn.server.ChatsContext; -import io.spine.examples.chatspn.user.command.RegisterUser; -import io.spine.examples.chatspn.user.UserChats; -import io.spine.examples.chatspn.user.event.UserChatsCreated; -import io.spine.server.BoundedContextBuilder; -import io.spine.testing.core.given.GivenUserId; -import io.spine.testing.server.blackbox.ContextAwareTest; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static io.spine.testing.TestValues.randomString; - -@DisplayName("`UserChats` should") -class UserChatsTest extends ContextAwareTest { - - @Override - protected BoundedContextBuilder contextBuilder() { - return ChatsContext.newBuilder(); - } - - @Test - @DisplayName("react on `UserRegistered` and emit the `UserChatsCreated` event") - void creation() { - RegisterUser command = RegisterUser - .newBuilder() - .setUser(GivenUserId.generated()) - .setName(randomString()) - .vBuild(); - - context().receivesCommand(command); - - UserChatsCreated expectedEvent = UserChatsCreated - .newBuilder() - .setOwner(command.getUser()) - .build(); - UserChats expectedState = UserChats - .newBuilder() - .setOwner(command.getUser()) - .vBuild(); - - context().assertEvents() - .withType(UserChatsCreated.class) - .hasSize(1); - context().assertEvent(expectedEvent); - context().assertState(command.getUser(), expectedState); - } -} From 545b10a318574ea0bbb1afff94da93b6fb49dc6a Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 18:08:59 +0200 Subject: [PATCH 14/25] Define proto messages for user blocking flow. --- .../proto/spine_examples/chatspn/user/commands.proto | 10 ++++++++++ .../proto/spine_examples/chatspn/user/events.proto | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/model/src/main/proto/spine_examples/chatspn/user/commands.proto b/model/src/main/proto/spine_examples/chatspn/user/commands.proto index 3bf2b767..609c86b3 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/commands.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/commands.proto @@ -45,3 +45,13 @@ message RegisterUser { // A name of the user to register. string name = 2 [(required) = true]; } + +// Tells to block user. +message BlockUser { + + // The ID of the user who wants to block. + spine.core.UserId user_who_block = 1; + + // The ID of the user to be blocked. + spine.core.UserId user_to_block = 2; +} diff --git a/model/src/main/proto/spine_examples/chatspn/user/events.proto b/model/src/main/proto/spine_examples/chatspn/user/events.proto index 659d21c0..7f41ccae 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/events.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/events.proto @@ -45,3 +45,13 @@ message UserRegistered { // A name of the registered user. string name = 2 [(required) = true]; } + +// A user has been blocked. +message UserBlocked { + + // The ID of the user who blocked. + spine.core.UserId blocking_user = 1; + + // The ID of the user who was blocked. + spine.core.UserId blocked_user = 2; +} From d08157dd44452219d7108802a5c100af1432ec0f Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 19:25:34 +0200 Subject: [PATCH 15/25] Implement user blocking flow. --- .../chatspn/user/user_blocklist.proto | 12 +----------- .../chatspn/server/user/UserAggregate.java | 19 +++++++++++++++++++ .../server/user/UserBlocklistProjection.java | 6 ++++++ .../server/user/UserBlocklistRepository.java | 4 +++- 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto index 3391ee15..adaa8297 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto @@ -44,15 +44,5 @@ message UserBlocklist { spine.core.UserId id = 1; // User's blocklist. - repeated BlockedUser blockedUser = 2; - - // Represents a view of a blocked user. - message BlockedUser { - - // The ID of the blocked user. - spine.core.UserId user = 1; - - // The name of the blocked user. - string name = 2 [(required) = true]; - } + repeated spine.core.UserId blockedUser = 2; } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java index 06c8b6d2..85562ec7 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java @@ -28,7 +28,9 @@ import io.spine.core.UserId; import io.spine.examples.chatspn.user.User; +import io.spine.examples.chatspn.user.command.BlockUser; import io.spine.examples.chatspn.user.command.RegisterUser; +import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.server.aggregate.Aggregate; import io.spine.server.aggregate.Apply; @@ -56,4 +58,21 @@ private void event(UserRegistered e) { builder().setId(e.getUser()) .setName(e.getName()); } + + /** + * Handles the command to block a user. + */ + @Assign + UserBlocked handle(BlockUser c) { + return UserBlocked + .newBuilder() + .setBlockingUser(c.getUserWhoBlock()) + .setBlockedUser(c.getUserToBlock()) + .vBuild(); + } + + @Apply + private void event(UserBlocked e) { + builder().addBlockedUsers(e.getBlockedUser()); + } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java index cb3b6ae3..a609db9e 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java @@ -29,6 +29,7 @@ import io.spine.core.Subscribe; import io.spine.core.UserId; import io.spine.examples.chatspn.user.UserBlocklist; +import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.server.projection.Projection; @@ -41,4 +42,9 @@ public final class UserBlocklistProjection extends Projection routing) { super.setupEventRouting(routing); - routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())); + routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())) + .route(UserBlocked.class, (event, context) -> withId(event.getBlockingUser())); } } From e2b7b9caf83ad3b0eb7448b66b96c7f0075d840c Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:26:59 +0200 Subject: [PATCH 16/25] Add a positive test on user blocking. --- .../chatspn/server/given/UserTestEnv.java | 60 +++++++++++++++++++ .../chatspn/server/user/UserTest.java | 55 ++++++++++++----- 2 files changed, 101 insertions(+), 14 deletions(-) create mode 100644 server/src/test/java/io/spine/examples/chatspn/server/given/UserTestEnv.java diff --git a/server/src/test/java/io/spine/examples/chatspn/server/given/UserTestEnv.java b/server/src/test/java/io/spine/examples/chatspn/server/given/UserTestEnv.java new file mode 100644 index 00000000..e823e9b5 --- /dev/null +++ b/server/src/test/java/io/spine/examples/chatspn/server/given/UserTestEnv.java @@ -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.chatspn.server.given; + +import io.spine.examples.chatspn.user.User; +import io.spine.examples.chatspn.user.command.RegisterUser; +import io.spine.testing.core.given.GivenUserId; +import io.spine.testing.server.blackbox.BlackBoxContext; + +import static io.spine.testing.TestValues.randomString; + +public final class UserTestEnv { + + /** + * Prevents instantiation of this test environment. + */ + private UserTestEnv() { + } + + public static User registerRandomUser(BlackBoxContext context) { + User user = User + .newBuilder() + .setId(GivenUserId.generated()) + .setName(randomString()) + .vBuild(); + + RegisterUser command = RegisterUser + .newBuilder() + .setUser(user.getId()) + .setName(user.getName()) + .vBuild(); + + context.receivesCommand(command); + return user; + } +} diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java index 329ed48a..998cd495 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java @@ -28,15 +28,15 @@ import io.spine.examples.chatspn.server.ChatsContext; import io.spine.examples.chatspn.user.User; -import io.spine.examples.chatspn.user.command.RegisterUser; +import io.spine.examples.chatspn.user.command.BlockUser; +import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; import io.spine.server.BoundedContextBuilder; -import io.spine.testing.core.given.GivenUserId; import io.spine.testing.server.blackbox.ContextAwareTest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static io.spine.testing.TestValues.randomString; +import static io.spine.examples.chatspn.server.given.UserTestEnv.registerRandomUser; @DisplayName("`User` should") class UserTest extends ContextAwareTest { @@ -49,29 +49,56 @@ protected BoundedContextBuilder contextBuilder() { @Test @DisplayName("allow registration and emit the `UserRegistered` event") void registration() { - RegisterUser command = RegisterUser + User user = registerRandomUser(context()); + + UserRegistered expectedEvent = UserRegistered + .newBuilder() + .setUser(user.getId()) + .setName(user.getName()) + .build(); + User expectedState = User + .newBuilder() + .setId(user.getId()) + .setName(user.getName()) + .vBuild(); + + context().assertEvents() + .withType(UserRegistered.class) + .hasSize(1); + context().assertEvent(expectedEvent); + context().assertState(user.getId(), expectedState); + } + + @Test + @DisplayName("allow blocking another user and emit the `UserBlocked` event") + void blocking() { + User blockingUser = registerRandomUser(context()); + User userToBlock = registerRandomUser(context()); + + BlockUser blockingCommand = BlockUser .newBuilder() - .setUser(GivenUserId.generated()) - .setName(randomString()) + .setUserWhoBlock(blockingUser.getId()) + .setUserToBlock(userToBlock.getId()) .vBuild(); - context().receivesCommand(command); + context().receivesCommand(blockingCommand); - UserRegistered expectedEvent = UserRegistered + UserBlocked expectedEvent = UserBlocked .newBuilder() - .setUser(command.getUser()) - .setName(command.getName()) + .setBlockingUser(blockingUser.getId()) + .setBlockedUser(userToBlock.getId()) .build(); User expectedState = User .newBuilder() - .setId(command.getUser()) - .setName(command.getName()) + .setId(blockingUser.getId()) + .setName(blockingUser.getName()) + .addBlockedUsers(userToBlock.getId()) .vBuild(); context().assertEvents() - .withType(UserRegistered.class) + .withType(UserBlocked.class) .hasSize(1); context().assertEvent(expectedEvent); - context().assertState(command.getUser(), expectedState); + context().assertState(blockingCommand.getUserWhoBlock(), expectedState); } } From 5f94c4b79ab9ba5847704131af693f0dc4fc1c36 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 20:34:48 +0200 Subject: [PATCH 17/25] Update `UserBlocklistProjectionTest`. --- .../user/UserBlocklistProjectionTest.java | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java index 12bb2302..0edf6aa6 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java @@ -27,15 +27,15 @@ package io.spine.examples.chatspn.server.user; import io.spine.examples.chatspn.server.ChatsContext; +import io.spine.examples.chatspn.user.User; import io.spine.examples.chatspn.user.UserBlocklist; -import io.spine.examples.chatspn.user.command.RegisterUser; +import io.spine.examples.chatspn.user.command.BlockUser; import io.spine.server.BoundedContextBuilder; -import io.spine.testing.core.given.GivenUserId; import io.spine.testing.server.blackbox.ContextAwareTest; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import static io.spine.testing.TestValues.randomString; +import static io.spine.examples.chatspn.server.given.UserTestEnv.registerRandomUser; @DisplayName("`UserBlocklistProjection` should") class UserBlocklistProjectionTest extends ContextAwareTest { @@ -48,18 +48,36 @@ protected BoundedContextBuilder contextBuilder() { @Test @DisplayName("display `UserBlocklist`, as soon as `User` registered") void reactOnRegistration() { - RegisterUser command = RegisterUser + User user = registerRandomUser(context()); + + UserBlocklist expected = UserBlocklist + .newBuilder() + .setId(user.getId()) + .vBuild(); + + context().assertState(user.getId(), expected); + } + + @Test + @DisplayName("update `UserBlocklist`, as soon as the user blocked") + void reactOnBlocking() { + User blockingUser = registerRandomUser(context()); + User userToBlock = registerRandomUser(context()); + + BlockUser blockingCommand = BlockUser .newBuilder() - .setUser(GivenUserId.generated()) - .setName(randomString()) + .setUserWhoBlock(blockingUser.getId()) + .setUserToBlock(userToBlock.getId()) .vBuild(); - context().receivesCommand(command); + + context().receivesCommand(blockingCommand); UserBlocklist expected = UserBlocklist .newBuilder() - .setId(command.getUser()) + .setId(blockingUser.getId()) + .addBlockedUser(userToBlock.getId()) .vBuild(); - context().assertState(command.getUser(), expected); + context().assertState(blockingUser.getId(), expected); } } From 33a8973f39fdde36583e29ea24218b0ae67ce9e8 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Mon, 13 Mar 2023 21:34:59 +0200 Subject: [PATCH 18/25] Implement blocking rejection. --- .../chatspn/user/rejection/package-info.java | 36 +++++++++++++ .../chatspn/user/rejections.proto | 54 +++++++++++++++++++ .../chatspn/server/user/UserAggregate.java | 14 ++++- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 model/src/main/java/io/spine/examples/chatspn/user/rejection/package-info.java create mode 100644 model/src/main/proto/spine_examples/chatspn/user/rejections.proto diff --git a/model/src/main/java/io/spine/examples/chatspn/user/rejection/package-info.java b/model/src/main/java/io/spine/examples/chatspn/user/rejection/package-info.java new file mode 100644 index 00000000..1e2760aa --- /dev/null +++ b/model/src/main/java/io/spine/examples/chatspn/user/rejection/package-info.java @@ -0,0 +1,36 @@ +/* + * 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. + */ + +/** + * Provides ChatSPN User rejections. + */ +@CheckReturnValue +@ParametersAreNonnullByDefault +package io.spine.examples.chatspn.user.rejection; + +import com.google.errorprone.annotations.CheckReturnValue; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/model/src/main/proto/spine_examples/chatspn/user/rejections.proto b/model/src/main/proto/spine_examples/chatspn/user/rejections.proto new file mode 100644 index 00000000..10aaf180 --- /dev/null +++ b/model/src/main/proto/spine_examples/chatspn/user/rejections.proto @@ -0,0 +1,54 @@ +/* + * Copyright 2021, 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.chatspn.user; + +import "spine/options.proto"; + +option (type_url_prefix) = "type.chatspn.spine.io"; +option java_package = "io.spine.examples.chatspn.user.rejection"; + +// Set the value of `java_multiple_files` to `false` to instruct Protobuf Compiler to put all the +// rejection classes in one outer class. +// +// Then Spine Model Compiler for Java would generate `ThrowableMessage` classes for all +// these messages. These classes will be named after the classes of rejection messages. +// Putting rejection message classes under an outer class avoids name clash inside the package. +// +option java_multiple_files = false; + +import "spine/core/user_id.proto"; + +// A user cannot be blocked. +message UserCannotBeBlocked { + + // The ID of the user that could not block. + spine.core.UserId user_who_block = 1; + + // The ID of the user that could not be blocked. + spine.core.UserId user_to_block = 2; +} diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java index 85562ec7..633df77a 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java @@ -26,12 +26,14 @@ package io.spine.examples.chatspn.server.user; +import com.google.common.base.Objects; import io.spine.core.UserId; import io.spine.examples.chatspn.user.User; import io.spine.examples.chatspn.user.command.BlockUser; import io.spine.examples.chatspn.user.command.RegisterUser; import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.rejection.UserCannotBeBlocked; import io.spine.server.aggregate.Aggregate; import io.spine.server.aggregate.Apply; import io.spine.server.command.Assign; @@ -63,7 +65,17 @@ private void event(UserRegistered e) { * Handles the command to block a user. */ @Assign - UserBlocked handle(BlockUser c) { + UserBlocked handle(BlockUser c) throws UserCannotBeBlocked { + if (Objects.equal(c.getUserToBlock(), c.getUserWhoBlock()) || + state().getBlockedUsersList() + .contains(c.getUserToBlock())) { + throw UserCannotBeBlocked + .newBuilder() + .setUserWhoBlock(c.getUserWhoBlock()) + .setUserToBlock(c.getUserToBlock()) + .build(); + } + return UserBlocked .newBuilder() .setBlockingUser(c.getUserWhoBlock()) From 53f8fb379fab68031e0924ff85cab9300f3c6243 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Tue, 14 Mar 2023 13:10:31 +0200 Subject: [PATCH 19/25] Add a test for rejection to block a user. --- .../chatspn/server/user/UserTest.java | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java index 998cd495..ca023aea 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java @@ -31,6 +31,7 @@ import io.spine.examples.chatspn.user.command.BlockUser; import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.rejection.Rejections.UserCannotBeBlocked; import io.spine.server.BoundedContextBuilder; import io.spine.testing.server.blackbox.ContextAwareTest; import org.junit.jupiter.api.DisplayName; @@ -101,4 +102,56 @@ void blocking() { context().assertEvent(expectedEvent); context().assertState(blockingCommand.getUserWhoBlock(), expectedState); } + + @Test + @DisplayName("reject blocking himself") + void rejectSelfBlocking() { + User user = registerRandomUser(context()); + + BlockUser command = BlockUser + .newBuilder() + .setUserWhoBlock(user.getId()) + .setUserToBlock(user.getId()) + .vBuild(); + + context().receivesCommand(command); + + UserCannotBeBlocked expectedRejection = UserCannotBeBlocked + .newBuilder() + .setUserWhoBlock(user.getId()) + .setUserToBlock(user.getId()) + .vBuild(); + + context().assertEvents() + .withType(UserCannotBeBlocked.class) + .message(0) + .isEqualTo(expectedRejection); + } + + @Test + @DisplayName("reject blocking already blocked user") + void rejectReblocking() { + User blockingUser = registerRandomUser(context()); + User userToBlock = registerRandomUser(context()); + + BlockUser command = BlockUser + .newBuilder() + .setUserWhoBlock(blockingUser.getId()) + .setUserToBlock(userToBlock.getId()) + .vBuild(); + + context().receivesCommand(command); + context().receivesCommand(command); + + UserCannotBeBlocked expectedRejection = UserCannotBeBlocked + .newBuilder() + .setUserWhoBlock(blockingUser.getId()) + .setUserToBlock(userToBlock.getId()) + .vBuild(); + + context().assertEvents() + .withType(UserCannotBeBlocked.class) + .message(0) + .isEqualTo(expectedRejection); + } } From e43bf3a0b6e96b01cc2498c078d04c96a310c52e Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Tue, 14 Mar 2023 13:29:51 +0200 Subject: [PATCH 20/25] Define proto messages for user unblocking. --- .../spine_examples/chatspn/user/commands.proto | 12 +++++++++++- .../spine_examples/chatspn/user/events.proto | 12 +++++++++++- .../chatspn/user/rejections.proto | 18 ++++++++++++++---- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/model/src/main/proto/spine_examples/chatspn/user/commands.proto b/model/src/main/proto/spine_examples/chatspn/user/commands.proto index 609c86b3..d606ccfd 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/commands.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/commands.proto @@ -53,5 +53,15 @@ message BlockUser { spine.core.UserId user_who_block = 1; // The ID of the user to be blocked. - spine.core.UserId user_to_block = 2; + spine.core.UserId user_to_block = 2 [(required) = true]; +} + +// Tells to unblock user. +message UnblockUser { + + // The ID of the user who wants to unblock. + spine.core.UserId user_who_unblock = 1; + + // The ID of the user to be unblocked. + spine.core.UserId user_to_unblock = 2 [(required) = true]; } diff --git a/model/src/main/proto/spine_examples/chatspn/user/events.proto b/model/src/main/proto/spine_examples/chatspn/user/events.proto index 7f41ccae..7e63f5de 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/events.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/events.proto @@ -53,5 +53,15 @@ message UserBlocked { spine.core.UserId blocking_user = 1; // The ID of the user who was blocked. - spine.core.UserId blocked_user = 2; + spine.core.UserId blocked_user = 2 [(required) = true]; +} + +// A user has been unblocked. +message UserUnblocked { + + // The ID of the user who unblocked. + spine.core.UserId unblocking_user = 1; + + // The ID of the user who was unblocked. + spine.core.UserId unblocked_user = 2 [(required) = true]; } diff --git a/model/src/main/proto/spine_examples/chatspn/user/rejections.proto b/model/src/main/proto/spine_examples/chatspn/user/rejections.proto index 10aaf180..5e69ab92 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/rejections.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/rejections.proto @@ -46,9 +46,19 @@ import "spine/core/user_id.proto"; // A user cannot be blocked. message UserCannotBeBlocked { - // The ID of the user that could not block. - spine.core.UserId user_who_block = 1; + // The ID of the user that could not block. + spine.core.UserId user_who_block = 1; - // The ID of the user that could not be blocked. - spine.core.UserId user_to_block = 2; + // The ID of the user that could not be blocked. + spine.core.UserId user_to_block = 2 [(required) = true]; +} + +// A user cannot be unblocked. +message UserCannotBeUnblocked { + + // The ID of the user that could not unblock. + spine.core.UserId user_who_unblock = 1; + + // The ID of the user that could not be unblocked. + spine.core.UserId user_to_unblock = 2 [(required) = true]; } From 2b22040523d8b085327b3f300bfd058f7ed5422b Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:09:48 +0200 Subject: [PATCH 21/25] Implement user unblocking flow. --- .../spine_examples/chatspn/user/user.proto | 4 +- .../chatspn/server/user/UserAggregate.java | 39 +++++++++++++++++++ .../server/user/UserBlocklistProjection.java | 10 +++++ .../server/user/UserBlocklistRepository.java | 4 +- 4 files changed, 54 insertions(+), 3 deletions(-) diff --git a/model/src/main/proto/spine_examples/chatspn/user/user.proto b/model/src/main/proto/spine_examples/chatspn/user/user.proto index 559af2a0..8a8b0156 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/user.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user.proto @@ -57,9 +57,9 @@ message User { // The chats in which the user is a member. repeated ChatId chats = 4; - // The chats that the user muted. + // The chats that the user has muted. repeated ChatId muted_chats = 5; - // The users that the owner-user blocked. + // The users that the user has blocked. repeated spine.core.UserId blocked_users = 6; } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java index 633df77a..44903eab 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java @@ -31,9 +31,12 @@ import io.spine.examples.chatspn.user.User; import io.spine.examples.chatspn.user.command.BlockUser; import io.spine.examples.chatspn.user.command.RegisterUser; +import io.spine.examples.chatspn.user.command.UnblockUser; import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.event.UserUnblocked; import io.spine.examples.chatspn.user.rejection.UserCannotBeBlocked; +import io.spine.examples.chatspn.user.rejection.UserCannotBeUnblocked; import io.spine.server.aggregate.Aggregate; import io.spine.server.aggregate.Apply; import io.spine.server.command.Assign; @@ -63,6 +66,9 @@ private void event(UserRegistered e) { /** * Handles the command to block a user. + * + * @throws UserCannotBeBlocked + * if user tells to block himself or already blocked user. */ @Assign UserBlocked handle(BlockUser c) throws UserCannotBeBlocked { @@ -87,4 +93,37 @@ UserBlocked handle(BlockUser c) throws UserCannotBeBlocked { private void event(UserBlocked e) { builder().addBlockedUsers(e.getBlockedUser()); } + + /** + * Handles the command to unblock a user. + * + * @throws UserCannotBeUnblocked + * if user tells to unblock a non-blocked user. + */ + @Assign + UserUnblocked handle(UnblockUser c) throws UserCannotBeUnblocked { + if (!state().getBlockedUsersList() + .contains(c.getUserToUnblock())) { + throw UserCannotBeUnblocked + .newBuilder() + .setUserWhoUnblock(c.getUserWhoUnblock()) + .setUserToUnblock(c.getUserToUnblock()) + .build(); + } + + return UserUnblocked + .newBuilder() + .setUnblockingUser(c.getUserWhoUnblock()) + .setUnblockedUser(c.getUserToUnblock()) + .vBuild(); + } + + @Apply + private void event(UserUnblocked e) { + int unblockedUserIndex = state() + .getBlockedUsersList() + .indexOf(e.getUnblockedUser()); + + builder().removeBlockedUsers(unblockedUserIndex); + } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java index a609db9e..98e0cbfa 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistProjection.java @@ -31,6 +31,7 @@ import io.spine.examples.chatspn.user.UserBlocklist; import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.event.UserUnblocked; import io.spine.server.projection.Projection; /** @@ -47,4 +48,13 @@ void on(UserRegistered e) { void on(UserBlocked e) { builder().addBlockedUser(e.getBlockedUser()); } + + @Subscribe + void on(UserUnblocked e) { + int unblockedUserIndex = state() + .getBlockedUserList() + .indexOf(e.getUnblockedUser()); + + builder().removeBlockedUser(unblockedUserIndex); + } } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java index 00f36c11..d6113c8a 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserBlocklistRepository.java @@ -31,6 +31,7 @@ import io.spine.examples.chatspn.user.UserBlocklist; import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.event.UserUnblocked; import io.spine.server.projection.ProjectionRepository; import io.spine.server.route.EventRouting; @@ -47,6 +48,7 @@ public final class UserBlocklistRepository protected void setupEventRouting(EventRouting routing) { super.setupEventRouting(routing); routing.route(UserRegistered.class, (event, context) -> withId(event.getUser())) - .route(UserBlocked.class, (event, context) -> withId(event.getBlockingUser())); + .route(UserBlocked.class, (event, context) -> withId(event.getBlockingUser())) + .route(UserUnblocked.class, (event, context) -> withId(event.getUnblockingUser())); } } From 25c7da6f3d429a310ad69070c8a2800d3cf03c41 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:17:16 +0200 Subject: [PATCH 22/25] Fix naming in `repeated` fields in proto messages. --- .../main/proto/spine_examples/chatspn/user/user.proto | 6 +++--- .../examples/chatspn/server/user/UserAggregate.java | 10 +++++----- .../spine/examples/chatspn/server/user/UserTest.java | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/model/src/main/proto/spine_examples/chatspn/user/user.proto b/model/src/main/proto/spine_examples/chatspn/user/user.proto index 8a8b0156..963f12d7 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/user.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user.proto @@ -55,11 +55,11 @@ message User { google.protobuf.Timestamp when_last_active = 3; // The chats in which the user is a member. - repeated ChatId chats = 4; + repeated ChatId chat = 4; // The chats that the user has muted. - repeated ChatId muted_chats = 5; + repeated ChatId muted_chat = 5; // The users that the user has blocked. - repeated spine.core.UserId blocked_users = 6; + repeated spine.core.UserId blocked_user = 6; } diff --git a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java index 44903eab..6f7a8100 100644 --- a/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java +++ b/server/src/main/java/io/spine/examples/chatspn/server/user/UserAggregate.java @@ -73,7 +73,7 @@ private void event(UserRegistered e) { @Assign UserBlocked handle(BlockUser c) throws UserCannotBeBlocked { if (Objects.equal(c.getUserToBlock(), c.getUserWhoBlock()) || - state().getBlockedUsersList() + state().getBlockedUserList() .contains(c.getUserToBlock())) { throw UserCannotBeBlocked .newBuilder() @@ -91,7 +91,7 @@ UserBlocked handle(BlockUser c) throws UserCannotBeBlocked { @Apply private void event(UserBlocked e) { - builder().addBlockedUsers(e.getBlockedUser()); + builder().addBlockedUser(e.getBlockedUser()); } /** @@ -102,7 +102,7 @@ private void event(UserBlocked e) { */ @Assign UserUnblocked handle(UnblockUser c) throws UserCannotBeUnblocked { - if (!state().getBlockedUsersList() + if (!state().getBlockedUserList() .contains(c.getUserToUnblock())) { throw UserCannotBeUnblocked .newBuilder() @@ -121,9 +121,9 @@ UserUnblocked handle(UnblockUser c) throws UserCannotBeUnblocked { @Apply private void event(UserUnblocked e) { int unblockedUserIndex = state() - .getBlockedUsersList() + .getBlockedUserList() .indexOf(e.getUnblockedUser()); - builder().removeBlockedUsers(unblockedUserIndex); + builder().removeBlockedUser(unblockedUserIndex); } } diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java index ca023aea..9a74c1c3 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java @@ -93,7 +93,7 @@ void blocking() { .newBuilder() .setId(blockingUser.getId()) .setName(blockingUser.getName()) - .addBlockedUsers(userToBlock.getId()) + .addBlockedUser(userToBlock.getId()) .vBuild(); context().assertEvents() From f71252962765fefd4fe1508e91e71e530545f943 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:42:14 +0200 Subject: [PATCH 23/25] Add tests on user unblocking flow. --- .../user/UserBlocklistProjectionTest.java | 31 +++++++++ .../chatspn/server/user/UserTest.java | 69 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java index 0edf6aa6..edac5583 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserBlocklistProjectionTest.java @@ -30,6 +30,7 @@ import io.spine.examples.chatspn.user.User; import io.spine.examples.chatspn.user.UserBlocklist; import io.spine.examples.chatspn.user.command.BlockUser; +import io.spine.examples.chatspn.user.command.UnblockUser; import io.spine.server.BoundedContextBuilder; import io.spine.testing.server.blackbox.ContextAwareTest; import org.junit.jupiter.api.DisplayName; @@ -80,4 +81,34 @@ void reactOnBlocking() { context().assertState(blockingUser.getId(), expected); } + + @Test + @DisplayName("update `UserBlocklist`, as soon as the user unblocked") + void reactOnUnblocking() { + User unblockingUser = registerRandomUser(context()); + User userToUnblock = registerRandomUser(context()); + + BlockUser blockingCommand = BlockUser + .newBuilder() + .setUserWhoBlock(unblockingUser.getId()) + .setUserToBlock(userToUnblock.getId()) + .vBuild(); + + context().receivesCommand(blockingCommand); + + UnblockUser unblockingCommand = UnblockUser + .newBuilder() + .setUserWhoUnblock(unblockingUser.getId()) + .setUserToUnblock(userToUnblock.getId()) + .vBuild(); + + context().receivesCommand(unblockingCommand); + + UserBlocklist expected = UserBlocklist + .newBuilder() + .setId(unblockingUser.getId()) + .vBuild(); + + context().assertState(unblockingUser.getId(), expected); + } } diff --git a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java index 9a74c1c3..c7b2562d 100644 --- a/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java +++ b/server/src/test/java/io/spine/examples/chatspn/server/user/UserTest.java @@ -29,9 +29,12 @@ import io.spine.examples.chatspn.server.ChatsContext; import io.spine.examples.chatspn.user.User; import io.spine.examples.chatspn.user.command.BlockUser; +import io.spine.examples.chatspn.user.command.UnblockUser; import io.spine.examples.chatspn.user.event.UserBlocked; import io.spine.examples.chatspn.user.event.UserRegistered; +import io.spine.examples.chatspn.user.event.UserUnblocked; import io.spine.examples.chatspn.user.rejection.Rejections.UserCannotBeBlocked; +import io.spine.examples.chatspn.user.rejection.Rejections.UserCannotBeUnblocked; import io.spine.server.BoundedContextBuilder; import io.spine.testing.server.blackbox.ContextAwareTest; import org.junit.jupiter.api.DisplayName; @@ -154,4 +157,70 @@ void rejectReblocking() { .message(0) .isEqualTo(expectedRejection); } + + @Test + @DisplayName("allow unblocking blocked user and emit the `UserUnblocked` event") + void unblocking() { + User unblockingUser = registerRandomUser(context()); + User userToUnblock = registerRandomUser(context()); + + BlockUser blockingCommand = BlockUser + .newBuilder() + .setUserWhoBlock(unblockingUser.getId()) + .setUserToBlock(userToUnblock.getId()) + .vBuild(); + + context().receivesCommand(blockingCommand); + + UnblockUser unblockingCommand = UnblockUser + .newBuilder() + .setUserWhoUnblock(unblockingUser.getId()) + .setUserToUnblock(userToUnblock.getId()) + .vBuild(); + + context().receivesCommand(unblockingCommand); + + UserUnblocked expectedEvent = UserUnblocked + .newBuilder() + .setUnblockingUser(unblockingUser.getId()) + .setUnblockedUser(userToUnblock.getId()) + .build(); + User expectedState = User + .newBuilder() + .setId(unblockingUser.getId()) + .setName(unblockingUser.getName()) + .vBuild(); + + context().assertEvents() + .withType(UserUnblocked.class) + .hasSize(1); + context().assertEvent(expectedEvent); + context().assertState(unblockingCommand.getUserWhoUnblock(), expectedState); + } + + @Test + @DisplayName("reject unblocking a non-blocked user") + void rejectUnblockingNonblocked() { + User unblockingUser = registerRandomUser(context()); + User userToUnblock = registerRandomUser(context()); + + UnblockUser unblockingCommand = UnblockUser + .newBuilder() + .setUserWhoUnblock(unblockingUser.getId()) + .setUserToUnblock(userToUnblock.getId()) + .vBuild(); + + context().receivesCommand(unblockingCommand); + + UserCannotBeUnblocked expectedRejection = UserCannotBeUnblocked + .newBuilder() + .setUserWhoUnblock(unblockingUser.getId()) + .setUserToUnblock(userToUnblock.getId()) + .build(); + + context().assertEvents() + .withType(UserCannotBeUnblocked.class) + .message(0) + .isEqualTo(expectedRejection); + } } From 683d40540a27b2317a42b7385aa607a36cf80876 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Tue, 14 Mar 2023 14:47:26 +0200 Subject: [PATCH 24/25] Update copyright in `rejections.proto`. --- .../src/main/proto/spine_examples/chatspn/user/rejections.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/src/main/proto/spine_examples/chatspn/user/rejections.proto b/model/src/main/proto/spine_examples/chatspn/user/rejections.proto index 5e69ab92..c8578cab 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/rejections.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/rejections.proto @@ -1,5 +1,5 @@ /* - * Copyright 2021, TeamDev. All rights reserved. + * 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. From 382d9d0c7c952569c122e82563ab7eb00b700aa5 Mon Sep 17 00:00:00 2001 From: Hellamb <106074440+Vladyslav-Kuksiuk@users.noreply.github.com> Date: Tue, 14 Mar 2023 15:17:44 +0200 Subject: [PATCH 25/25] Fix `blocked_user` field name in `user_blocklist.proto`. --- .../main/proto/spine_examples/chatspn/user/user_blocklist.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto index adaa8297..e256a92a 100644 --- a/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto +++ b/model/src/main/proto/spine_examples/chatspn/user/user_blocklist.proto @@ -44,5 +44,5 @@ message UserBlocklist { spine.core.UserId id = 1; // User's blocklist. - repeated spine.core.UserId blockedUser = 2; + repeated spine.core.UserId blocked_user = 2; }