diff --git a/.github/keys/spine-dev-framework-ci.json.gpg b/.github/keys/spine-dev-framework-ci.json.gpg
new file mode 100644
index 00000000..3041f37e
Binary files /dev/null and b/.github/keys/spine-dev-framework-ci.json.gpg differ
diff --git a/.github/keys/spine-dev.json.gpg b/.github/keys/spine-dev.json.gpg
deleted file mode 100644
index 49f7fd52..00000000
Binary files a/.github/keys/spine-dev.json.gpg and /dev/null differ
diff --git a/.github/workflows/build-on-ubuntu.yml b/.github/workflows/build-on-ubuntu.yml
index e77b63c5..d7e73081 100644
--- a/.github/workflows/build-on-ubuntu.yml
+++ b/.github/workflows/build-on-ubuntu.yml
@@ -19,9 +19,9 @@ jobs:
# This operation is specific to `gcloud-java` repository only.
- name: Decrypt the credentials for the Spine-Dev service account
- run: ./scripts/decrypt.sh "$SPINE_DEV_KEY" ./.github/keys/spine-dev.json.gpg ./spine-dev.json
+ run: ./config/scripts/decrypt.sh "$SPINE_DEV_CI_KEY" ./.github/keys/spine-dev-framework-ci.json.gpg ./spine-dev.json
env:
- SPINE_DEV_KEY: ${{ secrets.SPINE_DEV_KEY }}
+ SPINE_DEV_CI_KEY: ${{ secrets.SPINE_DEV_CI_KEY }}
# The OS-managed Google Cloud SDK does not provide a Datastore emulator.
- name: Remove the OS-managed Google Cloud SDK
diff --git a/README.md b/README.md
index 3f6780b9..a79bab99 100644
--- a/README.md
+++ b/README.md
@@ -16,16 +16,16 @@ Gradle:
```kotlin
dependencies {
// Datastore Storage support library.
- implementation("io.spine.gcloud:spine-datastore:1.8.0")
+ implementation("io.spine.gcloud:spine-datastore:1.9.1")
// Pub/Sub messaging support library.
- implementation("io.spine.gcloud:spine-pubsub:1.8.0")
+ implementation("io.spine.gcloud:spine-pubsub:1.9.1")
// Stackdriver Trace support library.
- implementation("io.spine.gcloud:spine-stackdriver-trace:1.8.0")
+ implementation("io.spine.gcloud:spine-stackdriver-trace:1.9.1")
// Datastore-related test utilities (if needed).
- implementation("io.spine.gcloud:testutil-gcloud:1.8.0")
+ implementation("io.spine.gcloud:testutil-gcloud:1.9.1")
}
```
diff --git a/datastore/src/main/java/io/spine/server/storage/datastore/DsColumnMapping.java b/datastore/src/main/java/io/spine/server/storage/datastore/DsColumnMapping.java
index 847a2cda..b76a62ca 100644
--- a/datastore/src/main/java/io/spine/server/storage/datastore/DsColumnMapping.java
+++ b/datastore/src/main/java/io/spine/server/storage/datastore/DsColumnMapping.java
@@ -39,11 +39,16 @@
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import com.google.protobuf.Timestamp;
+import io.spine.annotation.SPI;
import io.spine.core.Version;
import io.spine.server.entity.storage.AbstractColumnMapping;
+import io.spine.server.entity.storage.ColumnMapping;
import io.spine.server.entity.storage.ColumnTypeMapping;
import io.spine.string.Stringifiers;
+import java.util.HashMap;
+import java.util.Map;
+
import static com.google.cloud.Timestamp.ofTimeSecondsAndNanos;
/**
@@ -52,16 +57,57 @@
*
All column values are stored as Datastore {@link Value}-s.
*
*
Users of the storage can extend this class to specify their own column mapping for the
- * selected types.
+ * selected types. See {@link DsColumnMapping#customMapping() DsColumnMapping.customMapping()}.
+ *
+ * @see DatastoreStorageFactory.Builder#setColumnMapping(ColumnMapping)
*/
public class DsColumnMapping extends AbstractColumnMapping> {
+ private static final Map, ColumnTypeMapping, ? extends Value>>> defaults
+ = ImmutableMap.of(Timestamp.class, ofTimestamp(),
+ Version.class, ofVersion());
+
+ /**
+ * {@inheritDoc}
+ *
+ * Merges the default column mapping rules with those provided by SPI users.
+ * In case there are duplicate mappings for some column type, the value provided
+ * by SPI users is used.
+ *
+ * @apiNote This method is made {@code final}, as it is designed
+ * to use {@code ImmutableMap.Builder}, which does not allow to override values.
+ * Therefore, it is not possible for SPI users to provide their own mapping rules
+ * for types such as {@code Timestamp}, for which this class already has
+ * a default mapping. SPI users should override
+ * {@link #customMapping() DsColumnMapping.customMapping()} instead.
+ */
@Override
- protected void
+ protected final void
setupCustomMapping(
ImmutableMap.Builder, ColumnTypeMapping, ? extends Value>>> builder) {
- builder.put(Timestamp.class, ofTimestamp());
- builder.put(Version.class, ofVersion());
+ Map, ColumnTypeMapping, ? extends Value>>> merged = new HashMap<>();
+ ImmutableMap, ColumnTypeMapping, ? extends Value>>> custom = customMapping();
+ merged.putAll(defaults);
+ merged.putAll(custom);
+ builder.putAll(merged);
+ }
+
+ /**
+ * Returns the custom column mapping rules.
+ *
+ * This method is designed for SPI users in order to be able to re-define
+ * and-or append their custom mapping. As by default, {@code DsColumnMapping}
+ * provides rules for {@link Timestamp} and {@link Version}, SPI users may
+ * choose to either override these defaults by returning their own mapping for these types,
+ * or supply even more mapping rules.
+ *
+ *
By default, this method returns an empty map.
+ *
+ * @return custom column mappings, per Java class of column
+ */
+ @SPI
+ protected ImmutableMap, ColumnTypeMapping, ? extends Value>>> customMapping() {
+ return ImmutableMap.of();
}
@Override
@@ -120,16 +166,26 @@ protected ColumnTypeMapping ofMessage() {
return o -> NullValue.of();
}
- @SuppressWarnings({"ProtoTimestampGetSecondsGetNano", "UnnecessaryLambda"})
- // This behavior is intended.
- private static ColumnTypeMapping ofTimestamp() {
+ /**
+ * Returns the default mapping from {@link Timestamp} to {@link TimestampValue}.
+ */
+ @SuppressWarnings({
+ "ProtoTimestampGetSecondsGetNano" /* In order to create exact value. */,
+ "UnnecessaryLambda" /* For brevity.*/,
+ "WeakerAccess" /* To allow access for SPI users. */})
+ protected static ColumnTypeMapping ofTimestamp() {
return timestamp -> TimestampValue.of(
ofTimeSecondsAndNanos(timestamp.getSeconds(), timestamp.getNanos())
);
}
- @SuppressWarnings("UnnecessaryLambda")
- private static ColumnTypeMapping ofVersion() {
+ /**
+ * Returns the default mapping from {@link Version} to {@link LongValue}.
+ */
+ @SuppressWarnings({
+ "UnnecessaryLambda" /* For brevity.*/,
+ "WeakerAccess" /* To allow access for SPI users. */})
+ protected static ColumnTypeMapping ofVersion() {
return version -> LongValue.of(version.getNumber());
}
}
diff --git a/datastore/src/test/java/io/spine/server/storage/datastore/DsProjectionColumnsTest.java b/datastore/src/test/java/io/spine/server/storage/datastore/DsProjectionColumnsTest.java
new file mode 100644
index 00000000..5244e618
--- /dev/null
+++ b/datastore/src/test/java/io/spine/server/storage/datastore/DsProjectionColumnsTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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.server.storage.datastore;
+
+import com.google.cloud.datastore.Key;
+import com.google.protobuf.Timestamp;
+import io.spine.core.Version;
+import io.spine.server.ContextSpec;
+import io.spine.server.projection.ProjectionStorage;
+import io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.CustomMapping;
+import io.spine.test.datastore.College;
+import io.spine.test.datastore.CollegeId;
+import io.spine.testing.server.storage.datastore.TestDatastoreStorageFactory;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+
+import static com.google.common.truth.Truth.assertThat;
+import static io.spine.server.storage.datastore.RecordId.ofEntityId;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.COLLEGE_CLS;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.COLLEGE_KIND;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.clearAdmission;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.futureFromNow;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.newCollege;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.newId;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.someVersion;
+import static io.spine.server.storage.datastore.given.DsProjectionColumnsTestEnv.writeAndReadDeadline;
+import static io.spine.server.storage.datastore.given.TestEnvironment.singleTenantSpec;
+import static io.spine.testing.server.storage.datastore.TestDatastoreStorageFactory.local;
+
+@DisplayName("When dealing with `Projection` columns, `DsProjectionStorage` should")
+final class DsProjectionColumnsTest {
+
+ private static final TestDatastoreStorageFactory datastoreFactory = local(new CustomMapping());
+
+ @Test
+ @DisplayName("allow clearing the column values " +
+ "if the column mapping used returns Datastore-specific `null` " +
+ "for their values")
+ void clearTimestampColumns() {
+ ContextSpec spec = singleTenantSpec();
+ ProjectionStorage storage =
+ datastoreFactory.createProjectionStorage(spec, COLLEGE_CLS);
+ DatastoreWrapper datastore = datastoreFactory.createDatastoreWrapper(false);
+
+ CollegeId id = newId();
+ Key key = datastore.keyFor(COLLEGE_KIND, ofEntityId(id));
+ Version version = someVersion();
+
+ Timestamp admissionDeadline = futureFromNow();
+ College college = newCollege(id, admissionDeadline);
+
+ com.google.cloud.Timestamp storedDeadline =
+ writeAndReadDeadline(college, version, storage, datastore, key);
+ assertThat(storedDeadline).isNotNull();
+
+ College collegeNoAdmission = clearAdmission(college);
+ com.google.cloud.Timestamp presumablyEmptyDeadline =
+ writeAndReadDeadline(collegeNoAdmission, version, storage, datastore, key);
+ assertThat(presumablyEmptyDeadline)
+ .isNull();
+ }
+}
diff --git a/datastore/src/test/java/io/spine/server/storage/datastore/given/DsProjectionColumnsTestEnv.java b/datastore/src/test/java/io/spine/server/storage/datastore/given/DsProjectionColumnsTestEnv.java
new file mode 100644
index 00000000..1bc7b38d
--- /dev/null
+++ b/datastore/src/test/java/io/spine/server/storage/datastore/given/DsProjectionColumnsTestEnv.java
@@ -0,0 +1,175 @@
+/*
+ * 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.server.storage.datastore.given;
+
+import com.google.cloud.datastore.Entity;
+import com.google.cloud.datastore.Key;
+import com.google.cloud.datastore.NullValue;
+import com.google.cloud.datastore.TimestampValue;
+import com.google.cloud.datastore.Value;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.Timestamp;
+import com.google.protobuf.util.Durations;
+import com.google.protobuf.util.Timestamps;
+import io.spine.base.Time;
+import io.spine.core.Version;
+import io.spine.core.Versions;
+import io.spine.server.entity.EntityRecord;
+import io.spine.server.entity.given.Given;
+import io.spine.server.entity.storage.ColumnTypeMapping;
+import io.spine.server.entity.storage.EntityRecordWithColumns;
+import io.spine.server.projection.ProjectionStorage;
+import io.spine.server.storage.datastore.DatastoreWrapper;
+import io.spine.server.storage.datastore.DsColumnMapping;
+import io.spine.server.storage.datastore.Kind;
+import io.spine.server.storage.datastore.tenant.given.CollegeProjection;
+import io.spine.test.datastore.College;
+import io.spine.test.datastore.CollegeId;
+import io.spine.type.TypeUrl;
+
+import static com.google.cloud.Timestamp.ofTimeSecondsAndNanos;
+import static io.spine.base.Identifier.newUuid;
+import static io.spine.protobuf.AnyPacker.pack;
+
+/**
+ * A test environment for {@link DsProjectionColumnsTest}.
+ */
+public final class DsProjectionColumnsTestEnv {
+
+ public static final Class COLLEGE_CLS = CollegeProjection.class;
+ public static final Kind COLLEGE_KIND = Kind.of(TypeUrl.from(College.getDescriptor()));
+
+ /**
+ * Prevents this test environment from instantiation.
+ */
+ private DsProjectionColumnsTestEnv() {
+ }
+
+ public static com.google.cloud.Timestamp
+ writeAndReadDeadline(College college,
+ Version version,
+ ProjectionStorage storage,
+ DatastoreWrapper datastore,
+ Key key) {
+ EntityRecord record = toEntityRecord(college, version);
+ CollegeProjection projection = entityWith(college, version);
+ EntityRecordWithColumns recordWithCols =
+ EntityRecordWithColumns.create(record, projection, storage);
+ storage.write(college.getId(), recordWithCols);
+ Entity response = datastore.read(key);
+ com.google.cloud.Timestamp storedDeadline = readAdmissionDeadline(response);
+ return storedDeadline;
+ }
+
+ public static College newCollege(CollegeId id, Timestamp admissionDeadline) {
+ College college =
+ College.newBuilder()
+ .setId(id)
+ .setName("Alma")
+ .setStudentCount(42)
+ .setAdmissionDeadline(admissionDeadline)
+ .setPassingGrade(4.2)
+ .setStateSponsored(false)
+ .setCreated(Time.currentTime())
+ .addAllSubjects(
+ ImmutableList.of("English Literature", "Engineering","Psychology"))
+ .vBuild();
+ return college;
+ }
+
+ private static EntityRecord toEntityRecord(College college, Version version) {
+ EntityRecord recordNoAdmission = EntityRecord
+ .newBuilder()
+ .setState(pack(college))
+ .setVersion(version)
+ .vBuild();
+ return recordNoAdmission;
+ }
+
+ public static College clearAdmission(College college) {
+ return college.toBuilder()
+ .clearAdmissionDeadline()
+ .vBuild();
+ }
+
+ private static CollegeProjection entityWith(College state, Version version) {
+ CollegeProjection projection =
+ Given.projectionOfClass(CollegeProjection.class)
+ .withId(state.getId())
+ .withState(state)
+ .withVersion(version.getNumber())
+ .build();
+ return projection;
+ }
+
+ public static Version someVersion() {
+ return Versions.newVersion(42, Time.currentTime());
+ }
+
+ public static Timestamp futureFromNow() {
+ return Timestamps.add(Time.currentTime(), Durations.fromDays(100));
+ }
+
+ public static CollegeId newId() {
+ return CollegeId.newBuilder()
+ .setValue(newUuid())
+ .vBuild();
+ }
+
+ private static com.google.cloud.Timestamp readAdmissionDeadline(Entity response) {
+ com.google.cloud.Timestamp storedTimestamp = response.getTimestamp(
+ College.Column.admissionDeadline()
+ .name()
+ .value());
+ return storedTimestamp;
+ }
+
+ /**
+ * A mapping similar to the default one,
+ * but telling to store {@link Timestamp}s as {@code null}s.
+ */
+ public static final class CustomMapping extends DsColumnMapping {
+
+ @Override
+ protected ImmutableMap, ColumnTypeMapping, ? extends Value>>> customMapping() {
+ return ImmutableMap.of(Timestamp.class, ofNullableTimestamp());
+ }
+
+ @SuppressWarnings("UnnecessaryLambda" /* For brevity */)
+ private static ColumnTypeMapping> ofNullableTimestamp() {
+ return timestamp -> {
+ if (timestamp.equals(Timestamp.getDefaultInstance())) {
+ return NullValue.of();
+ }
+ return TimestampValue.of(
+ ofTimeSecondsAndNanos(timestamp.getSeconds(), timestamp.getNanos())
+ );
+ };
+ }
+ }
+}
diff --git a/datastore/src/test/java/io/spine/server/storage/datastore/tenant/NamespaceIndexTest.java b/datastore/src/test/java/io/spine/server/storage/datastore/tenant/NamespaceIndexTest.java
index 430468ae..463c81bf 100644
--- a/datastore/src/test/java/io/spine/server/storage/datastore/tenant/NamespaceIndexTest.java
+++ b/datastore/src/test/java/io/spine/server/storage/datastore/tenant/NamespaceIndexTest.java
@@ -41,8 +41,9 @@
import io.spine.server.entity.EntityRecord;
import io.spine.server.storage.RecordStorage;
import io.spine.server.storage.datastore.DatastoreStorageFactory;
-import io.spine.server.storage.datastore.tenant.given.TestProjection;
+import io.spine.server.storage.datastore.tenant.given.CollegeProjection;
import io.spine.test.datastore.College;
+import io.spine.test.datastore.CollegeId;
import io.spine.testing.TestValues;
import io.spine.testing.server.storage.datastore.TestDatastores;
import org.junit.jupiter.api.AfterEach;
@@ -174,14 +175,16 @@ void findPrefixedNamespaces() {
.use(storageFactory);
storageFactory.configureTenantIndex(contextBuilder);
BoundedContext context = contextBuilder.build();
- RecordStorage storage = storageFactory
- .createRecordStorage(context.spec(), TestProjection.class);
- String id = "ABC";
+ RecordStorage storage = storageFactory
+ .createRecordStorage(context.spec(), CollegeProjection.class);
+ CollegeId id = CollegeId.newBuilder()
+ .setValue("ABC")
+ .vBuild();
EntityRecord record = EntityRecord
.newBuilder()
.setEntityId(Identifier.pack(id))
.setState(pack(College.newBuilder()
- .setName(id)
+ .setName(id.getValue())
.build()))
.build();
TenantId tenantId = TenantId
diff --git a/datastore/src/test/java/io/spine/server/storage/datastore/tenant/given/TestProjection.java b/datastore/src/test/java/io/spine/server/storage/datastore/tenant/given/CollegeProjection.java
similarity index 90%
rename from datastore/src/test/java/io/spine/server/storage/datastore/tenant/given/TestProjection.java
rename to datastore/src/test/java/io/spine/server/storage/datastore/tenant/given/CollegeProjection.java
index 76f67a60..966439dc 100644
--- a/datastore/src/test/java/io/spine/server/storage/datastore/tenant/given/TestProjection.java
+++ b/datastore/src/test/java/io/spine/server/storage/datastore/tenant/given/CollegeProjection.java
@@ -28,6 +28,8 @@
import io.spine.server.projection.Projection;
import io.spine.test.datastore.College;
+import io.spine.test.datastore.CollegeId;
-public final class TestProjection extends Projection {
+public final class CollegeProjection
+ extends Projection {
}
diff --git a/license-report.md b/license-report.md
index 3ec3e4d0..16043fc1 100644
--- a/license-report.md
+++ b/license-report.md
@@ -1,6 +1,6 @@
-# Dependencies of `io.spine.gcloud:spine-datastore:1.9.0`
+# Dependencies of `io.spine.gcloud:spine-datastore:1.9.1`
## Runtime
1. **Group:** com.fasterxml.jackson **Name:** jackson-bom **Version:** 2.14.2 **No license information found**
@@ -722,12 +722,12 @@
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri May 19 11:08:42 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Sun Sep 24 16:41:14 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.gcloud:spine-pubsub:1.9.0`
+# Dependencies of `io.spine.gcloud:spine-pubsub:1.9.1`
## Runtime
1. **Group:** com.google.android **Name:** annotations **Version:** 4.1.1.4
@@ -1149,12 +1149,12 @@ This report was generated on **Fri May 19 11:08:42 WEST 2023** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri May 19 11:08:54 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Sun Sep 24 16:41:22 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.gcloud:spine-stackdriver-trace:1.9.0`
+# Dependencies of `io.spine.gcloud:spine-stackdriver-trace:1.9.1`
## Runtime
1. **Group:** com.google.android **Name:** annotations **Version:** 4.1.1.4
@@ -1784,12 +1784,12 @@ This report was generated on **Fri May 19 11:08:54 WEST 2023** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri May 19 11:09:10 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
+This report was generated on **Sun Sep 24 16:41:35 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
-# Dependencies of `io.spine.gcloud:spine-testutil-gcloud:1.9.0`
+# Dependencies of `io.spine.gcloud:spine-testutil-gcloud:1.9.1`
## Runtime
1. **Group:** com.fasterxml.jackson **Name:** jackson-bom **Version:** 2.14.2 **No license information found**
@@ -2511,4 +2511,4 @@ This report was generated on **Fri May 19 11:09:10 WEST 2023** using [Gradle-Lic
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
-This report was generated on **Fri May 19 11:09:19 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
+This report was generated on **Sun Sep 24 16:41:42 WEST 2023** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 62d27698..ee410ae1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@ all modules and does not describe the project structure per-subproject.
io.spine.gcloud
spine-gcloud-java
-1.9.0
+1.9.1
2015
diff --git a/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/SpyStorageFactory.java b/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/SpyStorageFactory.java
index 4689fed8..1632e2b1 100644
--- a/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/SpyStorageFactory.java
+++ b/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/SpyStorageFactory.java
@@ -57,7 +57,7 @@ public SpyStorageFactory() {
}
@Override
- protected DatastoreWrapper createDatastoreWrapper(boolean multitenant) {
+ public DatastoreWrapper createDatastoreWrapper(boolean multitenant) {
return injectedWrapper;
}
diff --git a/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/TestDatastoreStorageFactory.java b/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/TestDatastoreStorageFactory.java
index dd4faf99..9e6b6cec 100644
--- a/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/TestDatastoreStorageFactory.java
+++ b/testutil-gcloud/src/main/java/io/spine/testing/server/storage/datastore/TestDatastoreStorageFactory.java
@@ -27,9 +27,11 @@
package io.spine.testing.server.storage.datastore;
import com.google.cloud.datastore.Datastore;
+import com.google.cloud.datastore.Value;
import com.google.common.collect.ImmutableSet;
import com.google.common.flogger.FluentLogger;
import io.spine.annotation.Internal;
+import io.spine.server.entity.storage.ColumnMapping;
import io.spine.server.storage.datastore.DatastoreStorageFactory;
import io.spine.server.storage.datastore.DatastoreWrapper;
import io.spine.server.storage.datastore.DsColumnMapping;
@@ -52,10 +54,14 @@ public class TestDatastoreStorageFactory extends DatastoreStorageFactory {
private final Collection allCreatedWrappers = new HashSet<>();
protected TestDatastoreStorageFactory(Datastore datastore) {
+ this(datastore, new DsColumnMapping());
+ }
+
+ protected TestDatastoreStorageFactory(Datastore datastore, ColumnMapping> mapping) {
super(DatastoreStorageFactory
.newBuilder()
.setDatastore(datastore)
- .setColumnMapping(new DsColumnMapping())
+ .setColumnMapping(mapping)
);
}
@@ -68,6 +74,15 @@ public static TestDatastoreStorageFactory local() {
return basedOn(TestDatastores.local());
}
+ /**
+ * Creates a new instance which works with a local Datastore emulator.
+ *
+ * A shortcut for {@code basedOn(TestDatastores.local())}.
+ */
+ public static TestDatastoreStorageFactory local(ColumnMapping> mapping) {
+ return basedOn(TestDatastores.local(), mapping);
+ }
+
/**
* Creates a new factory instance which wraps the given Datastore.
*/
@@ -76,9 +91,18 @@ public static TestDatastoreStorageFactory basedOn(Datastore datastore) {
return new TestDatastoreStorageFactory(datastore);
}
+ /**
+ * Creates a new factory instance which wraps the given Datastore.
+ */
+ private static TestDatastoreStorageFactory
+ basedOn(Datastore datastore, ColumnMapping> mapping) {
+ checkNotNull(datastore);
+ return new TestDatastoreStorageFactory(datastore, mapping);
+ }
+
@Internal
@Override
- protected DatastoreWrapper createDatastoreWrapper(boolean multitenant) {
+ public DatastoreWrapper createDatastoreWrapper(boolean multitenant) {
TestDatastoreWrapper wrapper = TestDatastoreWrapper.wrap(datastore(), false);
allCreatedWrappers.add(wrapper);
return wrapper;
diff --git a/version.gradle.kts b/version.gradle.kts
index 9e19e396..c4de9fe4 100644
--- a/version.gradle.kts
+++ b/version.gradle.kts
@@ -37,4 +37,4 @@ val cloudTraceVersion: String by extra("2.14.0")
val spineBaseVersion: String by extra("1.9.0")
val spineCoreVersion: String by extra("1.9.0")
-val versionToPublish: String by extra("1.9.0")
+val versionToPublish: String by extra("1.9.1")