Skip to content

Commit 97b8632

Browse files
authored
Merge pull request #133 from SpineEventEngine/protobuf-columns
The `(column)` option
2 parents f831476 + 9e6572f commit 97b8632

39 files changed

+721
-1631
lines changed

.idea/.name

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

datastore/src/main/java/io/spine/server/storage/datastore/DatastoreStorageFactory.java

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
package io.spine.server.storage.datastore;
2222

2323
import com.google.cloud.datastore.Datastore;
24+
import com.google.cloud.datastore.Value;
2425
import com.google.common.annotations.VisibleForTesting;
2526
import com.google.common.collect.Iterables;
2627
import com.google.errorprone.annotations.CanIgnoreReturnValue;
@@ -31,7 +32,7 @@
3132
import io.spine.server.aggregate.AggregateStorage;
3233
import io.spine.server.delivery.InboxStorage;
3334
import io.spine.server.entity.Entity;
34-
import io.spine.server.entity.storage.ColumnTypeRegistry;
35+
import io.spine.server.entity.storage.ColumnMapping;
3536
import io.spine.server.projection.Projection;
3637
import io.spine.server.projection.ProjectionStorage;
3738
import io.spine.server.storage.RecordStorage;
@@ -42,8 +43,6 @@
4243
import io.spine.server.storage.datastore.tenant.NamespaceSupplier;
4344
import io.spine.server.storage.datastore.tenant.NsConverterFactory;
4445
import io.spine.server.storage.datastore.tenant.PrefixedNsConverterFactory;
45-
import io.spine.server.storage.datastore.type.DatastoreColumnType;
46-
import io.spine.server.storage.datastore.type.DatastoreTypeRegistryFactory;
4746
import io.spine.server.tenant.TenantIndex;
4847

4948
import java.util.Map;
@@ -86,12 +85,12 @@ public class DatastoreStorageFactory implements StorageFactory {
8685
*/
8786
private final Map<Class<? extends Storage>, DatastoreWrapper> sysWrappers = newConcurrentMap();
8887

89-
private final ColumnTypeRegistry<? extends DatastoreColumnType<?, ?>> typeRegistry;
88+
private final ColumnMapping<Value<?>> columnMapping;
9089

9190
private final NsConverterFactory converterFactory;
9291

9392
protected DatastoreStorageFactory(Builder builder) {
94-
this.typeRegistry = builder.typeRegistry;
93+
this.columnMapping = builder.columnMapping;
9594
this.datastore = builder.datastore;
9695
this.converterFactory = builder.converterFactory;
9796
}
@@ -156,8 +155,8 @@ public InboxStorage createInboxStorage(boolean multitenant) {
156155
return new DsInboxStorage(wrapper, multitenant);
157156
}
158157

159-
public ColumnTypeRegistry getTypeRegistry() {
160-
return typeRegistry;
158+
public ColumnMapping<Value<?>> columnMapping() {
159+
return columnMapping;
161160
}
162161

163162
/**
@@ -168,7 +167,7 @@ S configure(B builder, Class<? extends Entity<I, ?>> cls, ContextSpec context) {
168167
builder.setModelClass(asEntityClass(cls))
169168
.setDatastore(wrapperFor(context))
170169
.setMultitenant(context.isMultitenant())
171-
.setColumnTypeRegistry(typeRegistry);
170+
.setColumnMapping(columnMapping);
172171
S storage = builder.build();
173172
return storage;
174173
}
@@ -268,7 +267,7 @@ public static Builder newBuilder() {
268267
public static class Builder {
269268

270269
private Datastore datastore;
271-
private ColumnTypeRegistry<? extends DatastoreColumnType<?, ?>> typeRegistry;
270+
private ColumnMapping<Value<?>> columnMapping;
272271
private NamespaceConverter namespaceConverter;
273272
private NsConverterFactory converterFactory;
274273

@@ -296,19 +295,17 @@ public Datastore getDatastore() {
296295
}
297296

298297
/**
299-
* Sets a {@link ColumnTypeRegistry} for handling the Entity Columns.
298+
* Sets the {@link ColumnMapping} to use.
300299
*
301-
* <p>Default value is {@link DatastoreTypeRegistryFactory#defaultInstance()}.
300+
* <p>Default value is {@link DsColumnMapping}.
302301
*
303-
* @param typeRegistry
304-
* the type registry containing all the required
305-
* {@linkplain io.spine.server.entity.storage.ColumnType column types}
306-
* to handle the existing Entity Columns
302+
* @param columnMapping
303+
* the storage rules for entity columns
307304
* @return self for method chaining
308305
*/
309306
public Builder
310-
setTypeRegistry(ColumnTypeRegistry<? extends DatastoreColumnType<?, ?>> typeRegistry) {
311-
this.typeRegistry = checkNotNull(typeRegistry);
307+
setColumnMapping(ColumnMapping<Value<?>> columnMapping) {
308+
this.columnMapping = checkNotNull(columnMapping);
312309
return this;
313310
}
314311

@@ -337,8 +334,8 @@ public Builder setNamespaceConverter(NamespaceConverter converter) {
337334
*/
338335
public DatastoreStorageFactory build() {
339336
checkNotNull(datastore);
340-
if (typeRegistry == null) {
341-
typeRegistry = DatastoreTypeRegistryFactory.defaultInstance();
337+
if (columnMapping == null) {
338+
columnMapping = new DsColumnMapping();
342339
}
343340
if (namespaceConverter == null) {
344341
converterFactory = NsConverterFactory.defaults();

datastore/src/main/java/io/spine/server/storage/datastore/DsAggregateStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ protected DsAggregateStorage(Class<? extends Aggregate<I, ?, ?>> cls,
116116
@SuppressWarnings("unchecked") // The ID class is ensured by the parameter type.
117117
Class<I> idClass = (Class<I>) modelClass.idClass();
118118
this.idClass = idClass;
119-
this.stateTypeName = modelClass.stateType()
119+
this.stateTypeName = modelClass.stateTypeUrl()
120120
.toTypeName();
121121
}
122122

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/*
2+
* Copyright 2019, TeamDev. All rights reserved.
3+
*
4+
* Redistribution and use in source and/or binary forms, with or without
5+
* modification, must retain the above copyright notice and the following
6+
* disclaimer.
7+
*
8+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19+
*/
20+
21+
package io.spine.server.storage.datastore;
22+
23+
import com.google.cloud.datastore.Blob;
24+
import com.google.cloud.datastore.BlobValue;
25+
import com.google.cloud.datastore.BooleanValue;
26+
import com.google.cloud.datastore.DoubleValue;
27+
import com.google.cloud.datastore.LongValue;
28+
import com.google.cloud.datastore.NullValue;
29+
import com.google.cloud.datastore.StringValue;
30+
import com.google.cloud.datastore.TimestampValue;
31+
import com.google.cloud.datastore.Value;
32+
import com.google.common.collect.ImmutableMap;
33+
import com.google.protobuf.ByteString;
34+
import com.google.protobuf.Message;
35+
import com.google.protobuf.Timestamp;
36+
import io.spine.core.Version;
37+
import io.spine.server.entity.storage.AbstractColumnMapping;
38+
import io.spine.server.entity.storage.ColumnTypeMapping;
39+
import io.spine.string.Stringifiers;
40+
41+
import static com.google.cloud.Timestamp.ofTimeSecondsAndNanos;
42+
43+
/**
44+
* The default Datastore column mapping.
45+
*
46+
* <p>All column values are stored as Datastore {@link Value}-s.
47+
*
48+
* <p>Users of the storage can extend this class to specify their own column mapping for the
49+
* selected types.
50+
*/
51+
public class DsColumnMapping extends AbstractColumnMapping<Value<?>> {
52+
53+
@Override
54+
protected void
55+
setupCustomMapping(
56+
ImmutableMap.Builder<Class<?>, ColumnTypeMapping<?, ? extends Value<?>>> builder) {
57+
builder.put(Timestamp.class, ofTimestamp());
58+
builder.put(Version.class, ofVersion());
59+
}
60+
61+
@Override
62+
protected ColumnTypeMapping<String, StringValue> ofString() {
63+
return StringValue::of;
64+
}
65+
66+
@Override
67+
protected ColumnTypeMapping<Integer, LongValue> ofInteger() {
68+
return LongValue::of;
69+
}
70+
71+
@Override
72+
protected ColumnTypeMapping<Long, LongValue> ofLong() {
73+
return LongValue::of;
74+
}
75+
76+
@Override
77+
protected ColumnTypeMapping<Float, DoubleValue> ofFloat() {
78+
return DoubleValue::of;
79+
}
80+
81+
@Override
82+
protected ColumnTypeMapping<Double, DoubleValue> ofDouble() {
83+
return DoubleValue::of;
84+
}
85+
86+
@Override
87+
protected ColumnTypeMapping<Boolean, BooleanValue> ofBoolean() {
88+
return BooleanValue::of;
89+
}
90+
91+
@Override
92+
protected ColumnTypeMapping<ByteString, BlobValue> ofByteString() {
93+
return bytes -> {
94+
Blob blob = Blob.copyFrom(bytes.asReadOnlyByteBuffer());
95+
return BlobValue.of(blob);
96+
};
97+
}
98+
99+
@Override
100+
protected ColumnTypeMapping<Enum<?>, LongValue> ofEnum() {
101+
return anEnum -> LongValue.of(anEnum.ordinal());
102+
}
103+
104+
@Override
105+
protected ColumnTypeMapping<Message, StringValue> ofMessage() {
106+
return msg -> {
107+
String str = Stringifiers.toString(msg);
108+
return StringValue.of(str);
109+
};
110+
}
111+
112+
@Override
113+
public ColumnTypeMapping<?, ? extends Value<?>> ofNull() {
114+
return o -> NullValue.of();
115+
}
116+
117+
@SuppressWarnings("ProtoTimestampGetSecondsGetNano") // This behavior is intended.
118+
private static ColumnTypeMapping<Timestamp, TimestampValue> ofTimestamp() {
119+
return timestamp -> TimestampValue.of(
120+
ofTimeSecondsAndNanos(timestamp.getSeconds(), timestamp.getNanos())
121+
);
122+
}
123+
124+
private static ColumnTypeMapping<Version, LongValue> ofVersion() {
125+
return version -> LongValue.of(version.getNumber());
126+
}
127+
}

datastore/src/main/java/io/spine/server/storage/datastore/DsEntityComparator.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
import static com.google.common.base.Preconditions.checkNotNull;
3535
import static io.spine.client.OrderBy.Direction.ASCENDING;
3636
import static io.spine.util.Exceptions.newIllegalStateException;
37-
import static io.spine.validate.Validate.checkNotDefault;
37+
import static io.spine.util.Preconditions2.checkNotDefaultArg;
3838
import static java.util.Arrays.stream;
3939

4040
/**
@@ -76,7 +76,7 @@ public int compare(Entity a, Entity b) {
7676
*/
7777
static Comparator<Entity> implementing(OrderBy orderBy) {
7878
checkNotNull(orderBy);
79-
checkNotDefault(orderBy);
79+
checkNotDefaultArg(orderBy);
8080
Comparator<Entity> comparator = new DsEntityComparator(orderBy.getColumn());
8181
return orderBy.getDirection() == ASCENDING ? comparator : comparator.reversed();
8282
}

datastore/src/main/java/io/spine/server/storage/datastore/DsFilters.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
import com.google.common.base.Objects;
2727
import com.google.common.collect.ImmutableMultimap;
2828
import io.spine.client.Filter;
29+
import io.spine.server.entity.storage.Column;
2930
import io.spine.server.entity.storage.CompositeQueryParameter;
30-
import io.spine.server.entity.storage.EntityColumn;
3131

3232
import javax.annotation.Nullable;
3333
import java.util.ArrayDeque;
@@ -69,7 +69,7 @@ final class DsFilters {
6969
private static final Predicate<CompositeQueryParameter> isConjunctive =
7070
input -> {
7171
checkNotNull(input);
72-
return input.getOperator() == ALL;
72+
return input.operator() == ALL;
7373
};
7474

7575
/**
@@ -285,9 +285,9 @@ private static FilterNode buildConjunctionTree(
285285
* Converts the given parameter to a {@code Collection} of {@link FilterNode}s.
286286
*/
287287
private static Collection<FilterNode> toFilters(CompositeQueryParameter param) {
288-
ImmutableMultimap<EntityColumn, Filter> srcFilters = param.getFilters();
288+
ImmutableMultimap<Column, Filter> srcFilters = param.filters();
289289
Set<FilterNode> filters = new HashSet<>(srcFilters.size());
290-
for (Map.Entry<EntityColumn, Filter> entry : srcFilters.entries()) {
290+
for (Map.Entry<Column, Filter> entry : srcFilters.entries()) {
291291
filters.add(new FilterNode(entry.getKey(), entry.getValue()));
292292
}
293293
return filters;
@@ -363,12 +363,12 @@ public void walk(Collection<FilterNode> conjunctionGroup) {
363363
*/
364364
private static class FilterNode {
365365

366-
private final EntityColumn column;
366+
private final Column column;
367367
private final Filter columnFilter;
368368

369369
private final Collection<FilterNode> subtrees;
370370

371-
private FilterNode(@Nullable EntityColumn column, @Nullable Filter filter) {
371+
private FilterNode(@Nullable Column column, @Nullable Filter filter) {
372372
this.column = column;
373373
this.columnFilter = filter;
374374
this.subtrees = newLinkedList();
@@ -378,19 +378,20 @@ private FilterNode() {
378378
this(null, null);
379379
}
380380

381-
private EntityColumn getColumn() {
381+
private Column column() {
382382
return column;
383383
}
384384

385-
private Filter getFilter() {
385+
private Filter filter() {
386386
return columnFilter;
387387
}
388388

389389
@SuppressWarnings({"EnumSwitchStatementWhichMissesCases", "MethodOnlyUsedFromInnerClass"})
390390
// Only non-faulty values are used.
391391
private StructuredQuery.Filter toFilter(FilterAdapter adapter) {
392392
Value<?> value = adapter.toValue(column, columnFilter);
393-
String columnName = column.name();
393+
String columnName = column.name()
394+
.value();
394395
switch (columnFilter.getOperator()) {
395396
case EQUAL:
396397
return eq(columnName, value);
@@ -468,13 +469,13 @@ public boolean equals(Object o) {
468469
return false;
469470
}
470471
FilterNode that = (FilterNode) o;
471-
return Objects.equal(getColumn(), that.getColumn()) &&
472-
Objects.equal(getFilter(), that.getFilter());
472+
return Objects.equal(column(), that.column()) &&
473+
Objects.equal(filter(), that.filter());
473474
}
474475

475476
@Override
476477
public int hashCode() {
477-
return Objects.hashCode(getColumn(), getFilter());
478+
return Objects.hashCode(column(), filter());
478479
}
479480

480481
private static Function<FilterNode, StructuredQuery.Filter>

datastore/src/main/java/io/spine/server/storage/datastore/DsLookupByQueries.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@
3939
import static com.google.common.base.Preconditions.checkArgument;
4040
import static com.google.common.base.Preconditions.checkNotNull;
4141
import static com.google.common.collect.Lists.newArrayList;
42+
import static io.spine.protobuf.Messages.isDefault;
4243
import static io.spine.server.storage.datastore.DsEntityComparator.implementing;
4344
import static io.spine.server.storage.datastore.FieldMaskApplier.recordMasker;
44-
import static io.spine.validate.Validate.isDefault;
4545
import static java.util.Collections.singleton;
4646
import static java.util.stream.Collectors.toList;
4747

datastore/src/main/java/io/spine/server/storage/datastore/DsProjectionStorage.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
import java.util.Iterator;
3434
import java.util.Optional;
3535

36-
import static io.spine.validate.Validate.isDefault;
36+
import static io.spine.protobuf.Messages.isDefault;
3737

3838
/**
3939
* Datastore implementation of the {@link ProjectionStorage}.
@@ -51,7 +51,7 @@ protected DsProjectionStorage(Class<? extends Projection<I, ?, ?>> projectionCla
5151
DsRecordStorage<I> recordStorage,
5252
DsPropertyStorage propertyStorage,
5353
boolean multitenant) {
54-
super(multitenant);
54+
super(projectionClass, multitenant);
5555
this.recordStorage = recordStorage;
5656
this.propertyStorage = propertyStorage;
5757
this.lastTimestampId =

0 commit comments

Comments
 (0)