Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preliminary DynamoDB update expression support #37

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
11 changes: 8 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@
target
.idea
META-INF
out
build
/out
/classes
/build
.gradle

.classpath
.project
.settings
/bin/
/test-output
23 changes: 23 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -57,13 +57,16 @@ dependencies {
version: "2.0.1");
compile(group: "com.github.fge", name: "jackson-coreutils",
version: "1.6");
compile(group: "com.amazonaws", name:"aws-java-sdk-dynamodb", version:"1.11.27");
testCompile(group: "org.testng", name: "testng", version: "6.8.7") {
exclude(group: "junit", module: "junit");
exclude(group: "org.beanshell", module: "bsh");
exclude(group: "org.yaml", module: "snakeyaml");
};
testCompile(group: "org.mockito", name: "mockito-core", version: "1.9.5");
testCompile(group: "org.assertj", name: "assertj-core", version: "1.7.0");
testCompile(group:"com.jayway.jsonpath", name:"json-path-assert", version:"2.2.0");
testCompile(group: "com.amazonaws", name:"DynamoDBLocal", version:"1.11.0.1");
}

javadoc.options.links("http://docs.oracle.com/javase/6/docs/api/");
Expand All @@ -80,6 +83,26 @@ javadoc.options.links("http://fge.github.io/jackson-coreutils/");
*/
repositories {
mavenCentral();
maven {
url 'http://dynamodb-local.s3-website-us-west-2.amazonaws.com/release'
}

}

task copyNativeDeps(type: Copy) {
from (configurations.testCompile) {
include "*.dylib"
include "*.so"
include "*.dll"
}
into 'build/libs'
}

test.dependsOn copyNativeDeps
test.doFirst {
systemProperty "java.library.path", 'build/libs'
environment "DYLD_LIBRARY_PATH", './build/libs'
environment "LD_LIBRARY_PATH", './build/libs'
}

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.amazonaws.services.dynamodbv2.xspec;

import com.amazonaws.services.dynamodbv2.xspec.NULL;

/**
* TODO for daisuke
*
* @since 0.13
* @version $Id$
* @author Alexander Patrikalakis
*/
public class NULLComparable extends NULL {
public NULLComparable(String path) {
super(path);
}
public ComparatorCondition eq(Operand that) {
return new ComparatorCondition("=", this, that);
}
public static final LiteralOperand generateNull() {
return new LiteralOperand((Object) null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.amazonaws.services.dynamodbv2.xspec;

/**
* TODO for daisuke
*
* @since 0.13
* @version $Id$
* @author Alexander Patrikalakis
*/
public class PathSetAction extends UpdateAction {
public PathSetAction(PathOperand attr, PathOperand value) {
super("SET", attr, value);
}
}
17 changes: 16 additions & 1 deletion src/main/java/com/github/fge/jsonpatch/AddOperation.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Alexander Patrikalakis ([email protected])
* Copyright (c) 2015, Daisuke Miyamoto ([email protected])
*
* This software is dual-licensed under:
*
Expand All @@ -19,6 +21,7 @@

package com.github.fge.jsonpatch;

import com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -28,6 +31,7 @@
import com.github.fge.jackson.jsonpointer.ReferenceToken;
import com.github.fge.jackson.jsonpointer.TokenResolver;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;


/**
Expand Down Expand Up @@ -99,6 +103,17 @@ public JsonNode apply(final JsonNode node)
? addToArray(path, node)
: addToObject(path, node);
}

@Override
public void applyToBuilder(ExpressionSpecBuilder builder) {
final TokenResolver<JsonNode> node = Iterators.getLast(path.iterator(), null /*default*/);
if(null == node || "-".equals(node.getToken().getRaw())) {
//list_append
throw new UnsupportedOperationException("list_append not supported yet");
} else {
super.applyToBuilder(builder);
}
}

private JsonNode addToArray(final JsonPointer path, final JsonNode node)
throws JsonPatchException
Expand Down Expand Up @@ -133,7 +148,7 @@ private JsonNode addToObject(final JsonPointer path, final JsonNode node)
{
final JsonNode ret = node.deepCopy();
final ObjectNode target = (ObjectNode) path.parent().get(ret);
target.put(Iterables.getLast(path).getToken().getRaw(), value);
target.set(Iterables.getLast(path).getToken().getRaw(), value);
return ret;
}
}
13 changes: 13 additions & 0 deletions src/main/java/com/github/fge/jsonpatch/CopyOperation.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Alexander Patrikalakis ([email protected])
* Copyright (c) 2015, Daisuke Miyamoto ([email protected])
*
* This software is dual-licensed under:
*
Expand All @@ -19,6 +21,8 @@

package com.github.fge.jsonpatch;

import com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder;
import com.amazonaws.services.dynamodbv2.xspec.PathSetAction;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.JsonNode;
Expand Down Expand Up @@ -60,4 +64,13 @@ public JsonNode apply(final JsonNode node)
"jsonPatch.noSuchPath"));
return new AddOperation(path, dupData).apply(node);
}

@Override
public void applyToBuilder(ExpressionSpecBuilder builder) {
String copyPath = pathGenerator.apply(from);
String setPath = pathGenerator.apply(path);
//set the attribute in the path location
builder.addUpdate(new PathSetAction(ExpressionSpecBuilder.attribute(setPath),
ExpressionSpecBuilder.attribute(copyPath)));
}
}
25 changes: 21 additions & 4 deletions src/main/java/com/github/fge/jsonpatch/JsonPatch.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Alexander Patrikalakis ([email protected])
* Copyright (c) 2015, Daisuke Miyamoto ([email protected])
*
* This software is dual-licensed under:
*
Expand All @@ -19,6 +21,7 @@

package com.github.fge.jsonpatch;

import com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonNode;
Expand All @@ -28,6 +31,7 @@
import com.github.fge.jackson.JacksonUtils;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.github.fge.msgsimple.load.MessageBundles;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableList;

import java.io.IOException;
Expand Down Expand Up @@ -89,16 +93,16 @@
* <p><b>IMPORTANT NOTE:</b> the JSON Patch is supposed to be VALID when the
* constructor for this class ({@link JsonPatch#fromJson(JsonNode)} is used.</p>
*/
public final class JsonPatch
implements JsonSerializable
public class JsonPatch
implements JsonSerializable, Supplier<ExpressionSpecBuilder>
{
private static final MessageBundle BUNDLE
= MessageBundles.getBundle(JsonPatchMessages.class);

/**
* List of operations
*/
private final List<JsonPatchOperation> operations;
protected final List<JsonPatchOperation> operations;

/**
* Constructor
Expand Down Expand Up @@ -126,7 +130,7 @@ public static JsonPatch fromJson(final JsonNode node)
throws IOException
{
BUNDLE.checkNotNull(node, "jsonPatch.nullInput");
return JacksonUtils.getReader().withType(JsonPatch.class)
return JacksonUtils.getReader().forType(JsonPatch.class)
.readValue(node);
}

Expand All @@ -148,6 +152,19 @@ public JsonNode apply(final JsonNode node)

return ret;
}

/**
* Converts this JsonPatch into an ExpressionSpecBuilder
* @return an expression spec builder that contains the updates contained in this
* patch
*/
public ExpressionSpecBuilder get() {
ExpressionSpecBuilder builder = new ExpressionSpecBuilder();
for(JsonPatchOperation operation : operations) {
operation.applyToBuilder(builder);
}
return builder;
}

@Override
public String toString()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Alexander Patrikalakis ([email protected])
*
* This software is dual-licensed under:
*
Expand All @@ -19,6 +20,7 @@

package com.github.fge.jsonpatch;

@SuppressWarnings("serial")
public final class JsonPatchException
extends Exception
{
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/com/github/fge/jsonpatch/JsonPatchOperation.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
/*
* Copyright (c) 2014, Francis Galiegue ([email protected])
* Copyright (c) 2016, Alexander Patrikalakis ([email protected])
* Copyright (c) 2015, Daisuke Miyamoto ([email protected])
*
* This software is dual-licensed under:
*
Expand Down Expand Up @@ -27,10 +29,13 @@
import com.github.fge.jackson.jsonpointer.JsonPointer;
import com.github.fge.msgsimple.bundle.MessageBundle;
import com.github.fge.msgsimple.load.MessageBundles;
import com.google.common.base.Function;

import static com.fasterxml.jackson.annotation.JsonSubTypes.*;
import static com.fasterxml.jackson.annotation.JsonTypeInfo.*;

import com.amazonaws.services.dynamodbv2.xspec.ExpressionSpecBuilder;

@JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "op")

@JsonSubTypes({
Expand Down Expand Up @@ -61,6 +66,8 @@ public abstract class JsonPatchOperation
{
protected static final MessageBundle BUNDLE
= MessageBundles.getBundle(JsonPatchMessages.class);

Function<JsonPointer, String> pathGenerator = new JsonPathToAttributePath();

protected final String op;

Expand Down Expand Up @@ -94,6 +101,12 @@ protected JsonPatchOperation(final String op, final JsonPointer path)
public abstract JsonNode apply(final JsonNode node)
throws JsonPatchException;

/**
* Apply the current patch operation to an update expression builder
* @param builder the builder to apply this expression to
*/
public abstract void applyToBuilder(ExpressionSpecBuilder builder);

@Override
public abstract String toString();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
* Copyright (c) 2016, Alexander Patrikalakis ([email protected])
* Copyright (c) 2015, Daisuke Miyamoto ([email protected])
*
* This software is dual-licensed under:
*
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
* later version;
* - the Apache Software License (ASL) version 2.0.
*
* The text of this file and of both licenses is available at the root of this
* project or, if you have the jar distribution, in directory META-INF/, under
* the names LGPL-3.0.txt and ASL-2.0.txt respectively.
*
* Direct link to the sources:
*
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/
package com.github.fge.jsonpatch;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.jsonpointer.JsonPointer;
import com.github.fge.jackson.jsonpointer.TokenResolver;
import com.google.common.base.Function;
import com.google.common.base.Joiner;

public class JsonPathToAttributePath implements Function<JsonPointer, String> {

private static Pattern ARRAY_PATTERN = Pattern.compile("(0|[1-9][0-9]+)");


@Override
public String apply(JsonPointer pointer) {
List<String> elements = new ArrayList<String>();
for (TokenResolver<JsonNode> tokenResolver : pointer) {
String token = tokenResolver.getToken().getRaw();
if (ARRAY_PATTERN.matcher(token).matches()) {
String last = elements.get(elements.size() - 1);
elements.set(elements.size() - 1, String.format("%s[%s]", last, token));
} else {
elements.add(token);
}
}

return Joiner.on(".").join(elements);
}
}
Loading