mirror of
https://github.com/valitydev/magista-dsl.git
synced 2024-11-06 09:25:17 +00:00
Merge pull request #1 from rbkmoney/ft/MST-204/init
Magista-dsl library
This commit is contained in:
commit
eee596c798
83
.gitignore
vendored
Normal file
83
.gitignore
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### Maven template
|
||||
target/
|
||||
pom.xml.tag
|
||||
pom.xml.releaseBackup
|
||||
pom.xml.versionsBackup
|
||||
pom.xml.next
|
||||
release.properties
|
||||
dependency-reduced-pom.xml
|
||||
buildNumber.properties
|
||||
.mvn/timing.properties
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
.idea/
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/vcs.xml
|
||||
.idea/jsLibraryMappings.xml
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/dataSources.ids
|
||||
.idea/dataSources.xml
|
||||
.idea/dataSources.local.xml
|
||||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
.idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
*.ipr
|
||||
*.iml
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
### Java template
|
||||
*.class
|
||||
|
||||
# Mobile Tools for Java (J2ME)
|
||||
.mtj.tmp/
|
||||
|
||||
# Package Files #
|
||||
*.jar
|
||||
*.war
|
||||
*.ear
|
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||
hs_err_pid*
|
||||
|
||||
env.list
|
||||
|
||||
|
||||
# OSX
|
||||
*.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# TestContainers
|
||||
.testcontainers-*
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule "build_utils"]
|
||||
path = build_utils
|
||||
url = git@github.com:rbkmoney/build_utils.git
|
13
Jenkinsfile
vendored
Normal file
13
Jenkinsfile
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
#!groovy
|
||||
build('magista_dsl', 'docker-host') {
|
||||
checkoutRepo()
|
||||
loadBuildUtils()
|
||||
|
||||
def javaLibPipeline
|
||||
runStage('load JavaLib pipeline') {
|
||||
javaLibPipeline = load("build_utils/jenkins_lib/pipeJavaLib.groovy")
|
||||
}
|
||||
|
||||
def buildImageTag = "7372dc01bf066b5b26be13d6de0c7bed70648a26"
|
||||
javaLibPipeline(buildImageTag)
|
||||
}
|
25
README.md
25
README.md
@ -1,2 +1,25 @@
|
||||
# magista-dsl
|
||||
magista-dsl library
|
||||
|
||||
Для формирования запросов к данным библиотека предоставляет DSL в JSON формате, который основан на Elasticsearch [Query DSL](https://www.elastic.co/guide/en/elasticsearch/reference/current/_introducing_the_query_language.html).
|
||||
|
||||
Общий формат запроса выглядит следующим образом:
|
||||
|
||||
```json
|
||||
{
|
||||
"query": {
|
||||
"<query_type>": {
|
||||
"<param>": "<val>"
|
||||
},
|
||||
"<query_param>": "<val>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`<query_type>` - тип запроса, который требуется выполнить. `<query_param>` зависит от типа запроса.
|
||||
|
||||
`<query_param>` - параметр запроса, может включать:
|
||||
|
||||
1. Для запросов на выборку по моделям:
|
||||
|
||||
1. `from` (__deprecated__) - (0-based) определяет, с какой записи результирующей выборки следует начать. Вместо этого поля необходимо использовать continuationToken, на основании которого оно будет вычисляться.
|
||||
2. `size` - определяет, сколько максимум записей следует вернуть, начиная с `from`.
|
||||
|
1
build_utils
Submodule
1
build_utils
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit 9b664082ddc8ec8cdfbe7513d54e433d24198cc2
|
56
pom.xml
Normal file
56
pom.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.rbkmoney</groupId>
|
||||
<artifactId>parent</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</parent>
|
||||
|
||||
<groupId>com.rbkmoney</groupId>
|
||||
<artifactId>magista-dsl</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.rbkmoney.geck</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>0.6.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
<version>1.7.25</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
<version>2.8.10</version>
|
||||
</dependency>
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<version>4.12</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>1.2.17</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<version>1.7.21</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
@ -0,0 +1,19 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public class BadTokenException extends IllegalArgumentException {
|
||||
|
||||
public BadTokenException() {
|
||||
}
|
||||
|
||||
public BadTokenException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public BadTokenException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public BadTokenException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BaseCompositeQuery<T, CT> extends BaseQuery<T, CT> implements CompositeQuery<T, CT> {
|
||||
private List<Query> queries;
|
||||
private final Function<QueryContext, QueryResult<T, CT>> execFunction;
|
||||
private final BiFunction<QueryContext, List<QueryResult>, QueryResult<T, CT>> parallelExecFunction;
|
||||
|
||||
public static <T, CT> BaseCompositeQuery<T, CT> newInstance(Object descriptor, QueryParameters params, List<Query> queries, Function<QueryContext, QueryResult<T, CT>> execFunction) {
|
||||
return newInstance(descriptor, params, queries, execFunction, null);
|
||||
}
|
||||
|
||||
public static <T, CT> BaseCompositeQuery<T, CT> newInstance(Object descriptor, QueryParameters params, List<Query> queries, Function<QueryContext, QueryResult<T, CT>> execFunction, BiFunction<QueryContext, List<QueryResult>, QueryResult<T, CT>> parallelExecFunction) {
|
||||
BaseCompositeQuery<T, CT> compositeQuery = new BaseCompositeQuery<>(descriptor, params, execFunction, parallelExecFunction);
|
||||
compositeQuery.setChildQueries(queries);
|
||||
return compositeQuery;
|
||||
}
|
||||
|
||||
|
||||
public BaseCompositeQuery(Object descriptor, QueryParameters params, Function<QueryContext, QueryResult<T, CT>> execFunction, BiFunction<QueryContext, List<QueryResult>, QueryResult<T, CT>> parallelExecFunction) {
|
||||
super(descriptor, params);
|
||||
if (execFunction == null) {
|
||||
throw new NullPointerException("Null exec function is not allowed");
|
||||
}
|
||||
this.execFunction = execFunction;
|
||||
this.parallelExecFunction = parallelExecFunction;
|
||||
}
|
||||
|
||||
public List<Query> getChildQueries() {
|
||||
return queries;
|
||||
}
|
||||
|
||||
public boolean isParallel() {
|
||||
return parallelExecFunction != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult<T, CT> execute(QueryContext context) throws QueryExecutionException {
|
||||
return execFunction.apply(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult<T, CT> execute(QueryContext context, List<QueryResult> collectedResults) throws QueryExecutionException {
|
||||
if (parallelExecFunction != null) {
|
||||
return parallelExecFunction.apply(context, collectedResults);
|
||||
} else {
|
||||
return CompositeQuery.super.execute(context, collectedResults);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setChildQueries(List<Query> queries) {
|
||||
this.queries = queries.stream().peek(query -> query.setParentQuery(this)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
17
src/main/java/com/rbkmoney/magista/dsl/BaseFunction.java
Normal file
17
src/main/java/com/rbkmoney/magista/dsl/BaseFunction.java
Normal file
@ -0,0 +1,17 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
|
||||
public abstract class BaseFunction<T, CT> extends BaseQuery<T, CT> implements FunctionQuery<T, CT> {
|
||||
private final String name;
|
||||
|
||||
public BaseFunction(Object descriptor, QueryParameters params, String name) {
|
||||
super(descriptor, params);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
}
|
54
src/main/java/com/rbkmoney/magista/dsl/BaseQuery.java
Normal file
54
src/main/java/com/rbkmoney/magista/dsl/BaseQuery.java
Normal file
@ -0,0 +1,54 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public abstract class BaseQuery<T, CT> implements Query<T, CT> {
|
||||
private final Object descriptor;
|
||||
private QueryParameters queryParameters;
|
||||
private Query parentQuery;
|
||||
|
||||
public BaseQuery(Object descriptor, QueryParameters params) {
|
||||
if (descriptor == null || params == null) {
|
||||
throw new NullPointerException("Null descriptor or params're not allowed");
|
||||
}
|
||||
this.descriptor = descriptor;
|
||||
this.queryParameters = createQueryParameters(params, params.getDerivedParameters());// y, this is bad
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query getParentQuery() {
|
||||
return parentQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParentQuery(Query query) {
|
||||
this.parentQuery = query;
|
||||
this.queryParameters = createQueryParameters(queryParameters, extractParameters(query));
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameters getQueryParameters() {
|
||||
return queryParameters;
|
||||
}
|
||||
|
||||
protected QueryParameters createQueryParameters(QueryParameters parameters, QueryParameters derivedParameters) {
|
||||
return new QueryParameters(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
protected QueryParameters extractParameters(Query query) {
|
||||
return query == null ? null : query.getQueryParameters();
|
||||
}
|
||||
|
||||
protected <R extends QueryContext> R getContext(QueryContext context, Class<R> expectedType) {
|
||||
if (expectedType.isAssignableFrom(context.getClass())) {
|
||||
return (R) context;
|
||||
} else {
|
||||
throw new QueryExecutionException("Wrong context type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
24
src/main/java/com/rbkmoney/magista/dsl/BaseQueryResult.java
Normal file
24
src/main/java/com/rbkmoney/magista/dsl/BaseQueryResult.java
Normal file
@ -0,0 +1,24 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class BaseQueryResult<T, CT> implements QueryResult<T, CT> {
|
||||
private final Supplier<Stream<T>> streamSupplier;
|
||||
private final Supplier<CT> collectedSuppier;
|
||||
|
||||
public BaseQueryResult(Supplier<Stream<T>> streamSupplier, Supplier<CT> collectedSupplier) {
|
||||
this.streamSupplier = streamSupplier;
|
||||
this.collectedSuppier = collectedSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<T> getDataStream() {
|
||||
return streamSupplier.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CT getCollectedStream() {
|
||||
return collectedSuppier.get();
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
|
||||
public abstract class BaseQueryValidator implements QueryValidator {
|
||||
@Override
|
||||
public void validateParameters(QueryParameters parameters) throws IllegalArgumentException {
|
||||
if (parameters == null) {
|
||||
checkParamsResult(true, "Parameters're not defined");
|
||||
}
|
||||
}
|
||||
|
||||
protected void validateTimePeriod(TemporalAccessor from, TemporalAccessor to) throws IllegalArgumentException {
|
||||
if (from != null && to != null) {
|
||||
Instant fromInstant = Instant.from(from);
|
||||
Instant toInstant = Instant.from(to);
|
||||
if (fromInstant.compareTo(toInstant) > 0) {
|
||||
checkParamsResult(true, "TimeRange is not valid [from: " + fromInstant + ", to: " + toInstant + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected <T extends QueryParameters> T checkParamsType(QueryParameters parameters, Class<? extends QueryParameters> expectedType) {
|
||||
if (!expectedType.isAssignableFrom(parameters.getClass())) {
|
||||
checkParamsResult(true, "Parameters has wrong type:" + parameters.getClass() + ", expected: " + expectedType);
|
||||
}
|
||||
return (T) parameters;
|
||||
}
|
||||
|
||||
protected void checkParamsResult(boolean hasError, String msg) {
|
||||
if (hasError) {
|
||||
throw new IllegalArgumentException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
protected void checkParamsResult(boolean hasError, String fieldName, String msg) {
|
||||
if (hasError) {
|
||||
checkParamsResult(hasError, "Validation failed for field: " + fieldName + ": " + msg);
|
||||
}
|
||||
}
|
||||
}
|
16
src/main/java/com/rbkmoney/magista/dsl/CompositeQuery.java
Normal file
16
src/main/java/com/rbkmoney/magista/dsl/CompositeQuery.java
Normal file
@ -0,0 +1,16 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface CompositeQuery<T, CT> extends Query<T, CT> {
|
||||
List<Query> getChildQueries();
|
||||
|
||||
default boolean isParallel() {
|
||||
return false;
|
||||
}
|
||||
|
||||
default QueryResult<T, CT> execute(QueryContext context, List<QueryResult> collectedResults) throws QueryExecutionException {
|
||||
throw new UnsupportedOperationException("Explicit implementation required");
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public interface FunctionQuery<T, CT> extends Query<T, CT> {
|
||||
|
||||
String getName();
|
||||
}
|
@ -0,0 +1,74 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.rbkmoney.magista.dsl.Parameters.SIZE_PARAMETER;
|
||||
|
||||
public abstract class PagedBaseFunction<T, CT> extends BaseFunction<T, CT> {
|
||||
|
||||
public static final int MAX_SIZE_VALUE = 1000;
|
||||
|
||||
private String continuationToken;
|
||||
|
||||
public PagedBaseFunction(Object descriptor, QueryParameters params, String name, String continuationToken) {
|
||||
super(descriptor, params, name);
|
||||
this.continuationToken = continuationToken;
|
||||
}
|
||||
|
||||
public String getContinuationToken() {
|
||||
return continuationToken;
|
||||
}
|
||||
|
||||
public Optional<Long> getFromId() {
|
||||
return TokenUtil.extractIdValue(continuationToken);
|
||||
}
|
||||
|
||||
public static class PagedBaseParameters extends QueryParameters {
|
||||
|
||||
public PagedBaseParameters(Map<String, Object> parameters, QueryParameters derivedParameters) {
|
||||
super(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
public PagedBaseParameters(QueryParameters parameters, QueryParameters derivedParameters) {
|
||||
super(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
public Integer getSize() {
|
||||
return Optional.ofNullable(getIntParameter(SIZE_PARAMETER, true))
|
||||
.orElse(MAX_SIZE_VALUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class PagedBaseValidator extends BaseQueryValidator {
|
||||
@Override
|
||||
public void validateQuery(Query query) throws IllegalArgumentException {
|
||||
super.validateQuery(query);
|
||||
if (query instanceof PagedBaseFunction) {
|
||||
validateContinuationToken(query.getQueryParameters(), ((PagedBaseFunction) query).getContinuationToken());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateParameters(QueryParameters parameters) throws IllegalArgumentException {
|
||||
super.validateParameters(parameters);
|
||||
PagedBaseParameters pagedBaseParameters = super.checkParamsType(parameters, PagedBaseParameters.class);
|
||||
checkParamsResult(pagedBaseParameters.getSize() > MAX_SIZE_VALUE,
|
||||
String.format(
|
||||
"Size must be less or equals to %d but was %d",
|
||||
MAX_SIZE_VALUE,
|
||||
pagedBaseParameters.getSize()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void validateContinuationToken(QueryParameters queryParameters, String continuationToken) throws BadTokenException {
|
||||
try {
|
||||
TokenUtil.validateToken(queryParameters, continuationToken);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new BadTokenException("Token validation failure", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
6
src/main/java/com/rbkmoney/magista/dsl/Parameters.java
Normal file
6
src/main/java/com/rbkmoney/magista/dsl/Parameters.java
Normal file
@ -0,0 +1,6 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public class Parameters {
|
||||
public static final String QUERY_PARAMETER = "query";
|
||||
public static final String SIZE_PARAMETER = "size";
|
||||
}
|
15
src/main/java/com/rbkmoney/magista/dsl/Query.java
Normal file
15
src/main/java/com/rbkmoney/magista/dsl/Query.java
Normal file
@ -0,0 +1,15 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public interface Query<T, CT> {
|
||||
Object getDescriptor();
|
||||
|
||||
Query getParentQuery();
|
||||
|
||||
void setParentQuery(Query query);
|
||||
|
||||
QueryParameters getQueryParameters();
|
||||
|
||||
QueryResult<T, CT> execute(QueryContext context) throws QueryExecutionException;
|
||||
|
||||
|
||||
}
|
4
src/main/java/com/rbkmoney/magista/dsl/QueryContext.java
Normal file
4
src/main/java/com/rbkmoney/magista/dsl/QueryContext.java
Normal file
@ -0,0 +1,4 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public interface QueryContext {
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public interface QueryContextFactory {
|
||||
QueryContext getContext();
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public class QueryExecutionException extends QueryProcessingException {
|
||||
public QueryExecutionException() {
|
||||
}
|
||||
|
||||
public QueryExecutionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public QueryExecutionException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public QueryExecutionException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public QueryExecutionException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
121
src/main/java/com/rbkmoney/magista/dsl/QueryParameters.java
Normal file
121
src/main/java/com/rbkmoney/magista/dsl/QueryParameters.java
Normal file
@ -0,0 +1,121 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import com.rbkmoney.geck.common.util.TypeUtil;
|
||||
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
public class QueryParameters {
|
||||
public interface QueryParametersRef<T extends QueryParameters> {
|
||||
T newInstance(Map<String, Object> parameters, QueryParameters derivedParameters);
|
||||
}
|
||||
|
||||
private final Map<String, Object> parameters;
|
||||
private final QueryParameters derivedParameters;
|
||||
|
||||
public QueryParameters(Map<String, Object> parameters, QueryParameters derivedParameters) {
|
||||
this.parameters = new HashMap<>(parameters);
|
||||
this.derivedParameters = derivedParameters;
|
||||
}
|
||||
|
||||
public QueryParameters(QueryParameters parameters, QueryParameters derivedParameters) {
|
||||
this(parameters.getParametersMap(), derivedParameters);
|
||||
}
|
||||
|
||||
public Object getParameter(String key, boolean deepSearch) {
|
||||
return parameters.getOrDefault(key, deepSearch && derivedParameters != null ? derivedParameters.getParameter(key, deepSearch) : null);
|
||||
}
|
||||
|
||||
public <T extends QueryParameters> T removeParameters(QueryParametersRef<T> parametersRef, String... keys) {
|
||||
Map<String, Object> newParameters = new HashMap<>(parameters);
|
||||
for (String key : keys) {
|
||||
newParameters.remove(key);
|
||||
}
|
||||
return parametersRef.newInstance(newParameters, derivedParameters);
|
||||
}
|
||||
|
||||
public Long getLongParameter(String key, boolean deepSearch) throws IllegalArgumentException {
|
||||
|
||||
Object val = getParameter(key, deepSearch);
|
||||
if (val instanceof Number) {
|
||||
return ((Number) val).longValue();
|
||||
} else if (val instanceof String) {
|
||||
try {
|
||||
return Long.parseLong(val.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Field:" + key + " has incorrect value", e);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getIntParameter(String key, boolean deepSearch) throws IllegalArgumentException {
|
||||
Object val = getParameter(key, deepSearch);
|
||||
if (val instanceof Number) {
|
||||
return ((Number) val).intValue();
|
||||
} else if (val instanceof String) {
|
||||
try {
|
||||
return Integer.parseInt(val.toString());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Field:" + key + " has incorrect value", e);
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public List getArrayParameter(String key, boolean deepSearch) {
|
||||
Object val = getParameter(key, deepSearch);
|
||||
return val != null ? (List) val : null;
|
||||
}
|
||||
|
||||
public String getStringParameter(String key, boolean deepSearch) {
|
||||
Object val = getParameter(key, deepSearch);
|
||||
return val != null ? val.toString() : null;
|
||||
}
|
||||
|
||||
public TemporalAccessor getTimeParameter(String key, boolean deepSearch) throws IllegalArgumentException {
|
||||
Object val = getParameter(key, deepSearch);
|
||||
if (val instanceof TemporalAccessor) {
|
||||
return (TemporalAccessor) val;
|
||||
} else if (val instanceof String) {
|
||||
return TypeUtil.stringToTemporal(val.toString());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public QueryParameters getDerivedParameters() {
|
||||
return derivedParameters;
|
||||
}
|
||||
|
||||
public Map<String, Object> getParametersMap() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
QueryParameters that = (QueryParameters) o;
|
||||
return Objects.equals(parameters, that.parameters) &&
|
||||
Objects.equals(derivedParameters, that.derivedParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueryParameters{" +
|
||||
"parameters=" + parameters +
|
||||
", derivedParameters=" + (derivedParameters == null ? "null" : "notnull") +
|
||||
'}';
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public class QueryProcessingException extends RuntimeException {
|
||||
public QueryProcessingException() {
|
||||
}
|
||||
|
||||
public QueryProcessingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public QueryProcessingException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public QueryProcessingException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public QueryProcessingException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public interface QueryProcessor<S, R> {
|
||||
R processQuery(S source) throws BadTokenException, QueryProcessingException;
|
||||
}
|
12
src/main/java/com/rbkmoney/magista/dsl/QueryResult.java
Normal file
12
src/main/java/com/rbkmoney/magista/dsl/QueryResult.java
Normal file
@ -0,0 +1,12 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public interface QueryResult<T, CT> {
|
||||
|
||||
Stream<T> getDataStream();
|
||||
|
||||
CT getCollectedStream();
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public interface QueryValidator {
|
||||
void validateParameters(QueryParameters parameters) throws IllegalArgumentException;
|
||||
|
||||
default void validateQuery(Query query) throws IllegalArgumentException {
|
||||
validateParameters(query.getQueryParameters());
|
||||
}
|
||||
}
|
128
src/main/java/com/rbkmoney/magista/dsl/RootQuery.java
Normal file
128
src/main/java/com/rbkmoney/magista/dsl/RootQuery.java
Normal file
@ -0,0 +1,128 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import com.rbkmoney.magista.dsl.builder.AbstractQueryBuilder;
|
||||
import com.rbkmoney.magista.dsl.builder.QueryBuilder;
|
||||
import com.rbkmoney.magista.dsl.builder.QueryBuilderException;
|
||||
import com.rbkmoney.magista.dsl.parser.AbstractQueryParser;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryParserException;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryPart;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static com.rbkmoney.magista.dsl.Parameters.QUERY_PARAMETER;
|
||||
|
||||
public class RootQuery extends BaseQuery {
|
||||
private final Query childQuery;
|
||||
|
||||
private RootQuery(Object descriptor, QueryParameters params, Query childQuery) {
|
||||
super(descriptor, params);
|
||||
this.childQuery = childQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult execute(QueryContext context) throws QueryExecutionException {
|
||||
return childQuery.execute(context);
|
||||
}
|
||||
|
||||
public static class RootParameters extends QueryParameters {
|
||||
public RootParameters(Map<String, Object> parameters, QueryParameters derivedParameters) {
|
||||
super(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
public RootParameters(QueryParameters parameters, QueryParameters derivedParameters) {
|
||||
super(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
public Map getQuery() {
|
||||
Object obj = getParameter(QUERY_PARAMETER, false);
|
||||
return obj instanceof Map ? (Map) obj : null;
|
||||
}
|
||||
|
||||
public QueryParameters getRestParameters() {
|
||||
return removeParameters(QueryParameters::new, QUERY_PARAMETER);
|
||||
}
|
||||
}
|
||||
|
||||
public static class RootValidator extends BaseQueryValidator {
|
||||
public static final String DEFAULT_ERR_MSG_STRING = "invalid or not found";
|
||||
|
||||
@Override
|
||||
public void validateParameters(QueryParameters parameters) throws IllegalArgumentException {
|
||||
super.validateParameters(parameters);
|
||||
RootParameters rootParameters = super.checkParamsType(parameters, RootParameters.class);
|
||||
if (rootParameters.getQuery() == null) {
|
||||
checkParamsResult(true, QUERY_PARAMETER, DEFAULT_ERR_MSG_STRING);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateQuery(Query query) throws IllegalArgumentException {
|
||||
if (query instanceof RootQuery) {
|
||||
Query childQuery = ((RootQuery) query).childQuery;
|
||||
if (childQuery instanceof CompositeQuery) {
|
||||
Optional<? extends Collection> childQueries = Optional.ofNullable(((CompositeQuery) childQuery).getChildQueries());
|
||||
checkParamsResult(
|
||||
!childQueries.isPresent() || childQueries.get().isEmpty(),
|
||||
"Request must contain at least one query"
|
||||
);
|
||||
}
|
||||
} else if (query instanceof CompositeQuery) {
|
||||
checkParamsResult(true, "Request can't hold more than one query, received count: " + ((CompositeQuery) query).getChildQueries().size());
|
||||
} else {
|
||||
assert false : "No other types expected here";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class RootParser extends AbstractQueryParser {
|
||||
private RootValidator validator = new RootValidator();
|
||||
|
||||
@Override
|
||||
public List<QueryPart> parseQuery(Map<String, Object> source, QueryPart parent) throws QueryParserException {
|
||||
RootParameters parameters = getValidatedParameters(source, parent, RootParameters::new, validator);
|
||||
|
||||
return Stream.of(
|
||||
new QueryPart(getMainDescriptor(), new QueryParameters(parameters.getQuery(), parameters), parent),
|
||||
new QueryPart(QueryPart.DEFAULT_DESCRIPTOR, parameters.getRestParameters(), parent))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Map source, QueryPart parent) {
|
||||
return parent == null;
|
||||
}
|
||||
|
||||
public static String getMainDescriptor() {
|
||||
return QUERY_PARAMETER;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RootBuilder extends AbstractQueryBuilder {
|
||||
private RootValidator validator = new RootValidator();
|
||||
|
||||
@Override
|
||||
public Query buildQuery(List<QueryPart> queryParts, String continuationToken, QueryPart parentQueryPart, QueryBuilder baseBuilder) throws QueryBuilderException {
|
||||
Query resultQuery = buildSingleQuery(RootParser.getMainDescriptor(), queryParts, queryPart -> createQuery(queryPart, continuationToken, baseBuilder));
|
||||
validator.validateQuery(resultQuery);
|
||||
return resultQuery;
|
||||
}
|
||||
|
||||
private RootQuery createQuery(QueryPart queryPart, String continuationToken, QueryBuilder baseBuilder) {
|
||||
Query childQuery = baseBuilder.buildQuery(queryPart.getChildren(), continuationToken, queryPart, baseBuilder);
|
||||
RootQuery rootQuery = new RootQuery(queryPart.getDescriptor(), queryPart.getParameters(), childQuery);
|
||||
childQuery.setParentQuery(rootQuery);
|
||||
return rootQuery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(List<QueryPart> queryParts, QueryPart parent) {
|
||||
return getMatchedPartsStream(RootParser.getMainDescriptor(), queryParts).findFirst().isPresent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
23
src/main/java/com/rbkmoney/magista/dsl/TokenUtil.java
Normal file
23
src/main/java/com/rbkmoney/magista/dsl/TokenUtil.java
Normal file
@ -0,0 +1,23 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
public class TokenUtil {
|
||||
|
||||
public static Optional<Long> extractIdValue(String token) {
|
||||
return Optional.ofNullable(token)
|
||||
.map(uuid -> UUID.fromString(uuid).getLeastSignificantBits());
|
||||
}
|
||||
|
||||
public static String buildToken(QueryParameters queryParameters, long id) {
|
||||
return new UUID(queryParameters.hashCode(), id).toString();
|
||||
}
|
||||
|
||||
public static void validateToken(QueryParameters queryParameters, String token) {
|
||||
if (token != null && queryParameters.hashCode() != UUID.fromString(token).getMostSignificantBits()) {
|
||||
throw new IllegalArgumentException("Invalid token");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package com.rbkmoney.magista.dsl.builder;
|
||||
|
||||
import com.rbkmoney.magista.dsl.*;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryPart;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class AbstractQueryBuilder implements QueryBuilder {
|
||||
|
||||
protected Stream<QueryPart> getMatchedPartsStream(Object descriptor, List<QueryPart> queryParts) {
|
||||
return queryParts.stream().filter(queryPart -> descriptor.equals(queryPart.getDescriptor()));
|
||||
}
|
||||
|
||||
|
||||
protected <T, CT> CompositeQuery<T, CT> createCompositeQuery(
|
||||
Object descriptor,
|
||||
QueryParameters derivedParameters,
|
||||
List<Query> childQueries,
|
||||
Function<QueryContext, QueryResult<T, CT>> execFunction,
|
||||
BiFunction<QueryContext, List<QueryResult>, QueryResult<T, CT>> parallelExecFunction
|
||||
) {
|
||||
return BaseCompositeQuery.newInstance(descriptor, new QueryParameters(Collections.emptyMap(), derivedParameters), childQueries, execFunction, parallelExecFunction);
|
||||
}
|
||||
|
||||
protected CompositeQuery createCompositeQuery(
|
||||
Object descriptor,
|
||||
QueryParameters derivedParameters,
|
||||
List<Query> childQueries
|
||||
) {
|
||||
return createCompositeQuery(
|
||||
descriptor, derivedParameters, childQueries,
|
||||
context -> new BaseQueryResult<>(
|
||||
() -> childQueries.stream().map(query -> query.execute(context)),
|
||||
() -> childQueries.stream().map(query -> query.execute(context)).collect(Collectors.toList())
|
||||
),
|
||||
(context, queryResults) -> new BaseQueryResult<>(
|
||||
() -> queryResults.stream(),
|
||||
() -> queryResults
|
||||
)
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
protected <T, CT> CompositeQuery<T, CT> createCompositeQuery(
|
||||
Object descriptor,
|
||||
List<Query> childQueries,
|
||||
Function<QueryContext, QueryResult<T, CT>> execFunction,
|
||||
BiFunction<QueryContext, List<QueryResult>, QueryResult<T, CT>> parallelExecFunction
|
||||
) {
|
||||
return createCompositeQuery(descriptor, new QueryParameters(Collections.emptyMap(), null), childQueries, execFunction, parallelExecFunction);
|
||||
}
|
||||
|
||||
protected QueryParameters getParameters(QueryPart queryPart) {
|
||||
return queryPart == null ? null : queryPart.getParameters();
|
||||
}
|
||||
|
||||
protected List<Query> buildQueries(Object matchDescriptor, List<QueryPart> queryParts, Function<QueryPart, Query> queryCreator) {
|
||||
List<QueryPart> matchedParts = getMatchedPartsStream(matchDescriptor, queryParts).collect(Collectors.toList());
|
||||
return matchedParts.stream().map(queryPart -> queryCreator.apply(queryPart)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
protected Query buildSingleQuery(Object matchDescriptor, List<QueryPart> queryParts, Function<QueryPart, Query> queryCreator) {
|
||||
List<Query> queries = buildQueries(matchDescriptor, queryParts, queryCreator);
|
||||
if (queries.size() == 0) {
|
||||
throw new QueryBuilderException("No queries found in referred data");
|
||||
} else if (queries.size() > 1) {
|
||||
throw new QueryBuilderException("Only one query expected");
|
||||
} else {
|
||||
return queries.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
protected <T, CT> Query<T, CT> buildAndWrapQueries(
|
||||
Object matchDescriptor,
|
||||
List<QueryPart> queryParts,
|
||||
Function<QueryPart, Query> queryCreator,
|
||||
QueryParameters parentParameters,
|
||||
Function<QueryContext, QueryResult<T, CT>> execFunction,
|
||||
BiFunction<QueryContext, List<QueryResult>, QueryResult<T, CT>> parallelExecFunction
|
||||
) {
|
||||
List<Query> queries = buildQueries(matchDescriptor, queryParts, queryCreator);
|
||||
return createCompositeQuery(queries.get(0).getDescriptor(), parentParameters, queries, execFunction, parallelExecFunction);
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package com.rbkmoney.magista.dsl.builder;
|
||||
|
||||
import com.rbkmoney.magista.dsl.BadTokenException;
|
||||
import com.rbkmoney.magista.dsl.Query;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryPart;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class BaseQueryBuilder implements QueryBuilder {
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
|
||||
private final List<QueryBuilder> builders;
|
||||
|
||||
public BaseQueryBuilder(List<QueryBuilder> builders) {
|
||||
this.builders = new ArrayList<>(builders);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query buildQuery(List<QueryPart> queryParts, String continuationToken, QueryPart parentQueryPart, QueryBuilder baseBuilder) throws QueryBuilderException {
|
||||
try {
|
||||
List<Query> queries = queryParts.isEmpty() ? Collections.emptyList() : builders.stream()
|
||||
.filter(
|
||||
builder -> builder.apply(queryParts, parentQueryPart))
|
||||
.map(
|
||||
builder -> builder.buildQuery(queryParts, continuationToken, parentQueryPart, baseBuilder == null ? this : baseBuilder)
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
if (queries.size() > 1) {
|
||||
throw new QueryBuilderException("Build result has more than one query");
|
||||
} else if (queries.size() == 1) {
|
||||
return queries.get(0);
|
||||
} else {
|
||||
log.warn("No builders matched following query parts: {}", queryParts);
|
||||
throw new QueryBuilderException("Can't build query, no match to process");
|
||||
}
|
||||
} catch (BadTokenException | QueryBuilderException e) {
|
||||
throw e;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new QueryBuilderException(iae.getMessage(), iae);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package com.rbkmoney.magista.dsl.builder;
|
||||
|
||||
import com.rbkmoney.magista.dsl.Query;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryPart;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface QueryBuilder {
|
||||
|
||||
Query buildQuery(List<QueryPart> queryParts, String continuationToken, QueryPart parentQueryPart, QueryBuilder baseBuilder) throws QueryBuilderException;
|
||||
|
||||
boolean apply(List<QueryPart> queryParts, QueryPart parent);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.rbkmoney.magista.dsl.builder;
|
||||
|
||||
import com.rbkmoney.magista.dsl.QueryProcessingException;
|
||||
|
||||
public class QueryBuilderException extends QueryProcessingException {
|
||||
public QueryBuilderException() {
|
||||
}
|
||||
|
||||
public QueryBuilderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public QueryBuilderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public QueryBuilderException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public QueryBuilderException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package com.rbkmoney.magista.dsl.parser;
|
||||
|
||||
import com.rbkmoney.magista.dsl.QueryParameters;
|
||||
import com.rbkmoney.magista.dsl.QueryValidator;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractQueryParser implements QueryParser<Map<String, Object>> {
|
||||
|
||||
protected QueryParameters getParameters(QueryPart queryPart) {
|
||||
return queryPart == null ? null : queryPart.getParameters();
|
||||
}
|
||||
|
||||
protected <T extends QueryParameters> T getValidatedParameters(Map<String, Object> source, QueryPart parent, QueryParameters.QueryParametersRef<T> parametersRef, QueryValidator validator) throws QueryParserException {
|
||||
try {
|
||||
T parameters = parametersRef.newInstance(source, getParameters(parent));
|
||||
validator.validateParameters(parameters);
|
||||
return parameters;
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new QueryParserException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.rbkmoney.magista.dsl.parser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public abstract class BaseQueryParser implements QueryParser<Map<String, Object>> {
|
||||
|
||||
private final List<QueryParser<Map<String, Object>>> parsers;
|
||||
|
||||
public BaseQueryParser(List<QueryParser<Map<String, Object>>> parsers) {
|
||||
this.parsers = new ArrayList<>(parsers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryPart> parseQuery(Map<String, Object> source, QueryPart parent) throws QueryParserException {
|
||||
try {
|
||||
return source.isEmpty() ? Collections.emptyList() : parsers.stream()
|
||||
.filter(
|
||||
parser -> parser.apply(source, parent))
|
||||
.flatMap(
|
||||
parser -> parser.parseQuery(source, parent).stream()
|
||||
)
|
||||
.peek(
|
||||
queryPart -> queryPart.setChildren(
|
||||
parseQuery(queryPart.getParameters().getParametersMap(), queryPart)
|
||||
)
|
||||
)
|
||||
.filter(
|
||||
queryPart -> !queryPart.isEmpty()
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
} catch (QueryParserException e) {
|
||||
throw e;
|
||||
} catch (IllegalArgumentException iae) {
|
||||
throw new QueryParserException(iae.getMessage(), iae);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package com.rbkmoney.magista.dsl.parser;
|
||||
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class JsonQueryParser implements QueryParser<String> {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(this.getClass());
|
||||
|
||||
private QueryParser<Map<String, Object>> queryPartParser;
|
||||
|
||||
public JsonQueryParser withQueryParser(QueryParser<Map<String, Object>> queryPartParser){
|
||||
this.queryPartParser = queryPartParser;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<QueryPart> parseQuery(String source) throws QueryParserException {
|
||||
return parseQuery(source, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<QueryPart> parseQuery(String source, QueryPart parent) throws QueryParserException {
|
||||
try {
|
||||
log.info("Received json string request: {}", source);
|
||||
Map<String, Object> jsonMap = parseJsonMap(source);
|
||||
return queryPartParser.parseQuery(jsonMap, parent);
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new QueryParserException("Failed to parse received json request: "+e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean apply(String source, QueryPart parent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected ObjectMapper getMapper() {
|
||||
return new ObjectMapper();
|
||||
}
|
||||
|
||||
protected Map<String, Object> parseJsonMap(String data) throws IOException {
|
||||
return getMapper().readerFor(new TypeReference<Map<String, Object>>() {}).readValue(data);
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.rbkmoney.magista.dsl.parser;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface QueryParser<Src> {
|
||||
List<QueryPart> parseQuery(Src source, QueryPart parent) throws QueryParserException;
|
||||
boolean apply(Src source, QueryPart parent);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package com.rbkmoney.magista.dsl.parser;
|
||||
|
||||
import com.rbkmoney.magista.dsl.QueryExecutionException;
|
||||
|
||||
public class QueryParserException extends QueryExecutionException {
|
||||
public QueryParserException() {
|
||||
}
|
||||
|
||||
public QueryParserException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public QueryParserException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public QueryParserException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public QueryParserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, cause, enableSuppression, writableStackTrace);
|
||||
}
|
||||
}
|
60
src/main/java/com/rbkmoney/magista/dsl/parser/QueryPart.java
Normal file
60
src/main/java/com/rbkmoney/magista/dsl/parser/QueryPart.java
Normal file
@ -0,0 +1,60 @@
|
||||
package com.rbkmoney.magista.dsl.parser;
|
||||
|
||||
import com.rbkmoney.magista.dsl.QueryParameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class QueryPart {
|
||||
public static final Object DEFAULT_DESCRIPTOR = "default_descriptor";
|
||||
private final Object descriptor;
|
||||
private final QueryParameters parameters;
|
||||
private List<QueryPart> children;
|
||||
private final QueryPart parent;
|
||||
|
||||
public QueryPart(Object descriptor, QueryParameters parameters) {
|
||||
this(descriptor, parameters, null);
|
||||
}
|
||||
|
||||
public QueryPart(Object descriptor, QueryParameters parameters, QueryPart parent) {
|
||||
if (descriptor == null || parameters == null) {
|
||||
throw new NullPointerException("Null arguments're not allowed");
|
||||
}
|
||||
this.descriptor = descriptor;
|
||||
this.parameters = parameters;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public Object getDescriptor() {
|
||||
return descriptor;
|
||||
}
|
||||
|
||||
public QueryParameters getParameters() {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
public List<QueryPart> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<QueryPart> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public QueryPart getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return descriptor.equals(DEFAULT_DESCRIPTOR) && parameters.getParametersMap().isEmpty() && (children == null || children.isEmpty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "QueryPart{" +
|
||||
"descriptor=" + descriptor +
|
||||
", parameters=" + parameters +
|
||||
", children=" + children +
|
||||
", parent=" + (parent == null ? "null" : "notnull") +
|
||||
'}';
|
||||
}
|
||||
}
|
207
src/test/java/com/rbkmoney/magista/dsl/DummiesFunction.java
Normal file
207
src/test/java/com/rbkmoney/magista/dsl/DummiesFunction.java
Normal file
@ -0,0 +1,207 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import com.rbkmoney.magista.dsl.builder.AbstractQueryBuilder;
|
||||
import com.rbkmoney.magista.dsl.builder.QueryBuilder;
|
||||
import com.rbkmoney.magista.dsl.builder.QueryBuilderException;
|
||||
import com.rbkmoney.magista.dsl.parser.AbstractQueryParser;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryParserException;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryPart;
|
||||
|
||||
import java.time.temporal.TemporalAccessor;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class DummiesFunction extends PagedBaseFunction<Map.Entry<Long, Dummy>, StatTestResponse> implements CompositeQuery<Map.Entry<Long, Dummy>, StatTestResponse> {
|
||||
|
||||
public static final String FUNC_NAME = "dummies";
|
||||
|
||||
private final CompositeQuery<QueryResult, List<QueryResult>> subquery;
|
||||
|
||||
private DummiesFunction(Object descriptor, QueryParameters params, String continuationToken, CompositeQuery<QueryResult, List<QueryResult>> subquery) {
|
||||
super(descriptor, params, FUNC_NAME, continuationToken);
|
||||
this.subquery = subquery;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult<Map.Entry<Long, Dummy>, StatTestResponse> execute(QueryContext context) throws QueryExecutionException {
|
||||
QueryResult<QueryResult, List<QueryResult>> collectedResults = subquery.execute(context);
|
||||
|
||||
return execute(context, collectedResults.getCollectedStream());
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult<Map.Entry<Long, Dummy>, StatTestResponse> execute(QueryContext context, List<QueryResult> collectedResults) throws QueryExecutionException {
|
||||
QueryResult<Map.Entry<Long, Dummy>, List<Map.Entry<Long, Dummy>>> dummiesResult = (QueryResult<Map.Entry<Long, Dummy>, List<Map.Entry<Long, Dummy>>>) collectedResults.get(0);
|
||||
|
||||
return new BaseQueryResult<>(
|
||||
() -> dummiesResult.getDataStream(),
|
||||
() -> {
|
||||
List<Dummy> statResponseData = dummiesResult.getDataStream()
|
||||
.map(dummyEntry -> dummyEntry.getValue()).collect(Collectors.toList());
|
||||
StatTestResponse statResponse = new StatTestResponse(statResponseData);
|
||||
List<Map.Entry<Long, Dummy>> dummyStats = dummiesResult.getCollectedStream();
|
||||
if (!dummiesResult.getCollectedStream().isEmpty() && getQueryParameters().getSize() == dummyStats.size()) {
|
||||
statResponse.setContinuationToken(
|
||||
TokenUtil.buildToken(
|
||||
getQueryParameters(),
|
||||
dummyStats.get(dummyStats.size() - 1).getKey()
|
||||
)
|
||||
);
|
||||
}
|
||||
return statResponse;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestParameters getQueryParameters() {
|
||||
return (TestParameters) super.getQueryParameters();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected QueryParameters createQueryParameters(QueryParameters parameters, QueryParameters derivedParameters) {
|
||||
return new TestParameters(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Query> getChildQueries() {
|
||||
return subquery.getChildQueries();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParallel() {
|
||||
return subquery.isParallel();
|
||||
}
|
||||
|
||||
public static class TestParameters extends PagedBaseParameters {
|
||||
|
||||
public TestParameters(Map<String, Object> parameters, QueryParameters derivedParameters) {
|
||||
super(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
public TestParameters(QueryParameters parameters, QueryParameters derivedParameters) {
|
||||
super(parameters, derivedParameters);
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return getStringParameter("id", false);
|
||||
}
|
||||
|
||||
public String getKek() {
|
||||
return getStringParameter("kek", false);
|
||||
}
|
||||
|
||||
public TemporalAccessor getFromTime() {
|
||||
return getTimeParameter("from_time", false);
|
||||
}
|
||||
|
||||
public TemporalAccessor getToTime() {
|
||||
return getTimeParameter("to_time", false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class DummiesValidator extends PagedBaseValidator {
|
||||
|
||||
@Override
|
||||
public void validateParameters(QueryParameters parameters) throws IllegalArgumentException {
|
||||
super.validateParameters(parameters);
|
||||
TestParameters testParameters = super.checkParamsType(parameters, TestParameters.class);
|
||||
|
||||
String kek = testParameters.getKek();
|
||||
if (kek != null && !kek.matches("kek")) {
|
||||
checkParamsResult(true, "kek", RootQuery.RootValidator.DEFAULT_ERR_MSG_STRING);
|
||||
}
|
||||
|
||||
validateTimePeriod(testParameters.getFromTime(), testParameters.getToTime());
|
||||
}
|
||||
}
|
||||
|
||||
public static class DummiesParser extends AbstractQueryParser {
|
||||
private DummiesValidator validator = new DummiesValidator();
|
||||
|
||||
@Override
|
||||
public List<QueryPart> parseQuery(Map<String, Object> source, QueryPart parent) throws QueryParserException {
|
||||
Map<String, Object> funcSource = (Map) source.get(FUNC_NAME);
|
||||
TestParameters parameters = getValidatedParameters(funcSource, parent, TestParameters::new, validator);
|
||||
|
||||
return Stream.of(
|
||||
new QueryPart(FUNC_NAME, parameters, parent)
|
||||
)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Map source, QueryPart parent) {
|
||||
return parent != null
|
||||
&& RootQuery.RootParser.getMainDescriptor().equals(parent.getDescriptor())
|
||||
&& (source.get(FUNC_NAME) instanceof Map);
|
||||
}
|
||||
|
||||
public static String getMainDescriptor() {
|
||||
return FUNC_NAME;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DummiesBuilder extends AbstractQueryBuilder {
|
||||
private DummiesValidator validator = new DummiesValidator();
|
||||
|
||||
@Override
|
||||
public Query buildQuery(List<QueryPart> queryParts, String continuationToken, QueryPart parentQueryPart, QueryBuilder baseBuilder) throws QueryBuilderException {
|
||||
Query resultQuery = buildSingleQuery(DummiesParser.getMainDescriptor(), queryParts, queryPart -> createQuery(queryPart, continuationToken));
|
||||
validator.validateQuery(resultQuery);
|
||||
return resultQuery;
|
||||
}
|
||||
|
||||
private CompositeQuery createQuery(QueryPart queryPart, String continuationToken) {
|
||||
List<Query> queries = Arrays.asList(
|
||||
new GetDataFunction(queryPart.getDescriptor() + ":" + GetDataFunction.FUNC_NAME, queryPart.getParameters(), continuationToken)
|
||||
);
|
||||
CompositeQuery<QueryResult, List<QueryResult>> compositeQuery = createCompositeQuery(
|
||||
queryPart.getDescriptor(),
|
||||
getParameters(queryPart.getParent()),
|
||||
queries
|
||||
);
|
||||
return createDummiesFunction(queryPart.getDescriptor(), queryPart.getParameters(), continuationToken, compositeQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(List<QueryPart> queryParts, QueryPart parent) {
|
||||
return getMatchedPartsStream(DummiesParser.getMainDescriptor(), queryParts).findFirst().isPresent();
|
||||
}
|
||||
}
|
||||
|
||||
private static DummiesFunction createDummiesFunction(Object descriptor, QueryParameters queryParameters, String continuationToken, CompositeQuery<QueryResult, List<QueryResult>> subquery) {
|
||||
DummiesFunction dummiesFunction = new DummiesFunction(descriptor, queryParameters, continuationToken, subquery);
|
||||
subquery.setParentQuery(dummiesFunction);
|
||||
return dummiesFunction;
|
||||
}
|
||||
|
||||
private static class GetDataFunction extends PagedBaseFunction<Map.Entry<Long, Dummy>, Collection<Map.Entry<Long, Dummy>>> {
|
||||
private static final String FUNC_NAME = DummiesFunction.FUNC_NAME + "_data";
|
||||
|
||||
public GetDataFunction(Object descriptor, QueryParameters params, String continuationToken) {
|
||||
super(descriptor, params, FUNC_NAME, continuationToken);
|
||||
}
|
||||
|
||||
protected TestQueryContext getContext(QueryContext context) {
|
||||
return super.getContext(context, TestQueryContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryResult<Map.Entry<Long, Dummy>, Collection<Map.Entry<Long, Dummy>>> execute(QueryContext context) throws QueryExecutionException {
|
||||
TestQueryContext functionContext = getContext(context);
|
||||
TestParameters parameters = new TestParameters(getQueryParameters(), getQueryParameters().getDerivedParameters());
|
||||
Collection<Map.Entry<Long, Dummy>> result = functionContext.getSearchDao().getDummies(
|
||||
parameters.getId()
|
||||
);
|
||||
return new BaseQueryResult<>(() -> result.stream(), () -> result);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
47
src/test/java/com/rbkmoney/magista/dsl/Dummy.java
Normal file
47
src/test/java/com/rbkmoney/magista/dsl/Dummy.java
Normal file
@ -0,0 +1,47 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public class Dummy {
|
||||
private Long id;
|
||||
private String kek;
|
||||
private String fromTime;
|
||||
private String toTime;
|
||||
|
||||
public Dummy(Long id, String kek, String fromTime, String toTime) {
|
||||
this.id = id;
|
||||
this.kek = kek;
|
||||
this.fromTime = fromTime;
|
||||
this.toTime = toTime;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getKek() {
|
||||
return kek;
|
||||
}
|
||||
|
||||
public void setKek(String kek) {
|
||||
this.kek = kek;
|
||||
}
|
||||
|
||||
public String getFromTime() {
|
||||
return fromTime;
|
||||
}
|
||||
|
||||
public void setFromTime(String fromTime) {
|
||||
this.fromTime = fromTime;
|
||||
}
|
||||
|
||||
public String getToTime() {
|
||||
return toTime;
|
||||
}
|
||||
|
||||
public void setToTime(String toTime) {
|
||||
this.toTime = toTime;
|
||||
}
|
||||
}
|
17
src/test/java/com/rbkmoney/magista/dsl/SearchDao.java
Normal file
17
src/test/java/com/rbkmoney/magista/dsl/SearchDao.java
Normal file
@ -0,0 +1,17 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SearchDao {
|
||||
|
||||
public static final List<Dummy> allDummies = Arrays.asList(
|
||||
new Dummy(1L, "kek", "2016-03-22T06:12:27Z", "2016-03-22T06:12:27Z"),
|
||||
new Dummy(2L, "kek", "2016-03-22T06:12:27Z", "2016-03-22T06:12:27Z"),
|
||||
new Dummy(3L, "kek", "2016-03-22T06:12:27Z", "2016-03-22T06:12:27Z"),
|
||||
new Dummy(4L, "kek", "2016-03-22T06:12:27Z", "2016-03-22T06:12:27Z")
|
||||
);
|
||||
public Collection<Map.Entry<Long, Dummy>> getDummies(String id) {
|
||||
return allDummies.stream().filter(d -> id.equals(d.getId().toString())).map(d -> new AbstractMap.SimpleEntry<>(d.getId(), d)).collect(Collectors.toList());
|
||||
}
|
||||
}
|
27
src/test/java/com/rbkmoney/magista/dsl/StatTestRequest.java
Normal file
27
src/test/java/com/rbkmoney/magista/dsl/StatTestRequest.java
Normal file
@ -0,0 +1,27 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public class StatTestRequest {
|
||||
|
||||
private String dsl;
|
||||
private String continuationToken;
|
||||
|
||||
public StatTestRequest(String dsl) {
|
||||
this.dsl = dsl;
|
||||
}
|
||||
|
||||
public String getDsl() {
|
||||
return dsl;
|
||||
}
|
||||
|
||||
public void setDsl(String dsl) {
|
||||
this.dsl = dsl;
|
||||
}
|
||||
|
||||
public String getContinuationToken() {
|
||||
return continuationToken;
|
||||
}
|
||||
|
||||
public void setContinuationToken(String continuationToken) {
|
||||
this.continuationToken = continuationToken;
|
||||
}
|
||||
}
|
29
src/test/java/com/rbkmoney/magista/dsl/StatTestResponse.java
Normal file
29
src/test/java/com/rbkmoney/magista/dsl/StatTestResponse.java
Normal file
@ -0,0 +1,29 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StatTestResponse {
|
||||
|
||||
private StatTestResponseData statTestResponseData;
|
||||
private String continuationToken;
|
||||
|
||||
public StatTestResponse(List<Dummy> statResponseData) {
|
||||
this.statTestResponseData = new StatTestResponseData(statResponseData);
|
||||
}
|
||||
|
||||
public StatTestResponseData getStatTestResponseData() {
|
||||
return statTestResponseData;
|
||||
}
|
||||
|
||||
public void setStatTestResponseData(StatTestResponseData statTestResponseData) {
|
||||
this.statTestResponseData = statTestResponseData;
|
||||
}
|
||||
|
||||
public String getContinuationToken() {
|
||||
return continuationToken;
|
||||
}
|
||||
|
||||
public void setContinuationToken(String continuationToken) {
|
||||
this.continuationToken = continuationToken;
|
||||
}
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class StatTestResponseData {
|
||||
private List<Dummy> dummies;
|
||||
|
||||
public List<Dummy> getDummies() {
|
||||
return dummies;
|
||||
}
|
||||
|
||||
public void setDummies(List<Dummy> dummies) {
|
||||
this.dummies = dummies;
|
||||
}
|
||||
|
||||
public StatTestResponseData(List<Dummy> dummies) {
|
||||
this.dummies = dummies;
|
||||
}
|
||||
}
|
14
src/test/java/com/rbkmoney/magista/dsl/TestQueryContext.java
Normal file
14
src/test/java/com/rbkmoney/magista/dsl/TestQueryContext.java
Normal file
@ -0,0 +1,14 @@
|
||||
package com.rbkmoney.magista.dsl;
|
||||
|
||||
public class TestQueryContext implements QueryContext {
|
||||
|
||||
private final SearchDao searchDao;
|
||||
|
||||
public TestQueryContext(SearchDao searchDao) {
|
||||
this.searchDao = searchDao;
|
||||
}
|
||||
|
||||
public SearchDao getSearchDao() {
|
||||
return searchDao;
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
package com.rbkmoney.magista.dsl.builder;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.magista.dsl.DummiesFunction;
|
||||
import com.rbkmoney.magista.dsl.Query;
|
||||
import com.rbkmoney.magista.dsl.RootQuery;
|
||||
import com.rbkmoney.magista.dsl.parser.BaseQueryParser;
|
||||
import com.rbkmoney.magista.dsl.parser.JsonQueryParser;
|
||||
import com.rbkmoney.magista.dsl.parser.QueryPart;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class QueryBuilderImplTest {
|
||||
JsonQueryParser parser = new JsonQueryParser() {
|
||||
@Override
|
||||
protected ObjectMapper getMapper() {
|
||||
ObjectMapper mapper = super.getMapper();
|
||||
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
return mapper;
|
||||
}
|
||||
}.withQueryParser(new BaseQueryParser(Arrays.asList(new RootQuery.RootParser(), new DummiesFunction.DummiesParser())) {
|
||||
@Override
|
||||
public boolean apply(Map<String, Object> source, QueryPart parent) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
private QueryBuilder builder = new BaseQueryBuilder(Arrays.asList(new RootQuery.RootBuilder(), new DummiesFunction.DummiesBuilder())) {
|
||||
@Override
|
||||
public boolean apply(List<QueryPart> queryParts, QueryPart parent) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
String json = "{'query': {'dummies': {'id': '1','kek':'kek','from_time': '2016-03-22T00:12:00Z','to_time': '2016-03-22T01:12:00Z'}}}";
|
||||
Query query = buildQuery(json);
|
||||
assertTrue(query instanceof RootQuery);
|
||||
query.getDescriptor();
|
||||
}
|
||||
|
||||
@Test(expected = QueryBuilderException.class)
|
||||
public void testNoFunctionParse() {
|
||||
String json = "{'query': {'payments_geo_stat1': {}}}";
|
||||
Query query = buildQuery(json);
|
||||
fail("no functions in oot query, should not reach this point");
|
||||
}
|
||||
|
||||
Query buildQuery(String json) {
|
||||
List<QueryPart> queryParts = parser.parseQuery(json);
|
||||
return builder.buildQuery(queryParts, null, null, null);
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package com.rbkmoney.magista.dsl.parser;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.magista.dsl.DummiesFunction;
|
||||
import com.rbkmoney.magista.dsl.RootQuery;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class JsonQueryParserTest {
|
||||
|
||||
JsonQueryParser parser = new JsonQueryParser() {
|
||||
@Override
|
||||
protected ObjectMapper getMapper() {
|
||||
ObjectMapper mapper = super.getMapper();
|
||||
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
return mapper;
|
||||
}
|
||||
}.withQueryParser(new BaseQueryParser(Arrays.asList(new RootQuery.RootParser(), new DummiesFunction.DummiesParser())) {
|
||||
@Override
|
||||
public boolean apply(Map<String, Object> source, QueryPart parent) {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
@Test
|
||||
public void testNoFunctionParse() {
|
||||
String json = "{'query': {'keksiki': {}}}";
|
||||
List<QueryPart> queryParts = parser.parseQuery(json);
|
||||
assertEquals("root query", 1, queryParts.size());
|
||||
assertEquals("root query has 0 parameter - no recognized function names", 0, queryParts.get(0).getChildren().size());
|
||||
|
||||
}
|
||||
|
||||
@Test(expected = QueryParserException.class)
|
||||
public void testNoQueryParse() {
|
||||
String json = "{'query1': {'dummies': {}}}";
|
||||
List<QueryPart> queryParts = parser.parseQuery(json);
|
||||
fail("no root query, should not reach this point");
|
||||
}
|
||||
|
||||
@Test(expected = QueryParserException.class)
|
||||
public void testKekParseError() {
|
||||
String json = "{'query': {'dummies': {'id': '1','kek': 'kekekekkekekekke','from_time': '2016-03-22T00:12:00Z','to_time': '2016-03-22T01:12:00Z'}}}";
|
||||
parser.parseQuery(json);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHappyPath() {
|
||||
String json = "{'query': {'dummies': {'id': '1','kek': 'kek','from_time': '2016-03-22T00:12:00Z','to_time': '2016-03-22T01:12:00Z'}}}";
|
||||
parser.parseQuery(json);
|
||||
}
|
||||
|
||||
@Test(expected = QueryParserException.class)
|
||||
public void testTimeParseError() {
|
||||
String json = "{'query': {'dummies': {'id': '1','kek': '2','from_time': '2016-03-22T00:12:00Z','to_time': '2016-03-22T00:00:00Z'}}}";
|
||||
parser.parseQuery(json);
|
||||
}
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package com.rbkmoney.magista.dsl.search;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.rbkmoney.magista.dsl.*;
|
||||
import com.rbkmoney.magista.dsl.builder.BaseQueryBuilder;
|
||||
import com.rbkmoney.magista.dsl.builder.QueryBuilder;
|
||||
import com.rbkmoney.magista.dsl.parser.*;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class DummiesSearchQueryTest {
|
||||
|
||||
private final BaseQueryParser queryPartParser = new BaseQueryParser(Arrays.asList(new RootQuery.RootParser(), new DummiesFunction.DummiesParser())) {
|
||||
@Override
|
||||
public boolean apply(Map<String, Object> source, QueryPart parent) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private final BaseQueryBuilder baseQueryBuilder = new BaseQueryBuilder(Arrays.asList(new RootQuery.RootBuilder(), new DummiesFunction.DummiesBuilder())) {
|
||||
@Override
|
||||
public boolean apply(List<QueryPart> queryParts, QueryPart parent) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
private final JsonQueryParser jsonQueryParser = new JsonQueryParser() {
|
||||
@Override
|
||||
protected ObjectMapper getMapper() {
|
||||
ObjectMapper mapper = super.getMapper();
|
||||
mapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
|
||||
return mapper;
|
||||
}
|
||||
}.withQueryParser(queryPartParser);
|
||||
|
||||
QueryProcessorImpl queryProcessor = new QueryProcessorImpl(jsonQueryParser, baseQueryBuilder, new TestQueryContext(new SearchDao()));
|
||||
|
||||
@Test
|
||||
public void testPayments() {
|
||||
String json = "{'query': {'dummies': {'id': '1','kek': 'kek','from_time': '2016-03-22T00:12:00Z','to_time': '2016-03-22T01:12:00Z'}}}";
|
||||
StatTestResponse statResponse = queryProcessor.processQuery(new StatTestRequest(json));
|
||||
assertEquals(1, statResponse.getStatTestResponseData().getDummies().size());
|
||||
}
|
||||
|
||||
@Test(expected = QueryParserException.class)
|
||||
public void testWhenSizeOverflow() {
|
||||
String json = "{'query': {'dummies': {'size': 1001}}}";
|
||||
queryProcessor.processQuery(new StatTestRequest(json));
|
||||
}
|
||||
|
||||
|
||||
@Test(expected = BadTokenException.class)
|
||||
public void testBadToken() {
|
||||
String json = "{'query': {'dummies': {'id': '1','kek': 'kek','from_time': '2016-03-22T00:12:00Z','to_time': '2016-03-22T01:12:00Z'}}}";
|
||||
StatTestRequest statRequest = new StatTestRequest(json);
|
||||
statRequest.setContinuationToken(UUID.randomUUID().toString());
|
||||
queryProcessor.processQuery(statRequest);
|
||||
}
|
||||
|
||||
|
||||
class QueryProcessorImpl implements QueryProcessor<StatTestRequest, StatTestResponse> {
|
||||
private QueryParser<String> sourceParser;
|
||||
private QueryBuilder queryBuilder;
|
||||
private QueryContext queryContext;
|
||||
|
||||
public QueryProcessorImpl(QueryParser<String> sourceParser, QueryBuilder queryBuilder, QueryContext queryContext) {
|
||||
this.sourceParser = sourceParser;
|
||||
this.queryBuilder = queryBuilder;
|
||||
this.queryContext = queryContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StatTestResponse processQuery(StatTestRequest source) throws BadTokenException, QueryProcessingException {
|
||||
List<QueryPart> queryParts = sourceParser.parseQuery(source.getDsl(), null);
|
||||
Query query = queryBuilder.buildQuery(queryParts, source.getContinuationToken(), null, null);
|
||||
QueryResult queryResult = query.execute(queryContext);
|
||||
Object result = queryResult.getCollectedStream();
|
||||
if (result instanceof StatTestResponse) {
|
||||
return (StatTestResponse) result;
|
||||
} else {
|
||||
throw new QueryProcessingException("QueryResult has wrong type: " + result.getClass().getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
src/test/resources/log4j.properties
Normal file
9
src/test/resources/log4j.properties
Normal file
@ -0,0 +1,9 @@
|
||||
#log4j.rootLogger=ALL, stdout
|
||||
log4j.logger.com.rbkmoney=INFO, stdout
|
||||
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.stdout.Target=System.out
|
||||
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p Span[%X{trace_id}-%X{span_id}-%X{parent_id}] SubsKey[%X{es_subscription_key}] - %m%n
|
||||
|
||||
log4j.logger.org.eclipse.jetty=INFO, stdout
|
||||
|
Loading…
Reference in New Issue
Block a user