Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
f947b2c
Add docs and improve names.
Mar 14, 2016
3a03b22
Implement commands validation (message, number fields).
Mar 14, 2016
b019121
Add message validator tests.
Mar 14, 2016
844c259
Add/improve docs and default error messages.
Mar 15, 2016
874ecf9
Support repeated fields, add tests and docs.
Mar 15, 2016
fc726c0
Implement ByteString validator, add tests.
Mar 16, 2016
929e0d4
Improve the way the validation error message is build.
Mar 16, 2016
0a6772a
Reformat code.
Mar 16, 2016
4a5df70
Implement string field validator, add tests.
Mar 16, 2016
141c205
Implement string field validator, add tests.
Mar 16, 2016
4c81ca4
Add validation error messages tests.
Mar 16, 2016
90d5766
Validate fields of type message if "valid" annotation is present.
Mar 16, 2016
34d25d9
Extract FieldValidatorFactory class.
Mar 18, 2016
4398911
Add command field validators.
Mar 23, 2016
bd62f20
Implement entity ID validation in command messages.
Mar 25, 2016
7bd0e8b
Add decimal min/max options, change min/max options.
Mar 28, 2016
4c3a4ca
Check if the field is required entity id (in command) in all field va…
Mar 28, 2016
efa6eaf
Add a new method to field validator, remove a redundant one.
Mar 29, 2016
0675729
Refactoring: invert bool variables for better readability, simplify m…
Mar 29, 2016
b3ac918
Accept only command message to command bus `validate` method.
Mar 29, 2016
89a395a
Make command bus usage consistent.
Mar 29, 2016
dd4746e
Move/change commands methods.
Mar 29, 2016
4fc43ee
Improve docs.
Mar 29, 2016
10ebf73
Add `validate()` default implementation in field validator.
Mar 29, 2016
bcbcade
Add `outer_proto_file_name` options.
Mar 29, 2016
71c0346
Add `field_path.proto`.
Mar 29, 2016
5975493
Improve performance, refactoring.
Mar 29, 2016
e5b1ed1
Define ValidationFailure and ConstraintViolation messages.
Mar 29, 2016
7f3f433
Fix docs.
Mar 30, 2016
9ac7af0
Add format params and nested constraint violations to ConstraintViola…
Mar 30, 2016
07bb715
Remove a TODO.
Mar 31, 2016
d702298
Add utility methods.
Mar 31, 2016
2982b20
Rename a method.
Mar 31, 2016
b904bff
Return constraint violations as msg validation result.
Mar 31, 2016
ae6badb
Remove a method.
Mar 31, 2016
1bf9f3d
Refactor a method.
Mar 31, 2016
6bca3d2
Remove a field.
Mar 31, 2016
56aa222
Create field validators for float and int types to wrap field values …
Mar 31, 2016
f46bef4
Rename a field.
Mar 31, 2016
20c1647
Set full field paths to constraint violations.
Mar 31, 2016
ed8a408
Rename a method.
Apr 1, 2016
1cf8e26
Rename a fields.
Apr 1, 2016
b9f8d36
Move methods to the separate class and use them.
Apr 1, 2016
4cf7773
Minor change.
Apr 1, 2016
26b403e
Add utility methods for validation messages formatting.
Apr 1, 2016
38e8c8f
Remove duplicated code.
Apr 1, 2016
204192e
Remove duplicated code.
Apr 1, 2016
1e22cf5
Add tests.
Apr 1, 2016
1b2fbb7
Add min/max options tests.
Apr 1, 2016
2bc27a5
Cover messages validation 100%.
Apr 1, 2016
8c63030
Remove redundant warning suppression.
Apr 1, 2016
e37f68e
Fix docs.
Apr 1, 2016
2cb8df9
Fix docs.
Apr 4, 2016
fc2fe1f
Add the default enum value.
Apr 4, 2016
58b8b4b
Fix a package.
Apr 5, 2016
9c4d2f7
Fix a package.
Apr 6, 2016
7393c97
Rename method and field.
Apr 6, 2016
ac402be
Update Spine protobuf-plugin dependency.
Apr 6, 2016
2b1dc01
Add field validator factory tests.
Apr 6, 2016
6c6dcbc
Add utility classes tests.
Apr 6, 2016
fbf60ef
Merge branch 'master' into proto-messages-validation
Apr 6, 2016
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
4 changes: 2 additions & 2 deletions client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ buildscript {
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
}
dependencies {
classpath group: 'org.spine3.tools', name: 'proto-lookup-plugin', version: '1.1', changing: true
classpath group: 'org.spine3.tools', name: 'protobuf-plugin', version: '1.2', changing: true
}
}

Expand All @@ -13,7 +13,7 @@ dependencies {
testCompile project (path: ":testutil", configuration: 'testArtifacts')
}

apply plugin: 'org.spine3.tools.protolookup';
apply plugin: 'org.spine3.tools.protobuf-plugin';

sourceSets {
main {
Expand Down
37 changes: 37 additions & 0 deletions client/src/main/java/org/spine3/base/Commands.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,10 @@
package org.spine3.base;

import com.google.common.base.Predicate;
import com.google.protobuf.Descriptors.FileDescriptor;
import com.google.protobuf.Message;
import com.google.protobuf.Timestamp;
import org.spine3.protobuf.EntityPackagesMap;
import org.spine3.protobuf.Messages;
import org.spine3.protobuf.Timestamps;
import org.spine3.time.ZoneOffset;
Expand All @@ -45,6 +47,13 @@
*/
public class Commands {

/**
* A substring which the {@code .proto} file containing commands must have in its name.
*/
public static final String COMMANDS_FILE_SUBSTRING = "commands";

private static final char PROTO_FILE_SEPARATOR = '/';

private Commands() {}

/**
Expand Down Expand Up @@ -179,4 +188,32 @@ public static String formatMessageTypeAndId(String format, Message commandMessag
final String result = String.format(format, commandType, id);
return result;
}

/**
* Checks if the file is for commands.
*
* @param file a descriptor of a {@code .proto} file to check
* @return {@code true} if the file name contains {@link #COMMANDS_FILE_SUBSTRING} substring, {@code false} otherwise
*/
public static boolean isCommandsFile(FileDescriptor file) {
final String fqn = file.getName();
final int startIndexOfFileName = fqn.lastIndexOf(PROTO_FILE_SEPARATOR) + 1;
final String fileName = fqn.substring(startIndexOfFileName);
final boolean isCommandsFile = fileName.contains(COMMANDS_FILE_SUBSTRING);
return isCommandsFile;
}

/**
* Checks if the file belongs to an entity.
*
* <p>See {@link EntityPackagesMap} for more info.
*
* @param file a descriptor of a {@code .proto} file to check
* @return {@code true} if the file belongs to an entity, {@code false} otherwise
*/
public static boolean isEntityFile(FileDescriptor file) {
final String protoPackage = file.getPackage();
final boolean isCommandForEntity = EntityPackagesMap.contains(protoPackage);
return isCommandForEntity;
}
}
11 changes: 6 additions & 5 deletions client/src/main/java/org/spine3/base/Identifiers.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import com.google.protobuf.UInt32Value;
import com.google.protobuf.UInt64Value;
import com.google.protobuf.util.TimeUtil;
import org.spine3.protobuf.Messages;

import javax.annotation.Nullable;
import java.util.Collection;
Expand All @@ -45,6 +44,8 @@
import static com.google.common.collect.Maps.newHashMap;
import static com.google.protobuf.TextFormat.shortDebugString;
import static org.spine3.protobuf.Messages.fromAny;
import static org.spine3.protobuf.Messages.toAny;
import static org.spine3.protobuf.Values.newStringValue;

/**
* This class manages conversion of identifies to/from string.
Expand Down Expand Up @@ -189,16 +190,16 @@ public static <I> Any idToAny(I id) {
//noinspection IfStatementWithTooManyBranches,ChainOfInstanceofChecks
if (id instanceof Message) {
final Message message = (Message) id;
anyId = Messages.toAny(message);
anyId = toAny(message);
} else if (id instanceof String) {
final String strValue = (String) id;
anyId = Messages.toAny(StringValue.newBuilder().setValue(strValue).build());
anyId = toAny(newStringValue(strValue));
} else if (id instanceof Integer) {
final Integer intValue = (Integer) id;
anyId = Messages.toAny(UInt32Value.newBuilder().setValue(intValue).build());
anyId = toAny(UInt32Value.newBuilder().setValue(intValue).build());
} else if (id instanceof Long) {
final Long longValue = (Long) id;
anyId = Messages.toAny(UInt64Value.newBuilder().setValue(longValue).build());
anyId = toAny(UInt64Value.newBuilder().setValue(longValue).build());
} else {
throw unsupportedIdType(id);
}
Expand Down
63 changes: 62 additions & 1 deletion client/src/main/java/org/spine3/io/IoUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,89 @@
package org.spine3.io;

import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spine3.Internal;

import javax.annotation.Nullable;
import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.Properties;

import static com.google.common.base.Throwables.propagate;

/**
* Utility class working with I/O: streams etc.
* A utility class working with I/O: streams, resources, etc.
*
* @author Alexander Litus
*/
@Internal
public class IoUtil {

private static final String ERROR_MESSAGE = "Exception while closing:";

private IoUtil() {}

/**
* Loads all data from {@code .properties} file(s) into memory.
*
* <p>Logs {@link IOException} if it occurs.
*
* @param propsFilePath the path of the {@code .properties} file to load
*/
public static ImmutableSet<Properties> loadAllProperties(String propsFilePath) {
final ImmutableSet.Builder<Properties> result = ImmutableSet.builder();
final Enumeration<URL> resources = getResources(propsFilePath);
if (resources == null) {
return result.build();
}
while (resources.hasMoreElements()) {
final URL resourceUrl = resources.nextElement();
final Properties properties = loadPropertiesFile(resourceUrl);
result.add(properties);
}
return result.build();
}

private static Enumeration<URL> getResources(String propsFilePath) {
final ClassLoader classLoader = getContextClassLoader();
Enumeration<URL> resources = null;
try {
resources = classLoader.getResources(propsFilePath);
} catch (IOException e) {
if (log().isWarnEnabled()) {
log().warn("Failed to load resources: " + propsFilePath, e);
}
}
return resources;
}

private static Properties loadPropertiesFile(URL resourceUrl) {
final Properties properties = new Properties();
InputStream inputStream = null;
try {
inputStream = resourceUrl.openStream();
properties.load(inputStream);
} catch (IOException e) {
if (log().isWarnEnabled()) {
log().warn("Failed to load properties file.", e);
}
} finally {
close(inputStream);
}
return properties;
}

private static ClassLoader getContextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}

/**
* Closes passed closeables one by one.
*
Expand Down
123 changes: 123 additions & 0 deletions client/src/main/java/org/spine3/protobuf/EntityPackagesMap.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright 2016, TeamDev Ltd. All rights reserved.
*
* 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 org.spine3.protobuf;

import com.google.common.collect.ImmutableSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spine3.Internal;
import org.spine3.client.EntityType;

import javax.annotation.Nullable;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import static com.google.common.collect.Maps.newHashMap;
import static org.spine3.io.IoUtil.loadAllProperties;

/**
* A map from Protobuf package to {@link EntityType} (the package contains files for this entity type (aggregate, etc)).
*
* <p>If there is a {@code state_of} option in the entity state Protobuf message definition,
* all files in the current package are considered belonging to the specified type of the entity
* (containing entity state, commands, events, etc).
*
* <p>This information is needed when validating a command to check if it is sent to an entity
* (to check entity ID field in the command message).
*
* @author Alexander Litus
*/
@Internal
public class EntityPackagesMap {

/**
* A path to a file which contains Protobuf packages and appropriate {@link EntityType}s.
* Is generated by Gradle during build process.
*/
private static final String PROPS_FILE_PATH = "entities.properties";

/**
* A map from Protobuf package to {@link EntityType}.
* The package contains files for this type of entity (aggregate, etc).
*
* <p>Example:
* <p>{@code com.example.order} - {@code AGGREGATE}
*/
private static final Map<String, EntityType> PACKAGES_MAP = buildPackagesMap();

private EntityPackagesMap() {}

/**
* Returns an entity type for the Protobuf package.
*
* @param protoPackage a protobuf package name where entity files are located
* @return a type of the entity to which the package belongs
*/
@Nullable
public static EntityType get(String protoPackage) {
final EntityType result = PACKAGES_MAP.get(protoPackage);
return result;
}

/**
* Returns {@code true} if the map contains an entity type for a Protobuf package, {@code false} otherwise.
*
* @param protoPackage a protobuf package name where entity files are located
* @return {@code true} if the package belongs to an entity
*/
public static boolean contains(String protoPackage) {
final EntityType result = get(protoPackage);
final boolean contains = result != null;
return contains;
}

private static Map<String, EntityType> buildPackagesMap() {
final Map<String, EntityType> result = newHashMap();
final ImmutableSet<Properties> propertiesSet = loadAllProperties(PROPS_FILE_PATH);
for (Properties properties : propertiesSet) {
putTo(result, properties);
}
if (log().isDebugEnabled()) {
log().debug("Entry count in EntityPackagesMap: " + result.size());
}
return result;
}

private static void putTo(Map<String, EntityType> result, Properties properties) {
final Set<String> protoPackages = properties.stringPropertyNames();
for (String protoPackage : protoPackages) {
final String entityTypeStr = properties.getProperty(protoPackage);
final EntityType entityType = EntityType.valueOf(entityTypeStr);
result.put(protoPackage, entityType);
}
}

private enum LogSingleton {
INSTANCE;
@SuppressWarnings("NonSerializableFieldInSerializableClass")
private final Logger value = LoggerFactory.getLogger(EntityPackagesMap.class);
}

private static Logger log() {
return LogSingleton.INSTANCE.value;
}
}
1 change: 0 additions & 1 deletion client/src/main/java/org/spine3/protobuf/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,4 @@ public static Descriptors.GenericDescriptor getClassDescriptor(Class<? extends M
throw new MissingDescriptorException(clazz, e.getCause());
}
}

}
Loading