/*
* RESTHeart - the Web API for MongoDB
* 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.handlers.schema;
import com.google.common.collect.Lists;
import io.undertow.server.HttpServerExchange;
import java.util.List;
import org.bson.BsonDocument;
import org.bson.BsonObjectId;
import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.types.ObjectId;
import org.restheart.metadata.transformers.Transformer;
import org.restheart.handlers.RequestContext;
import org.restheart.utils.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author Andrea Di Cesare {@literal <andrea@softinstigate.com>}
*/
public class JsonSchemaTransformer implements Transformer {
static final Logger LOGGER = LoggerFactory
.getLogger(JsonSchemaTransformer.class);
private static final BsonString $SCHEMA
= new BsonString("http://json-schema.org/draft-04/schema#");
@Override
public void transform(HttpServerExchange exchange,
RequestContext context,
final BsonValue contentToTransform,
BsonValue args) {
BsonDocument _contentToTransform
= contentToTransform == null
? null
: contentToTransform.asDocument();
if (context.isSchema()) {
if (context.isGet()) {
unescapeSchema(_contentToTransform);
} else if (context.isPut() || context.isPatch()) {
BsonDocument content;
if (context.getContent() != null) {
content = context.getContent().asDocument();
} else {
content = new BsonDocument();
}
// generate id as specs mandates
SchemaStoreURL uri = new SchemaStoreURL(
context.getDBName(),
context.getDocumentId());
content.put("id", new BsonString(uri.toString()));
// escape all $ prefixed keys
escapeSchema(_contentToTransform);
// add (overwrite) $schema field
_contentToTransform.put("_$schema", $SCHEMA);
}
} else if (context.getType() == RequestContext.TYPE.SCHEMA_STORE) {
if (context.getMethod() == RequestContext.METHOD.POST) {
BsonDocument content;
if (context.getContent() != null) {
content = context.getContent().asDocument();
} else {
content = new BsonDocument();
}
// generate id as specs mandates
BsonValue schemaId;
if (!content.containsKey("_id")) {
schemaId = new BsonObjectId(new ObjectId());
content.put(
"id",
schemaId);
} else {
schemaId = content.get("_id");
}
SchemaStoreURL uri = new SchemaStoreURL(
context.getDBName(),
schemaId);
content.put("id", new BsonString(uri.toString()));
// escape all $ prefixed keys
escapeSchema(_contentToTransform);
// add (overwrite) $schema field
_contentToTransform.put("_$schema", $SCHEMA);
} else if (context.isGet()) {
// apply transformation on embedded schemas
if (_contentToTransform.containsKey("_embedded")) {
BsonValue _embedded = _contentToTransform
.get("_embedded");
if (_embedded.isDocument()
&& _embedded.asDocument()
.containsKey("rh:schema")) {
// execute the logic on children documents
BsonValue docs = _embedded
.asDocument()
.get("rh:schema");
if (docs.isArray()) {
docs.asArray().stream()
.filter(doc -> {
return doc.isDocument();
})
.forEach((doc) -> {
unescapeSchema(doc.asDocument());
});
}
}
}
}
}
}
public static void escapeSchema(BsonDocument schema) {
BsonValue escaped = JsonUtils.escapeKeys(schema, false);
if (escaped.isDocument()) {
List<String> keys = Lists.newArrayList(schema.keySet().iterator());
keys.stream().forEach(f -> schema.remove(f));
schema.putAll(escaped.asDocument());
}
}
public static void unescapeSchema(BsonDocument schema) {
BsonValue unescaped = JsonUtils.unescapeKeys(schema);
if (unescaped != null && unescaped.isDocument()) {
List<String> keys = Lists.newArrayList(schema.keySet().iterator());
keys.stream().forEach(f -> schema.remove(f));
schema.putAll(unescaped.asDocument());
}
}
}