Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b7a0b6e
Init `testutil-gcloud` module
dmitrykuzmin Sep 30, 2019
e62da41
Extract `gcloud` test utils to `testutil-gcloud` module
dmitrykuzmin Sep 30, 2019
dced052
Remove `BigDataTester` from `testutil-gcloud`
dmitrykuzmin Oct 1, 2019
9494e73
Clean up the code
dmitrykuzmin Oct 1, 2019
5eface1
Make created datastore instances configurable, de-singletonize them
dmitrykuzmin Oct 1, 2019
9945eec
Remove `TestEnvironment` from `testutil-gcloud`
dmitrykuzmin Oct 1, 2019
5fbc495
Remove `CountingDatastoreWrapper` from `testutil-gcloud`
dmitrykuzmin Oct 1, 2019
09fc2f8
Remove an unused method
dmitrykuzmin Oct 1, 2019
0f500f7
Fix kind cache not handling multiple project IDs at runtime correctly
dmitrykuzmin Oct 1, 2019
10bcf57
Add some doc
dmitrykuzmin Oct 1, 2019
e1b78a3
Add tests
dmitrykuzmin Oct 1, 2019
421a96d
Update `config`
dmitrykuzmin Oct 1, 2019
39a7d6a
Update Spine version to `1.1.3-SNAPSHOT+1`
dmitrykuzmin Oct 1, 2019
a8fa4a8
Fix build on Travis
dmitrykuzmin Oct 1, 2019
3b28bcc
Update license report and `pom.xml`
dmitrykuzmin Oct 1, 2019
4a18b93
Fix build on Travis
dmitrykuzmin Oct 1, 2019
7263620
Add missing `VisibleForTesting` annotations
dmitrykuzmin Oct 1, 2019
bdc8358
Fix build on Travis
dmitrykuzmin Oct 1, 2019
12816b3
Fix naming and docs
dmitrykuzmin Oct 1, 2019
c0f3766
Add ability to specify custom project ID for the local datastore
dmitrykuzmin Oct 1, 2019
aa2a4a6
Fix redundant method visibility
dmitrykuzmin Oct 1, 2019
12ef2b9
Fix unintended change
dmitrykuzmin Oct 1, 2019
02f02c3
Improve doc of default local project ID
dmitrykuzmin Oct 1, 2019
3f73ab2
Fix grammar
dmitrykuzmin Oct 1, 2019
4273663
Fix package info
dmitrykuzmin Oct 1, 2019
56ebb06
Throw an `IllegalStateException` on issues with reading service account
dmitrykuzmin Oct 1, 2019
7ec673b
Add a test for problematic parsing of service account
dmitrykuzmin Oct 1, 2019
6716d9f
Move the doc about the default project ID to `local(...)` methods
dmitrykuzmin Oct 2, 2019
6f828b6
Fix `assertThat...` formatting
dmitrykuzmin Oct 2, 2019
b5ec10a
Remove redundant Travis config
dmitrykuzmin Oct 2, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ dependencies {
implementation (

// Datastore Storage support library.
"io.spine.gcloud:spine-datastore:1.1.2",
"io.spine.gcloud:spine-datastore:1.1.3-SNAPSHOT+1",

// Stackdriver Trace support library.
"io.spine.gcloud:spine-stackdriver-trace:1.1.2"
"io.spine.gcloud:spine-stackdriver-trace:1.1.3-SNAPSHOT+1",

// Datastore-related test utilities (if needed).
"io.spine.gcloud:testutil-gcloud:1.1.3-SNAPSHOT+1"
)
}
```
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ ext {
credentialsPropertyFile = 'credentials.properties'
spineProtobufPluginId = 'io.spine.tools.spine-model-compiler'

projectsToPublish = ['datastore', 'stackdriver-trace']
projectsToPublish = ['datastore', 'stackdriver-trace', 'testutil-gcloud']
}

allprojects {
Expand Down
2 changes: 1 addition & 1 deletion config
1 change: 1 addition & 0 deletions datastore/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ dependencies {
exclude group: 'com.google.guava'
}

testImplementation project(path: ":testutil-gcloud")
testImplementation "io.spine:spine-server:"
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public class DatastoreStorageFactory implements StorageFactory {

private final NsConverterFactory converterFactory;

DatastoreStorageFactory(Builder builder) {
protected DatastoreStorageFactory(Builder builder) {
this.typeRegistry = builder.typeRegistry;
this.datastore = builder.datastore;
this.converterFactory = builder.converterFactory;
Expand Down Expand Up @@ -217,7 +217,7 @@ protected Iterable<DatastoreWrapper> wrappers() {
}

/**
* Returs the instance of wrapped {@link Datastore}.
* Returns the instance of wrapped {@link Datastore}.
*/
@VisibleForTesting
protected Datastore datastore() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
Expand Down Expand Up @@ -76,7 +77,7 @@ public class DatastoreWrapper implements Logging {
private static final int MAX_KEYS_PER_READ_REQUEST = 1000;
static final int MAX_ENTITIES_PER_WRITE_REQUEST = 500;

private static final Map<Kind, KeyFactory> keyFactories = new HashMap<>();
private static final Map<DatastoreKind, KeyFactory> keyFactories = new HashMap<>();

private static final Key[] EMPTY_KEY_ARRAY = new Key[0];

Expand Down Expand Up @@ -198,7 +199,7 @@ public void createOrUpdate(Collection<Entity> entities) {
* @return the {@link Entity} or {@code null} in case of no results for the key given
* @see DatastoreReader#get(Key)
*/
public Entity read(Key key) {
public @Nullable Entity read(Key key) {
return actor.get(key);
}

Expand Down Expand Up @@ -301,7 +302,7 @@ public <R> DsQueryIterator<R> read(StructuredQuery<R> query) {
* @throws IllegalArgumentException
* if the provided {@linkplain StructuredQuery#getLimit() query includes a limit}
*/
<R> Iterator<R> readAll(StructuredQuery<R> query, int pageSize) {
public <R> Iterator<R> readAll(StructuredQuery<R> query, int pageSize) {
return readAllPageByPage(query, pageSize);
}

Expand All @@ -322,7 +323,7 @@ <R> Iterator<R> readAll(StructuredQuery<R> query, int pageSize) {
* @throws IllegalArgumentException
* if the provided {@linkplain StructuredQuery#getLimit() query includes a limit}
*/
<R> Iterator<R> readAll(StructuredQuery<R> query) {
public <R> Iterator<R> readAll(StructuredQuery<R> query) {
return readAllPageByPage(query, null);
}

Expand Down Expand Up @@ -386,7 +387,8 @@ public void delete(Key... keys) {
* @param table
* kind (a.k.a. type, table, etc.) of the records to delete
*/
void dropTable(String table) {
@VisibleForTesting
protected void dropTable(String table) {
Namespace namespace = currentNamespace();
StructuredQuery<Entity> query =
Query.newEntityQueryBuilder()
Expand Down Expand Up @@ -504,7 +506,8 @@ public boolean isTransactionActive() {
* @return an instance of {@link KeyFactory} for given kind
*/
public KeyFactory keyFactory(Kind kind) {
KeyFactory keyFactory = keyFactories.get(kind);
DatastoreKind datastoreKind = new DatastoreKind(projectId(), kind);
KeyFactory keyFactory = keyFactories.get(datastoreKind);
if (keyFactory == null) {
keyFactory = initKeyFactory(kind);
}
Expand All @@ -522,17 +525,26 @@ public DatastoreOptions datastoreOptions() {
return options;
}

Datastore datastore() {
@VisibleForTesting
public Datastore datastore() {
return datastore;
}

private KeyFactory initKeyFactory(Kind kind) {
KeyFactory keyFactory = datastore.newKeyFactory()
.setKind(kind.getValue());
keyFactories.put(kind, keyFactory);
.setKind(kind.value());
DatastoreKind datastoreKind = new DatastoreKind(projectId(), kind);
keyFactories.put(datastoreKind, keyFactory);
return keyFactory;
}

private ProjectId projectId() {
String projectId = datastore.getOptions()
.getProjectId();
ProjectId result = ProjectId.of(projectId);
return result;
}

/**
* Reads big number of records.
*
Expand Down Expand Up @@ -587,4 +599,37 @@ private Namespace currentNamespace() {
private void writeSmallBulk(Entity[] entities) {
actor.put(entities);
}

/**
* A Datastore {@link Kind} by project ID.
*/
private static class DatastoreKind {

private final ProjectId projectId;
private final Kind kind;

private DatastoreKind(ProjectId projectId, Kind kind) {
this.projectId = projectId;
this.kind = kind;
}

@SuppressWarnings("EqualsGetClass") // The class is effectively final.
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DatastoreKind kind1 = (DatastoreKind) o;
return Objects.equals(projectId, kind1.projectId) &&
Objects.equals(kind, kind1.kind);
}

@Override
public int hashCode() {
return Objects.hash(projectId, kind);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public void writeAll(Iterable<M> messages) {
*/
Iterator<M> readAll(EntityQuery.Builder queryBuilder, int readBatchSize) {
StructuredQuery<Entity> query =
queryBuilder.setKind(kind.getValue())
queryBuilder.setKind(kind.value())
.build();
Iterator<Entity> iterator = datastore.readAll(query, readBatchSize);
Iterator<M> transformed =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ static <I> Iterator<I> indexIterator(DatastoreWrapper datastore, Kind kind, Clas
checkNotNull(idType);

StructuredQuery<Key> query = Query.newKeyQueryBuilder()
.setKind(kind.getValue())
.setKind(kind.value())
.build();
Iterator<Key> allEntities = datastore.read(query);
Iterator<I> idIterator = Streams.stream(allEntities)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@

package io.spine.server.storage.datastore;

import com.google.common.base.Objects;
import com.google.protobuf.Descriptors.Descriptor;
import com.google.protobuf.Message;
import io.spine.annotation.Internal;
import io.spine.type.TypeName;
import io.spine.type.TypeUrl;
import io.spine.value.StringTypeValue;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
Expand All @@ -34,7 +34,9 @@
* A data transfer object representing a Datastore
* <a href="https://cloud.google.com/datastore/docs/concepts/entities#kinds_and_identifiers">kind</a>.
*/
public final class Kind {
public final class Kind extends StringTypeValue {

private static final long serialVersionUID = 0L;

private static final String INVALID_KIND_ERROR_MESSAGE =
"Datastore kind cannot start with \"__\". See " +
Expand All @@ -44,10 +46,8 @@ public final class Kind {

private static final String NAMESPACE_KIND = "__namespace__";

private final String value;

private Kind(String value) {
this.value = checkValidKind(value);
super(checkValidKind(value));
}

/**
Expand All @@ -59,8 +59,8 @@ private Kind(String value) {
* the flag showing that the {@code Kind} is ancillary; must be set to {@code true}
*/
private Kind(String value, boolean ancillary) {
super(value);
checkArgument(ancillary);
this.value = value;
}

public static Kind of(String value) {
Expand Down Expand Up @@ -92,30 +92,9 @@ public static Kind ofNamespace() {
return new Kind(NAMESPACE_KIND, true);
}

public String getValue() {
return value;
}

private static String checkValidKind(String kind) {
checkNotNull(kind);
checkArgument(!kind.startsWith(FORBIDDEN_PREFIX), INVALID_KIND_ERROR_MESSAGE);
return kind;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Kind kind = (Kind) o;
return Objects.equal(getValue(), kind.getValue());
}

@Override
public int hashCode() {
return Objects.hashCode(getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ final class QueryWithFilter implements Function<StructuredQuery.Filter, Structur
checkNotNull(kind);

this.builder = Query.newEntityQueryBuilder()
.setKind(kind.getValue());
.setKind(kind.value());
if (format.hasOrderBy()) {
this.builder.setOrderBy(translateOrderBy(format.getOrderBy()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ private DefaultNamespaceQuery(Datastore datastore) {
@Override
public Iterator<Key> run() {
Query<Key> query = Query.newKeyQueryBuilder()
.setKind(NAMESPACE_KIND.getValue())
.setKind(NAMESPACE_KIND.value())
.build();
Iterator<Key> result = datastore.run(query);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
* @param <I>
* the type of the ID in the tested {@linkplain RecordStorage}
*/
public class BigDataTester<I> implements Logging {
public final class BigDataTester<I> implements Logging {

private static final int DEFAULT_BULK_SIZE = 500;

Expand All @@ -80,13 +80,14 @@ public static <I> Builder<I> newBuilder() {
*
* <p>The execution flow is as follows:
* <ol>
* <li>1. Produce the records with the given {@link EntryFactory}
* <li>2. Measure the time of the {@linkplain RecordStorage#write(Map) bulk write}
* <li>3. Fail if the time is over the specified limit
* <li>4. Wait 1 second to ensure the Datastore has established the data consistency
* <li>5. Measure the time of the {@linkplain RecordStorage#readAll() bulk read}
* <li>6. Fail if the time is over the specified limit
* <li>7. Check the count of the records written and read is equal
* <li>Produce the records with the given {@link EntryFactory}.
* <li>Measure the time of the {@linkplain RecordStorage#write(Map) bulk write}.
* <li>Fail if the time is over the specified limit.
* <li>Wait 1 second to ensure the Datastore has established the data consistency.
* <li>Measure the time of the
* {@linkplain RecordStorage#readAll(ResponseFormat) bulk read}.
* <li>Fail if the time is over the specified limit.
* <li>Check the count of the records written and read is equal.
* </ol>
*
* <p>This method performs {@code debug} logging of the measure results. To see the log, run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,20 @@
import io.spine.server.entity.storage.ColumnType;
import io.spine.server.entity.storage.ColumnTypeRegistry;
import io.spine.server.storage.datastore.given.Columns.ByteColumnType;
import io.spine.server.storage.datastore.given.TestDatastores;
import io.spine.server.storage.datastore.type.DatastoreTypeRegistryFactory;
import io.spine.testing.server.storage.datastore.TestDatastores;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

import static io.spine.server.ContextSpec.singleTenant;
import static io.spine.server.storage.datastore.given.Columns.byteColumn;
import static io.spine.server.storage.datastore.given.TestDatastores.projectId;
import static io.spine.server.storage.datastore.type.DatastoreTypeRegistryFactory.predefinedValuesAnd;
import static io.spine.testing.DisplayNames.HAVE_PARAMETERLESS_CTOR;
import static io.spine.testing.DisplayNames.NOT_ACCEPT_NULLS;
import static io.spine.testing.Tests.assertHasPrivateParameterlessCtor;
import static io.spine.testing.server.storage.datastore.TestDatastores.defaultLocalProjectId;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
Expand Down Expand Up @@ -102,7 +102,7 @@ class Namespaces {
void setUp() {
builder = DatastoreOptions
.newBuilder()
.setProjectId(projectId().getValue());
.setProjectId(defaultLocalProjectId().getValue());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@

import static com.google.common.truth.Truth.assertThat;
import static io.spine.server.ContextSpec.multitenant;
import static io.spine.server.storage.datastore.given.TestDatastores.local;
import static io.spine.server.storage.datastore.given.TestDatastores.projectId;
import static io.spine.server.tenant.TenantAwareRunner.with;
import static io.spine.testing.DisplayNames.NOT_ACCEPT_NULLS;
import static io.spine.testing.server.storage.datastore.TestDatastores.local;
import static io.spine.testing.server.storage.datastore.TestDatastores.defaultLocalProjectId;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
Expand Down Expand Up @@ -81,7 +81,8 @@ void testCreateMultitenant() {
StorageFactory factory = DatastoreStorageFactory.newBuilder()
.setDatastore(datastore)
.build();
RecordStorage storage = factory.createRecordStorage(TestEnvironment.multiTenantSpec(), TestEntity.class);
RecordStorage storage =
factory.createRecordStorage(TestEnvironment.multiTenantSpec(), TestEntity.class);
assertTrue(storage.isMultitenant());
storage.close();
}
Expand Down Expand Up @@ -176,7 +177,7 @@ private static Key.Builder whiteForTenant(DsPropertyStorage storage, TenantId te
with(tenant).run(
() -> storage.write(recordId, message)
);
return Key.newBuilder(projectId().getValue(),
return Key.newBuilder(defaultLocalProjectId().getValue(),
TypeName.of(message)
.value(),
recordId.getValue());
Expand Down
Loading