/* * 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; import io.undertow.security.idm.Account; import org.restheart.db.CursorPool.EAGER_CURSOR_ALLOCATION_POLICY; import org.restheart.utils.URLUtils; import io.undertow.server.HttpServerExchange; import io.undertow.util.HeaderValues; import io.undertow.util.Headers; import io.undertow.util.HttpString; import io.undertow.util.Methods; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.List; import org.bson.BsonArray; import org.bson.BsonDocument; import org.bson.BsonInt32; import org.bson.BsonValue; import org.bson.json.JsonParseException; import org.restheart.Bootstrapper; import org.restheart.db.OperationResult; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * * @author Andrea Di Cesare {@literal <andrea@softinstigate.com>} */ public class RequestContext { private static final Logger LOGGER = LoggerFactory.getLogger(RequestContext.class); public enum TYPE { INVALID, ROOT, DB, COLLECTION, DOCUMENT, COLLECTION_INDEXES, INDEX, FILES_BUCKET, FILE, FILE_BINARY, AGGREGATION, SCHEMA, SCHEMA_STORE, BULK_DOCUMENTS }; public enum METHOD { GET, POST, PUT, DELETE, PATCH, OPTIONS, OTHER }; public enum DOC_ID_TYPE { OID, // ObjectId STRING_OID, // String eventually converted to ObjectId in case ObjectId.isValid() is true STRING, // String NUMBER, // any Number (including mongodb NumberLong) DATE, // Date MINKEY, // org.bson.types.MinKey; MAXKEY, // org.bson.types.MaxKey NULL, // null BOOLEAN // boolean } public enum REPRESENTATION_FORMAT { PLAIN_JSON, // Plain Json PJ, // Alias for plain json HAL // Hypertext Application Language } public enum HAL_MODE { FULL, // full mode F, // alias for full COMPACT, // new compact mode C // alias for compact } public enum ETAG_CHECK_POLICY { REQUIRED, // always requires the etag, return PRECONDITION FAILED if missing REQUIRED_FOR_DELETE, // only requires the etag for DELETE, return PRECONDITION FAILED if missing OPTIONAL // checks the etag only if provided by client via If-Match header } // query parameters public static final String PAGE_QPARAM_KEY = "page"; public static final String PAGESIZE_QPARAM_KEY = "pagesize"; public static final String COUNT_QPARAM_KEY = "count"; public static final String SORT_BY_QPARAM_KEY = "sort_by"; public static final String SORT_QPARAM_KEY = "sort"; public static final String FILTER_QPARAM_KEY = "filter"; public static final String AGGREGATION_VARIABLES_QPARAM_KEY = "avars"; public static final String KEYS_QPARAM_KEY = "keys"; public static final String EAGER_CURSOR_ALLOCATION_POLICY_QPARAM_KEY = "eager"; public static final String HAL_QPARAM_KEY = "hal"; public static final String DOC_ID_TYPE_QPARAM_KEY = "id_type"; public static final String ETAG_CHECK_QPARAM_KEY = "checkEtag"; public static final String SHARDKEY_QPARAM_KEY = "shardkey"; public static final String NO_PROPS_KEY = "np"; public static final String REPRESENTATION_FORMAT_KEY = "rep"; // matadata public static final String ETAG_DOC_POLICY_METADATA_KEY = "etagDocPolicy"; public static final String ETAG_POLICY_METADATA_KEY = "etagPolicy"; // special resource names public static final String SYSTEM = "system."; public static final String LOCAL = "local"; public static final String ADMIN = "admin"; public static final String FS_CHUNKS_SUFFIX = ".chunks"; public static final String FS_FILES_SUFFIX = ".files"; public static final String RESOURCES_WILDCARD_KEY = "*"; public static final String _INDEXES = "_indexes"; public static final String _SCHEMAS = "_schemas"; public static final String _AGGREGATIONS = "_aggrs"; public static final String BINARY_CONTENT = "binary"; public static final String MAX_KEY_ID = "_MaxKey"; public static final String MIN_KEY_ID = "_MinKey"; public static final String NULL_KEY_ID = "_null"; public static final String TRUE_KEY_ID = "_true"; public static final String FALSE_KEY_ID = "_false"; // other constants public static final String SLASH = "/"; public static final String PATCH = "PATCH"; public static final String UNDERSCORE = "_"; private final String whereUri; private final String whatUri; private final TYPE type; private final METHOD method; private final String[] pathTokens; private BsonDocument dbProps; private BsonDocument collectionProps; private BsonValue content; private String rawContent; private Path filePath; private BsonValue responseContent; private int responseStatusCode; private String responseContentType; private final List<String> warnings = new ArrayList<>(); private int page = 1; private int pagesize = 100; private boolean count = false; private EAGER_CURSOR_ALLOCATION_POLICY cursorAllocationPolicy; private Deque<String> filter = null; private BsonDocument aggregationVars = null; // aggregation vars private Deque<String> keys = null; private Deque<String> sortBy = null; private DOC_ID_TYPE docIdType = DOC_ID_TYPE.STRING_OID; private REPRESENTATION_FORMAT representationFormat; private BsonValue documentId; private String mappedUri = null; private String unmappedUri = null; private static final String NUL = Character.toString('\0'); private final String etag; private boolean forceEtagCheck = false; private OperationResult dbOperationResult; private BsonDocument shardKey = null; private boolean noProps = false; private boolean inError = false; private Account authenticatedAccount = null; /** * the HAL mode */ private HAL_MODE halMode = HAL_MODE.FULL; /** * * @param exchange the url rewriting feature is implemented by the whatUri * and whereUri parameters. * * the exchange request path (mapped uri) is rewritten replacing the * whereUri string with the whatUri string the special whatUri value * means * any resource: the whereUri is replaced with / * * example 1 * * whatUri = /mydb/mycollection whereUri = / * * then the requestPath / is rewritten to /mydb/mycollection * * example 2 * * whatUri = * whereUri = /data * * then the requestPath /data is rewritten to / * * @param whereUri the uri to map to * @param whatUri the uri to map */ public RequestContext( HttpServerExchange exchange, String whereUri, String whatUri) { this.whereUri = URLUtils.removeTrailingSlashes(whereUri == null ? null : whereUri.startsWith("/") ? whereUri : "/" + whereUri); this.whatUri = URLUtils.removeTrailingSlashes( whatUri == null ? null : whatUri.startsWith("/") || "*".equals(whatUri) ? whatUri : "/" + whatUri); this.mappedUri = exchange.getRequestPath(); this.unmappedUri = unmapUri(exchange.getRequestPath()); // "/db/collection/document" --> { "", "mappedDbName", "collection", "document" } this.pathTokens = this.unmappedUri.split(SLASH); this.type = selectRequestType(pathTokens); this.method = selectRequestMethod(exchange.getRequestMethod()); // etag HeaderValues etagHvs = exchange.getRequestHeaders() == null ? null : exchange.getRequestHeaders().get(Headers.IF_MATCH); this.etag = etagHvs == null || etagHvs.getFirst() == null ? null : etagHvs.getFirst(); this.forceEtagCheck = exchange .getQueryParameters() .get(ETAG_CHECK_QPARAM_KEY) != null; this.noProps = exchange.getQueryParameters().get(NO_PROPS_KEY) != null; } protected static METHOD selectRequestMethod(HttpString _method) { METHOD method; if (Methods.GET.equals(_method)) { method = METHOD.GET; } else if (Methods.POST.equals(_method)) { method = METHOD.POST; } else if (Methods.PUT.equals(_method)) { method = METHOD.PUT; } else if (Methods.DELETE.equals(_method)) { method = METHOD.DELETE; } else if (PATCH.equals(_method.toString())) { method = METHOD.PATCH; } else if (Methods.OPTIONS.equals(_method)) { method = METHOD.OPTIONS; } else { method = METHOD.OTHER; } return method; } protected static TYPE selectRequestType(String[] pathTokens) { TYPE type; if (pathTokens.length < 2) { type = TYPE.ROOT; } else if (pathTokens.length < 3) { type = TYPE.DB; } else if (pathTokens.length >= 3 && pathTokens[2].endsWith(FS_FILES_SUFFIX)) { if (pathTokens.length == 3) { type = TYPE.FILES_BUCKET; } else if (pathTokens.length == 4 && pathTokens[3].equalsIgnoreCase(_INDEXES)) { type = TYPE.COLLECTION_INDEXES; } else if (pathTokens.length == 4 && !pathTokens[3].equalsIgnoreCase(_INDEXES) && !pathTokens[3].equals(RESOURCES_WILDCARD_KEY)) { type = TYPE.FILE; } else if (pathTokens.length > 4 && pathTokens[3].equalsIgnoreCase(_INDEXES)) { type = TYPE.INDEX; } else if (pathTokens.length > 4 && !pathTokens[3].equalsIgnoreCase(_INDEXES) && !pathTokens[4].equalsIgnoreCase(BINARY_CONTENT)) { type = TYPE.FILE; } else if (pathTokens.length == 5 && pathTokens[4].equalsIgnoreCase(BINARY_CONTENT)) { // URL: <host>/db/bucket.filePath/xxx/binary type = TYPE.FILE_BINARY; } else { type = TYPE.DOCUMENT; } } else if (pathTokens.length >= 3 && pathTokens[2].endsWith(_SCHEMAS)) { if (pathTokens.length == 3) { type = TYPE.SCHEMA_STORE; } else { type = TYPE.SCHEMA; } } else if (pathTokens.length < 4) { type = TYPE.COLLECTION; } else if (pathTokens.length == 4 && pathTokens[3].equalsIgnoreCase(_INDEXES)) { type = TYPE.COLLECTION_INDEXES; } else if (pathTokens.length == 4 && pathTokens[3].equals(RESOURCES_WILDCARD_KEY)) { type = TYPE.BULK_DOCUMENTS; } else if (pathTokens.length > 4 && pathTokens[3].equalsIgnoreCase(_INDEXES)) { type = TYPE.INDEX; } else if (pathTokens.length == 4 && pathTokens[3].equalsIgnoreCase(_AGGREGATIONS)) { type = TYPE.INVALID; } else if (pathTokens.length > 4 && pathTokens[3].equalsIgnoreCase(_AGGREGATIONS)) { type = TYPE.AGGREGATION; } else { type = TYPE.DOCUMENT; } return type; } /** * given a mapped uri (/some/mapping/coll) returns the canonical uri * (/db/coll) URLs are mapped to mongodb resources by using the mongo-mounts * configuration properties * * @param mappedUri * @return */ public final String unmapUri(String mappedUri) { String ret = URLUtils.removeTrailingSlashes(mappedUri); if (whatUri.equals("*")) { if (!this.whereUri.equals(SLASH)) { ret = ret.replaceFirst("^" + this.whereUri, ""); } } else if (!this.whereUri.equals(SLASH)) { ret = URLUtils.removeTrailingSlashes( ret.replaceFirst("^" + this.whereUri, this.whatUri)); } else { ret = URLUtils.removeTrailingSlashes( URLUtils.removeTrailingSlashes(this.whatUri) + ret); } if (ret.isEmpty()) { ret = SLASH; } return ret; } /** * given a canonical uri (/db/coll) returns the mapped uri * (/some/mapping/uri) relative to this context. URLs are mapped to mongodb * resources via the mongo-mounts configuration properties * * @param unmappedUri * @return */ public final String mapUri(String unmappedUri) { String ret = URLUtils.removeTrailingSlashes(unmappedUri); if (whatUri.equals("*")) { if (!this.whereUri.equals(SLASH)) { return this.whereUri + unmappedUri; } } else { ret = URLUtils.removeTrailingSlashes( ret.replaceFirst("^" + this.whatUri, this.whereUri)); } if (ret.isEmpty()) { ret = SLASH; } return ret; } /** * check if the parent of the requested resource is accessible in this * request context * * for instance if /mydb/mycollection is mapped to /coll then: * * the db is accessible from the collection the root is not accessible from * the collection (since / is actually mapped to the db) * * @return true if parent of the requested resource is accessible */ public final boolean isParentAccessible() { return type == TYPE.DB ? mappedUri.split(SLASH).length > 1 : mappedUri.split(SLASH).length > 2; } /** * * @return type */ public TYPE getType() { return type; } /** * * @return DB Name */ public String getDBName() { return getPathTokenAt(1); } /** * * @return collection name */ public String getCollectionName() { return getPathTokenAt(2); } /** * * @return document id */ public String getDocumentIdRaw() { return getPathTokenAt(3); } /** * * @return index id */ public String getIndexId() { return getPathTokenAt(4); } /** * * @return collection name */ public String getAggregationOperation() { return getPathTokenAt(4); } /** * * @return URI * @throws URISyntaxException */ public URI getUri() throws URISyntaxException { return new URI(Arrays.asList(pathTokens) .stream() .reduce(SLASH, (t1, t2) -> t1 + SLASH + t2)); } /** * * @return method */ public METHOD getMethod() { return method; } /** * * @param dbName * @return true if the dbName is a reserved resource */ public static boolean isReservedResourceDb(String dbName) { return dbName.equals(ADMIN) || dbName.equals(LOCAL) || dbName.startsWith(SYSTEM) || dbName.startsWith(UNDERSCORE) || dbName.equals(RESOURCES_WILDCARD_KEY); } /** * * @param collectionName * @return true if the collectionName is a reserved resource */ public static boolean isReservedResourceCollection(String collectionName) { return collectionName != null && !collectionName.equalsIgnoreCase(_SCHEMAS) && (collectionName.startsWith(SYSTEM) || collectionName.startsWith(UNDERSCORE) || collectionName.endsWith(FS_CHUNKS_SUFFIX) || collectionName.equals(RESOURCES_WILDCARD_KEY)); } /** * * @param type * @param documentIdRaw * @return true if the documentIdRaw is a reserved resource */ public static boolean isReservedResourceDocument( TYPE type, String documentIdRaw) { if (documentIdRaw == null) { return false; } return (documentIdRaw.startsWith(UNDERSCORE) || (type != TYPE.AGGREGATION && _AGGREGATIONS.equalsIgnoreCase(documentIdRaw))) && !documentIdRaw.equalsIgnoreCase(_INDEXES) && !documentIdRaw.equalsIgnoreCase(MIN_KEY_ID) && !documentIdRaw.equalsIgnoreCase(MAX_KEY_ID) && !documentIdRaw.equalsIgnoreCase(NULL_KEY_ID) && !documentIdRaw.equalsIgnoreCase(TRUE_KEY_ID) && !documentIdRaw.equalsIgnoreCase(FALSE_KEY_ID) && !(type == TYPE.AGGREGATION) || (documentIdRaw.equals(RESOURCES_WILDCARD_KEY) && !(type == TYPE.BULK_DOCUMENTS)); } /** * * @return isReservedResource */ public boolean isReservedResource() { if (type == TYPE.ROOT) { return false; } return isReservedResourceDb(getDBName()) || isReservedResourceCollection(getCollectionName()) || isReservedResourceDocument(type, getDocumentIdRaw()); } /** * @return the whereUri */ public String getUriPrefix() { return whereUri; } /** * @return the whatUri */ public String getMappingUri() { return whatUri; } /** * @return the page */ public int getPage() { return page; } /** * @param page the page to set */ public void setPage(int page) { this.page = page; } /** * @return the pagesize */ public int getPagesize() { return pagesize; } /** * @param pagesize the pagesize to set */ public void setPagesize(int pagesize) { this.pagesize = pagesize; } /** * @return the representationFormat */ public REPRESENTATION_FORMAT getRepresentationFormat() { return representationFormat; } /** * sets representationFormat */ public void setRepresentationFormat( REPRESENTATION_FORMAT representationFormat) { this.representationFormat = representationFormat; } /** * @return the count */ public boolean isCount() { return count; } /** * @param count the count to set */ public void setCount(boolean count) { this.count = count; } /** * @return the filter */ public Deque<String> getFilter() { return filter; } /** * @param filter the filter to set */ public void setFilter(Deque<String> filter) { this.filter = filter; } /** * * @return the $and composed filter qparam values */ public BsonDocument getFiltersDocument() throws JsonParseException { final BsonDocument filterQuery = new BsonDocument(); if (filter != null) { if (filter.size() > 1) { BsonArray _filters = new BsonArray(); filter.stream().forEach((String f) -> { _filters.add(BsonDocument.parse(f)); }); filterQuery.put("$and", _filters); } else if (filter.size() == 1) { filterQuery.putAll(BsonDocument.parse(filter.getFirst())); // this can throw JsonParseException for invalid filter parameters } else { return null; } } return filterQuery; } public BsonDocument getSortByDocument() throws JsonParseException { BsonDocument sort = new BsonDocument(); if (sortBy == null) { sort.put("_id", new BsonInt32(-1)); } else { sortBy.stream().forEach((s) -> { String _s = s.trim(); // the + sign is decoded into a space, in case remove it // manage the case where sort_by is a json object try { BsonDocument _sort = BsonDocument.parse(_s); sort.putAll(_sort); } catch (JsonParseException e) { // sort_by is just a string, i.e. a property name if (_s.startsWith("-")) { sort.put(_s.substring(1), new BsonInt32(-1)); } else if (_s.startsWith("+")) { sort.put(_s.substring(1), new BsonInt32(11)); } else { sort.put(_s, new BsonInt32(1)); } } }); } return sort; } public BsonDocument getProjectionDocument() throws JsonParseException { final BsonDocument projection = new BsonDocument(); if (keys == null || keys.isEmpty()) { return null; } else { keys.stream().forEach((String f) -> { projection.putAll(BsonDocument.parse(f)); // this can throw JsonParseException for invalid keys parameters }); } return projection; } /** * @return the aggregationVars */ public BsonDocument getAggreationVars() { return aggregationVars; } /** * @param aggregationVars the aggregationVars to set */ public void setAggregationVars(BsonDocument aggregationVars) { this.aggregationVars = aggregationVars; } /** * @return the sortBy */ public Deque<String> getSortBy() { return sortBy; } /** * @param sortBy the sortBy to set */ public void setSortBy(Deque<String> sortBy) { this.sortBy = sortBy; } /** * @return the collectionProps */ public BsonDocument getCollectionProps() { return collectionProps; } /** * @param collectionProps the collectionProps to set */ public void setCollectionProps(BsonDocument collectionProps) { this.collectionProps = collectionProps; } /** * @return the dbProps */ public BsonDocument getDbProps() { return dbProps; } /** * @param dbProps the dbProps to set */ public void setDbProps(BsonDocument dbProps) { this.dbProps = dbProps; } /** * @return the content */ public BsonValue getContent() { return content; } /** * @param content the content to set */ public void setContent(BsonValue content) { if (content != null && !(content.isDocument() || content.isArray())) { throw new IllegalArgumentException("content must be " + "either an object or an array"); } this.content = content; } /** * @return the rawContent */ public String getRawContent() { return rawContent; } /** * @param rawContent the rawContent to set */ public void setRawContent(String rawContent) { this.rawContent = rawContent; } /** * @return the warnings */ public List<String> getWarnings() { return warnings; } /** * @param warning */ public void addWarning(String warning) { warnings.add(warning); } /** * * The unmapped uri is the cononical uri of a mongodb resource (e.g. * /db/coll). * * @return the unmappedUri */ public String getUnmappedRequestUri() { return unmappedUri; } /** * The mapped uri is the exchange request uri. This is "mapped" by the * mongo-mounts mapping paramenters. * * @return the mappedUri */ public String getMappedRequestUri() { return mappedUri; } /** * * @param index * @return pathTokens[index] if pathTokens.length > index, else null */ private String getPathTokenAt(int index) { return pathTokens.length > index ? pathTokens[index] : null; } /** * * @return the cursorAllocationPolicy */ public EAGER_CURSOR_ALLOCATION_POLICY getCursorAllocationPolicy() { return cursorAllocationPolicy; } /** * @param cursorAllocationPolicy the cursorAllocationPolicy to set */ public void setCursorAllocationPolicy( EAGER_CURSOR_ALLOCATION_POLICY cursorAllocationPolicy) { this.cursorAllocationPolicy = cursorAllocationPolicy; } /** * @return the docIdType */ public DOC_ID_TYPE getDocIdType() { return docIdType; } /** * @param docIdType the docIdType to set */ public void setDocIdType(DOC_ID_TYPE docIdType) { this.docIdType = docIdType; } /** * @param documentId the documentId to set */ public void setDocumentId(BsonValue documentId) { this.documentId = documentId; } /** * @return the documentId */ public BsonValue getDocumentId() { return documentId; } /** * @return the responseContent */ public BsonValue getResponseContent() { return responseContent; } /** * @param responseContent the responseContent to set */ public void setResponseContent(BsonValue responseContent) { if (responseContent != null && !(responseContent.isDocument() || responseContent.isArray())) { throw new IllegalArgumentException("response content must be " + "either an object or an array"); } this.responseContent = responseContent; } /** * @return the responseStatusCode */ public int getResponseStatusCode() { return responseStatusCode; } /** * @param responseStatusCode the responseStatusCode to set */ public void setResponseStatusCode(int responseStatusCode) { this.responseStatusCode = responseStatusCode; } /** * @return the responseContentType */ public String getResponseContentType() { return responseContentType; } /** * @param responseContentType the responseContentType to set */ public void setResponseContentType(String responseContentType) { this.responseContentType = responseContentType; } /** * @return the filePath */ public Path getFilePath() { return filePath; } /** * @param filePath the filePath to set */ public void setFilePath(Path filePath) { this.filePath = filePath; } /** * @return keys */ public Deque<String> getKeys() { return keys; } /** * @param keys keys to set */ public void setKeys(Deque<String> keys) { this.keys = keys; } /** * @return the halMode */ public HAL_MODE getHalMode() { return halMode; } public boolean isFullHalMode() { return halMode == HAL_MODE.FULL || halMode == HAL_MODE.F; } /** * @param halMode the halMode to set */ public void setHalMode(HAL_MODE halMode) { this.halMode = halMode; } /** * @see https://docs.mongodb.org/v3.2/reference/limits/#naming-restrictions * @return */ public boolean isDbNameInvalid() { return isDbNameInvalid(getDBName()); } /** * @param dbName * @see https://docs.mongodb.org/v3.2/reference/limits/#naming-restrictions * @return */ public boolean isDbNameInvalid(String dbName) { return (dbName == null || dbName.contains(NUL) || dbName.contains(" ") || dbName.contains("/") || dbName.contains("\\") || dbName.contains(".") || dbName.contains("\"") || dbName.contains("$") || dbName.length() > 64 || dbName.length() == 0); } /** * @see https://docs.mongodb.org/v3.2/reference/limits/#naming-restrictions * @return */ public boolean isDbNameInvalidOnWindows() { return isDbNameInvalidOnWindows(getDBName()); } /** * @param dbName * @see https://docs.mongodb.org/v3.2/reference/limits/#naming-restrictions * @return */ public boolean isDbNameInvalidOnWindows(String dbName) { return (isDbNameInvalid() || dbName.contains("*") || dbName.contains("<") || dbName.contains(">") || dbName.contains(":") || dbName.contains(".") || dbName.contains("|") || dbName.contains("?")); } /** * @see https://docs.mongodb.org/v3.2/reference/limits/#naming-restrictions * @return */ public boolean isCollectionNameInvalid() { return isCollectionNameInvalid(getCollectionName()); } /** * @param collectionName * @see https://docs.mongodb.org/v3.2/reference/limits/#naming-restrictions * @return */ public boolean isCollectionNameInvalid(String collectionName) { // collection starting with system. will return FORBIDDEN return (collectionName == null || collectionName.contains(NUL) || collectionName.contains("$") || collectionName.length() == 64); } public String getETag() { return etag; } public boolean isETagCheckRequired() { // if client specifies the If-Match header, than check it if (getETag() != null) { return true; } // if client requires the check via qparam return true if (forceEtagCheck) { return true; } // for documents consider db and coll etagDocPolicy metadata if (type == TYPE.DOCUMENT || type == TYPE.FILE) { // check the coll metadata BsonValue _policy = collectionProps != null ? collectionProps.get(ETAG_DOC_POLICY_METADATA_KEY) : null; LOGGER.trace( "collection etag policy (from coll properties) {}", _policy); if (_policy == null) { // check the db metadata _policy = dbProps != null ? dbProps.get(ETAG_DOC_POLICY_METADATA_KEY) : null; LOGGER.trace( "collection etag policy (from db properties) {}", _policy); } ETAG_CHECK_POLICY policy = null; if (_policy != null && _policy.isString()) { try { policy = ETAG_CHECK_POLICY .valueOf(_policy.asString().getValue() .toUpperCase()); } catch (IllegalArgumentException iae) { policy = null; } } if (null != policy) { if (method == METHOD.DELETE) { return policy != ETAG_CHECK_POLICY.OPTIONAL; } else { return policy == ETAG_CHECK_POLICY.REQUIRED; } } } // for db consider db etagPolicy metadata if (type == TYPE.DB && dbProps != null) { // check the coll metadata BsonValue _policy = dbProps.get(ETAG_POLICY_METADATA_KEY); LOGGER.trace("db etag policy (from db properties) {}", _policy); ETAG_CHECK_POLICY policy = null; if (_policy != null && _policy.isString()) { try { policy = ETAG_CHECK_POLICY.valueOf( _policy.asString().getValue() .toUpperCase()); } catch (IllegalArgumentException iae) { policy = null; } } if (null != policy) { if (method == METHOD.DELETE) { return policy != ETAG_CHECK_POLICY.OPTIONAL; } else { return policy == ETAG_CHECK_POLICY.REQUIRED; } } } // for collection consider coll and db etagPolicy metadata if (type == TYPE.COLLECTION && collectionProps != null) { // check the coll metadata BsonValue _policy = collectionProps.get(ETAG_POLICY_METADATA_KEY); LOGGER.trace( "coll etag policy (from coll properties) {}", _policy); if (_policy == null) { // check the db metadata _policy = dbProps != null ? dbProps.get(ETAG_POLICY_METADATA_KEY) : null; LOGGER.trace( "coll etag policy (from db properties) {}", _policy); } ETAG_CHECK_POLICY policy = null; if (_policy != null && _policy.isString()) { try { policy = ETAG_CHECK_POLICY.valueOf( _policy.asString().getValue() .toUpperCase()); } catch (IllegalArgumentException iae) { policy = null; } } if (null != policy) { if (method == METHOD.DELETE) { return policy != ETAG_CHECK_POLICY.OPTIONAL; } else { return policy == ETAG_CHECK_POLICY.REQUIRED; } } } // apply the default policy from configuration ETAG_CHECK_POLICY dbP = Bootstrapper.getConfiguration() .getDbEtagCheckPolicy(); ETAG_CHECK_POLICY collP = Bootstrapper.getConfiguration() .getCollEtagCheckPolicy(); ETAG_CHECK_POLICY docP = Bootstrapper.getConfiguration() .getDocEtagCheckPolicy(); if (LOGGER.isTraceEnabled()) { LOGGER.trace("default etag db check (from conf) {}", dbP); LOGGER.trace("default etag coll check (from conf) {}", collP); LOGGER.trace("default etag doc check (from conf) {}", docP); } ETAG_CHECK_POLICY policy = null; if (null != type) { switch (type) { case DB: policy = dbP; break; case COLLECTION: case FILES_BUCKET: case SCHEMA_STORE: policy = collP; break; default: policy = docP; } } if (null != policy) { if (method == METHOD.DELETE) { return policy != ETAG_CHECK_POLICY.OPTIONAL; } else { return policy == ETAG_CHECK_POLICY.REQUIRED; } } return false; } /** * @return the dbOperationResult */ public OperationResult getDbOperationResult() { return dbOperationResult; } /** * @param dbOperationResult the dbOperationResult to set */ public void setDbOperationResult(OperationResult dbOperationResult) { this.dbOperationResult = dbOperationResult; } /** * @return the shardKey */ public BsonDocument getShardKey() { return shardKey; } /** * @param shardKey the shardKey to set */ public void setShardKey(BsonDocument shardKey) { this.shardKey = shardKey; } /** * @return the noProps */ public boolean isNoProps() { return noProps; } /** * @return the inError */ public boolean isInError() { return inError; } /** * @param inError the inError to set */ public void setInError(boolean inError) { this.inError = inError; } /** * @return the authenticatedAccount */ public Account getAuthenticatedAccount() { return authenticatedAccount; } /** * @param authenticatedAccount the authenticatedAccount to set */ public void setAuthenticatedAccount(Account authenticatedAccount) { this.authenticatedAccount = authenticatedAccount; } /** * helper method to check request resource type * * @return true if type is TYPE.AGGREGATION */ public boolean isAggregation() { return this.type == TYPE.AGGREGATION; } /** * helper method to check request resource type * * @return true if type is TYPE.BULK_DOCUMENTS */ public boolean isBulkDocuments() { return this.type == TYPE.BULK_DOCUMENTS; } /** * helper method to check request resource type * * @return true if type is TYPE.COLLECTION */ public boolean isCollection() { return this.type == TYPE.COLLECTION; } /** * helper method to check request resource type * * @return true if type is TYPE.COLLECTION_INDEXES */ public boolean isCollectionIndexes() { return this.type == TYPE.COLLECTION_INDEXES; } /** * helper method to check request resource type * * @return true if type is TYPE.DB */ public boolean isDb() { return this.type == TYPE.DB; } /** * helper method to check request resource type * * @return true if type is TYPE.DOCUMENT */ public boolean isDocument() { return this.type == TYPE.DOCUMENT; } /** * helper method to check request resource type * * @return true if type is TYPE.FILE */ public boolean isFile() { return this.type == TYPE.FILE; } /** * helper method to check request resource type * * @return true if type is TYPE.FILES_BUCKET */ public boolean isFilesBucket() { return this.type == TYPE.FILES_BUCKET; } /** * helper method to check request resource type * * @return true if type is TYPE.FILE_BINARY */ public boolean isFileBinary() { return this.type == TYPE.FILE_BINARY; } /** * helper method to check request resource type * * @return true if type is TYPE.INDEX */ public boolean isIndex() { return this.type == TYPE.INDEX; } /** * helper method to check request resource type * * @return true if type is TYPE.ROOT */ public boolean isRoot() { return this.type == TYPE.ROOT; } /** * helper method to check request resource type * * @return true if type is TYPE.SCHEMA */ public boolean isSchema() { return this.type == TYPE.SCHEMA; } /** * helper method to check request resource type * * @return true if type is TYPE.SCHEMA_STORE */ public boolean isSchemaStore() { return this.type == TYPE.SCHEMA_STORE; } /** * helper method to check request method * * @return true if method is METHOD.DELETE */ public boolean isDelete() { return this.method == METHOD.DELETE; } /** * helper method to check request method * * @return true if method is METHOD.GET */ public boolean isGet() { return this.method == METHOD.GET; } /** * helper method to check request method * * @return true if method is METHOD.OPTIONS */ public boolean isOptions() { return this.method == METHOD.OPTIONS; } /** * helper method to check request method * * @return true if method is METHOD.PATCH */ public boolean isPatch() { return this.method == METHOD.PATCH; } /** * helper method to check request method * * @return true if method is METHOD.POST */ public boolean isPost() { return this.method == METHOD.POST; } /** * helper method to check request method * * @return true if method is METHOD.PUT */ public boolean isPut() { return this.method == METHOD.PUT; } }