/* * 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.document; import static com.mongodb.client.model.Filters.and; import static com.mongodb.client.model.Filters.eq; import io.undertow.server.HttpServerExchange; import java.util.Deque; import org.bson.BsonDocument; import org.bson.BsonObjectId; import org.bson.conversions.Bson; import org.restheart.hal.Representation; import org.restheart.handlers.PipedHttpHandler; import org.restheart.handlers.RequestContext; import org.restheart.utils.HttpStatus; import org.restheart.utils.RequestHelper; import org.restheart.utils.ResponseHelper; import org.restheart.utils.URLUtils; import org.restheart.utils.JsonUtils; import org.restheart.handlers.RequestContext.TYPE; import java.util.HashSet; /** * * @author Andrea Di Cesare {@literal <andrea@softinstigate.com>} */ public class GetDocumentHandler extends PipedHttpHandler { /** * Default ctor */ public GetDocumentHandler() { super(); } /** * Default ctor * * @param next */ public GetDocumentHandler(PipedHttpHandler next) { super(next); } /** * * @param exchange * @param context * @throws Exception */ @Override public void handleRequest( HttpServerExchange exchange, RequestContext context) throws Exception { if (context.isInError()) { next(exchange, context); return; } Bson query = eq("_id", context.getDocumentId()); HashSet<Bson> terms = new HashSet<>(); if (context.getShardKey() != null) { terms.add(context.getShardKey()); } // filters are applied to GET /db/coll/docid as well // to make easy implementing filter based access restrictions // for instance a Trasnformer can add a filter to limit access to data // on the basis of the user role if (context.getFiltersDocument() != null) { terms.add(context.getFiltersDocument()); } if (terms.size() > 0) { terms.add(query); query = and(terms); } final BsonDocument fieldsToReturn = new BsonDocument(); Deque<String> keys = context.getKeys(); if (keys != null) { keys.stream().forEach((String f) -> { BsonDocument keyQuery = BsonDocument.parse(f); fieldsToReturn.putAll(keyQuery); // this can throw JSONParseException for invalid filter parameters }); } BsonDocument document = getDatabase().getCollection( context.getDBName(), context.getCollectionName()) .find(query) .projection(fieldsToReturn) .first(); if (document == null) { String errMsg = context.getDocumentId() == null ? " does not exist" : " ".concat(JsonUtils.getIdAsString( context.getDocumentId(), true)) .concat(" does not exist"); if (null != context.getType()) { switch (context.getType()) { case DOCUMENT: errMsg = "document".concat(errMsg); break; case FILE: errMsg = "file".concat(errMsg); break; case SCHEMA: errMsg = "schema".concat(errMsg); break; default: errMsg = "resource".concat(errMsg); break; } } ResponseHelper.endExchangeWithMessage( exchange, context, HttpStatus.SC_NOT_FOUND, errMsg); next(exchange, context); return; } Object etag; if (context.getType() == TYPE.FILE) { if (document.containsKey("metadata") && document.get("metadata").isDocument()) { etag = document.get("metadata").asDocument().get(("_etag")); } else if (document.containsKey("_etag")) { // backward compatibility. until version 2.0.x, _etag was not // in the metadata sub-document etag = document.get("_etag"); } else { etag = null; } } else { etag = document.get("_etag"); } // in case the request contains the IF_NONE_MATCH header with the current etag value, // just return 304 NOT_MODIFIED code if (RequestHelper.checkReadEtag(exchange, (BsonObjectId) etag)) { context.setResponseStatusCode(HttpStatus.SC_NOT_MODIFIED); next(exchange, context); return; } String requestPath = URLUtils.removeTrailingSlashes( exchange.getRequestPath()); context.setResponseContent(new DocumentRepresentationFactory() .getRepresentation( requestPath, exchange, context, document) .asBsonDocument()); context.setResponseContentType(Representation.HAL_JSON_MEDIA_TYPE); context.setResponseStatusCode(HttpStatus.SC_OK); ResponseHelper.injectEtagHeader(exchange, etag); next(exchange, context); } }