Skip to content

Commit

Permalink
added 2 transformers to help representing ObjectId as strings
Browse files Browse the repository at this point in the history
very useful for angularjs that removes all properties whose name start with $, due to issue 1463 angular/angular.js#1463
removed old angularjs workaround
  • Loading branch information
ujibang committed Apr 28, 2015
1 parent 51f3c64 commit 3960a71
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -149,12 +149,14 @@ private static RepresentationTransformer getSingleFromJson(DBObject props) throw

String name = (String) _name;

DBObject args;

if (_args == null || !(_args instanceof DBObject)) {
throw new InvalidMetadataException((_args == null ? "missing '" : "invalid '") + RT_ARGS_ELEMENT_NAME + "' element. it must be an Object");
args = null;
} else {
args = (DBObject) _args;
}

DBObject args = (DBObject) _args;

return new RepresentationTransformer(phase, scope, name, args);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* RESTHeart - the data REST API server
* Copyright (C) SoftInstigate Srl
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.restheart.hal.metadata.singletons;

import com.mongodb.DBObject;
import io.undertow.server.HttpServerExchange;
import org.bson.types.ObjectId;
import org.restheart.handlers.RequestContext;

/**
*
* @author Andrea Di Cesare <andrea@softinstigate.com>
*
* On RESPONSE phase this transformer replaces ObjectId with strings.
*
* <br>Example, document { "_id": {"$oid": "553f59d2e4b041ceaac64e33" }, a: 1 }
* <br>GET -> { "_id": "553f59d2e4b041ceaac64e33", a:1 }
*
*/
public class OidsAsStringsTransformer implements Transformer {
/**
*
* @param exchange
* @param context
* @param contentToTransform
* @param args
*/
@Override
public void tranform(final HttpServerExchange exchange, final RequestContext context, DBObject contentToTransform, final DBObject args) {
_transform(contentToTransform);
}

private void _transform(DBObject data) {
data.keySet().stream().forEach(key -> {
Object value = data.get(key);

if (value instanceof DBObject) {
_transform((DBObject)value);
} else if (value instanceof ObjectId) {
data.put(key, ((ObjectId)value).toString());
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* RESTHeart - the data REST API server
* Copyright (C) SoftInstigate Srl
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.restheart.hal.metadata.singletons;

import com.mongodb.BasicDBList;
import com.mongodb.DBObject;
import io.undertow.server.HttpServerExchange;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Pattern;
import org.bson.types.ObjectId;
import org.restheart.handlers.RequestContext;

/**
*
* @author Andrea Di Cesare <andrea@softinstigate.com>
*
* On REQUEST phase This transformer replaces strings that are valid ObjectIds
* with ObjectIds.
*
* <br>Example:
* <br>POST( { "_id": "553f59d2e4b041ceaac64e33", a:2 } -> { "_id": {"$oid":
* "553f59d2e4b041ceaac64e33" }, a: 1 }
*
*/
public class ValidOidsStringsAsOidsTransformer implements Transformer {
/**
*
* @param exchange
* @param context
* @param contentToTransform
* @param args names of properties to tranform eventually (if value is valid
* ObjectId) as an array of strings (["_id", "prop2"]
*/
@Override
public void tranform(final HttpServerExchange exchange, final RequestContext context, DBObject contentToTransform, final DBObject args) {
// this set contains the names of the properties to transform eventually
Set<String> propertiesToTransform = new HashSet<>();

if (args instanceof BasicDBList) {
BasicDBList _ids = (BasicDBList) args;

_ids.forEach(propertyName -> {
if (propertyName instanceof String) {
propertiesToTransform.add((String) propertyName);

} else {
context.addWarning("element in the args list is not a string: " + propertyName);
}
});

} else {
context.addWarning("transformer wrong definition: args property must be an arrary of string (properties names).");
}

_transform(contentToTransform, propertiesToTransform);
}

private void _transform(DBObject data, Set<String> propertiesNames) {
data.keySet().stream().forEach(key -> {
Object value = data.get(key);

if (shouldTransform(key, propertiesNames)) {
if (value instanceof String && ObjectId.isValid((String) value)) {
data.put(key, new ObjectId((String) value));
}
}

if (value instanceof DBObject) {
_transform((DBObject) value, propertiesNames);
}
});
}

/**
* @param key the name of the property to transform (in case of patch can
* also use the dot notation)
* @param propertiesToTransform the set of properties names to tranform if
* their value is a valid ObjectId
* @return true if the property should be transformed
*/
private boolean shouldTransform(String key, Set<String> propertiesToTransform) {
if (key.contains(".")) {
String keyTokens[] = key.split(Pattern.quote("."));

key = keyTokens[keyTokens.length - 1];
}

return propertiesToTransform.contains(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public void handleRequest(HttpServerExchange exchange, RequestContext context) t
DBObject content;

if (isNotFormData(contentTypes)) { // json or hal+json
final String contentString = workaroundAngularJSIssue1463(ChannelReader.read(exchange.getRequestChannel()));
final String contentString = ChannelReader.read(exchange.getRequestChannel());

try {
content = (DBObject) JSON.parse(contentString);
Expand Down Expand Up @@ -174,28 +174,6 @@ public void handleRequest(HttpServerExchange exchange, RequestContext context) t
getNext().handleRequest(exchange, context);
}

/**
* need this to workaroung angularjs issue
* https://github.com/angular/angular.js/issues/1463
*
* TODO: allow enabling the workaround via configuration option
*
* @param content
* @return
*/
private static String workaroundAngularJSIssue1463(String contentString) {
if (contentString == null)
return null;

String ret = contentString.replaceAll("\"€oid\" *:", "\"\\$oid\" :");

if (!ret.equals(contentString)) {
LOGGER.debug("Replaced €oid alias with $oid in message body. This is to workaround angularjs issue 1463 (https://github.com/angular/angular.js/issues/1463)");
}

return ret;
}

/**
*
* @param contentTypes
Expand Down

0 comments on commit 3960a71

Please sign in to comment.