diff --git a/core/src/main/groovyx/gaelyk/datastore/DatastoreEntity.java b/core/src/main/groovyx/gaelyk/datastore/DatastoreEntity.java index 39428c70..66497113 100644 --- a/core/src/main/groovyx/gaelyk/datastore/DatastoreEntity.java +++ b/core/src/main/groovyx/gaelyk/datastore/DatastoreEntity.java @@ -1,6 +1,7 @@ package groovyx.gaelyk.datastore; import java.util.List; +import org.codehaus.groovy.ast.PropertyNode; /** * Helper interface to speed up Object to Entity coercion skipping unnecessary reflection which @@ -142,5 +143,4 @@ public interface DatastoreEntity { * @param newValue the new value for the property */ void setProperty(String propertyName, Object newValue); - } diff --git a/core/src/main/groovyx/gaelyk/datastore/DatastoreEntityCoercion.java b/core/src/main/groovyx/gaelyk/datastore/DatastoreEntityCoercion.java index e31082e6..a8c50831 100755 --- a/core/src/main/groovyx/gaelyk/datastore/DatastoreEntityCoercion.java +++ b/core/src/main/groovyx/gaelyk/datastore/DatastoreEntityCoercion.java @@ -3,9 +3,13 @@ import com.google.appengine.api.datastore.Entities; import com.google.appengine.api.datastore.Entity; import com.google.appengine.api.datastore.Text; +import org.codehaus.groovy.ast.PropertyNode; import groovy.lang.GString; import groovyx.gaelyk.extensions.DatastoreExtensions; +import groovyx.gaelyk.datastore.ReflectionEntityCoercion; + +import java.util.List; import static java.nio.charset.StandardCharsets.UTF_8; @@ -113,31 +117,31 @@ private static Object transformValueForStorage(Object value) { dsEntity.setDatastoreVersion(0); } } - for(String propertyName : dsEntity.getDatastoreIndexedProperties()){ - setEntityProperty(en, dsEntity, propertyName); + List indexedProperties = dsEntity.getDatastoreIndexedProperties(); + List indexedTypes = dsEntity.getDatastoreIndexedPropertiesTypes(); + for (int i = 0; i < indexedProperties.size(); i++) { + setEntityProperty(en, dsEntity, indexedProperties.get(i), indexedTypes.get(i)); } - for(String propertyName : dsEntity.getDatastoreUnindexedProperties()){ - setEntityProperty(en, dsEntity, propertyName); + List unindexedProperties = dsEntity.getDatastoreUnindexedProperties(); + List unindexedTypes = dsEntity.getDatastoreUnindexedPropertiesTypes(); + for (int i = 0; i < unindexedProperties.size(); i++) { + setEntityProperty(en, dsEntity, unindexedProperties.get(i), unindexedTypes.get(i)); } return dsEntity; } - private static > void setEntityProperty(Entity en, E dsEntity, String propertyName) { + private static > void setEntityProperty(Entity en, E dsEntity, String propertyName, Class propertyType) { if (!en.hasProperty(propertyName)) { // the property doesn't have the property set so let it blank // this is important for keeping default values return; } Object value = en.getProperty(propertyName); - if (value instanceof Text) { - value = ((Text) value).getValue(); - } try { - dsEntity.setProperty(propertyName, value); + dsEntity.setProperty(propertyName, ReflectionEntityCoercion.transformValueForRetrieval(value, propertyType)); } catch (Exception e) { throw new IllegalArgumentException("Problem setting value '" + value + "' to property '" + propertyName + "' of entity " + dsEntity.getClass().getSimpleName(), e); } - } } diff --git a/core/src/main/groovyx/gaelyk/datastore/EntityTransformation.groovy b/core/src/main/groovyx/gaelyk/datastore/EntityTransformation.groovy index be76842e..7e6b1251 100644 --- a/core/src/main/groovyx/gaelyk/datastore/EntityTransformation.groovy +++ b/core/src/main/groovyx/gaelyk/datastore/EntityTransformation.groovy @@ -409,7 +409,6 @@ class EntityTransformation extends AbstractASTTransformation { } parent.addField new FieldNode('DATASTORE_INDEXED_PROPERTIES', Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL, getBoundListNode(ClassHelper.STRING_TYPE), parent, buildList(indexed)) - parent.addMethod new MethodNode( 'getDatastoreIndexedProperties', Modifier.PUBLIC, @@ -500,7 +499,6 @@ class EntityTransformation extends AbstractASTTransformation { list } - private Expression buildClassList(List values) { ListExpression list = new ListExpression() for (ClassNode value in values) { @@ -526,7 +524,7 @@ class EntityTransformation extends AbstractASTTransformation { ClassNode.EMPTY_ARRAY, block ).with { MethodNode self -> - self.lineNumber = 10013 + self.lineNumber = 10015 self.columnNumber = 1 self } @@ -563,7 +561,7 @@ class EntityTransformation extends AbstractASTTransformation { ClassNode.EMPTY_ARRAY, block ).with { MethodNode self -> - self.lineNumber = 10014 + self.lineNumber = 10016 self.columnNumber = 1 self } diff --git a/core/src/main/groovyx/gaelyk/datastore/ReflectionEntityCoercion.groovy b/core/src/main/groovyx/gaelyk/datastore/ReflectionEntityCoercion.groovy index a1193a84..1d39d638 100755 --- a/core/src/main/groovyx/gaelyk/datastore/ReflectionEntityCoercion.groovy +++ b/core/src/main/groovyx/gaelyk/datastore/ReflectionEntityCoercion.groovy @@ -25,6 +25,8 @@ import java.lang.reflect.Modifier import com.google.appengine.api.datastore.Entities import com.google.appengine.api.datastore.Entity import com.google.appengine.api.datastore.EntityNotFoundException +import com.google.appengine.api.datastore.Link +import com.google.appengine.api.blobstore.BlobKey import static groovyx.gaelyk.extensions.DatastoreExtensions.transformValueForRetrieval import static groovyx.gaelyk.extensions.DatastoreExtensions.transformValueForStorage @@ -193,6 +195,32 @@ class ReflectionEntityCoercion { return entity } + public static Object transformValueForRetrieval(Object value, Class type) { + // Google Client Libraries do not support Link and BlobKey. + // They automatically map them to String. If a properties type is String, + // consistant behaviour is expected. + // https://googleapis.dev/java/google-cloud-clients/latest/com/google/cloud/datastore/Value.html + switch (type) { // setting to + case BlobKey: + switch (value) { // from + case String: return new BlobKey((String) value) + } + break + case Link: + switch (value) { // from + case String: return new Link((String) value) + } + break + case String: + switch (value) { // from + case BlobKey: return ((BlobKey) value).getKeyString() + case Link: return value.toString() + } + } // otherwise normal behaviour + return transformValueForRetrieval(value) + } + + /** * Convert an entity into an object * @@ -213,9 +241,10 @@ class ReflectionEntityCoercion { } } else { entityProps.each { String k, v -> - if (o.metaClass.hasProperty(o, k)) { + def prop = o.metaClass.hasProperty(o, k) + if (prop) { try { - o[k] = transformValueForRetrieval(v) + o[k] = transformValueForRetrieval(v, prop.type) } catch(ReadOnlyPropertyException rope){ // cannot set read only property! } diff --git a/core/src/test/groovyx/gaelyk/datastore/EntityTransformationSpec.groovy b/core/src/test/groovyx/gaelyk/datastore/EntityTransformationSpec.groovy index b4f2c960..3659f5de 100644 --- a/core/src/test/groovyx/gaelyk/datastore/EntityTransformationSpec.groovy +++ b/core/src/test/groovyx/gaelyk/datastore/EntityTransformationSpec.groovy @@ -451,7 +451,6 @@ class EntityTransformationSpec extends Specification { class Person { @Ignore Order order } - new Person() ''' expect: