All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sap.cloud.sdk.odatav2.connectivity.ODataQueryBuilder Maven / Gradle / Ivy

There is a newer version: 1.40.11
Show newest version
/*******************************************************************************
 * (c) 201X SAP SE or an SAP affiliate company. All rights reserved.
 ******************************************************************************/
package com.sap.cloud.sdk.odatav2.connectivity;

import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.apache.olingo.odata2.api.edm.EdmEntityType;
import org.apache.olingo.odata2.api.edm.EdmException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.sap.cloud.sdk.cloudplatform.cache.CacheKey;
import com.sap.cloud.sdk.odatav2.connectivity.api.IQueryBuilder;
import com.sap.cloud.sdk.odatav2.connectivity.impl.ODataQueryImpl;

import lombok.Setter;
import lombok.experimental.Accessors;

@Accessors(chain = true, fluent = true)
public final class ODataQueryBuilder implements IQueryBuilder{

	final static Logger logger = LoggerFactory.getLogger(ODataQueryBuilder.class);
	public static final char QUERY_PARAM_SEPARATOR = '&';

	private final String servicePath;
	private final String entity;

	@Setter
	private ErrorResultHandler errorHandler;

	private Map headers = new HashMap();

	private Map destinationRelevantHeaders = new HashMap();

	private List navigationProperties = new ArrayList();
	
	private ODataNavigationRequest navigationProperty;
	
	private Number top;

	private Number skip;

	String inlineCount = null;
	// @Setter
	// private String id = null;

	
	private boolean useMetadata = true;

	private final Map queryParams = Maps.newHashMap();
	private final List selects = Lists.newArrayList();
	private final List filters = Lists.newArrayList();
	private final List expands = Lists.newArrayList();

	private Map keys;

	// this variable is true is metadata cache is enabled.
	private boolean cacheMetadata = false;

	// this variable have the cacheKey value.
	private CacheKey cacheKey;

	private boolean isCacheRefresh = false;

	private URL metadataFilePath;

	private ODataQueryBuilder(String servicePath, String entity) {
		this.servicePath = servicePath;
		this.entity = entity;
	}

	/**
	 * Creates an ODataQueryBuilder with the given service and entity name.
	 * 
	 * @param serviceName   name of the odata service where the developer wants to
	 *                      execute a query operation.
	 * @param entitySetName name of the entity set, on which the developer wants to
	 *                      execute a query operation.
	 * @return ODataQueryBuilder
	 */
	public static ODataQueryBuilder withEntity(String servicePath, String entity) {
		return new ODataQueryBuilder(servicePath, entity);
	}

	/**
	 * Selects properties to read.
	 * 
	 * @param selects The list of properties to be read.
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder select(final String... selects) {
		Collections.addAll(this.selects, selects);
		return this;
	}

	/**
	 * Selects properties to read.
	 * 
	 * @param selects of type List- The list of properties to be read.
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder select(final List selects) {
		String[] s = selects.toArray(new String[selects.size()]);
		Collections.addAll(this.selects, s);
		return this;
	}

	/**
	 * Enables caching of the metadata of an OData V2 data source. If your
	 * application is running on a tenant, then the tenant ID along with the
	 * metadata URL is used to form the cache key.
	 * 
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder enableMetadataCache() {
		this.cacheMetadata = true;
		return this;
	}

	/**
	 * Enables caching of the metadata of an OData V2 data source.
	 * 
	 * @param key {@link com.sap.cloud.sdk.cloudplatform.cache.CacheKey Cache key}
	 *            containing the ID of the tenant where the application runs. You
	 *            can also include the user name in the cache key.
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder enableMetadataCache(CacheKey cacheKey) {
		this.cacheKey = cacheKey;
		this.cacheMetadata = true;
		return this;
	}

	/**
	 * Replaces the existing metadata in the cache with the latest version from the
	 * OData V2 data source.
	 * 
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder withCacheRefresh() {
		this.isCacheRefresh = true;
		return this;

	}

	/**
	 * Gets the metadata from the specified path.
	 * 
	 * @param metadataFilePath URL pointing to the metadata information
	 * @return ODataQueryBuilder A builder for forming the Create
	 */
	public ODataQueryBuilder withMetadata(URL metadataFilePath) {
		this.metadataFilePath = metadataFilePath;
		return this;
	}

	/**
	 * Adds a header to the query request.
	 * 
	 * @param key   name of the header
	 * @param value value of the header
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder withHeader(String key, String value) {
		return withHeader(key, value, false);
	}

	/**
	 * Adds a header to the query request and optionally to the metadata request as
	 * well depending on the value of the passInAllRequests parameter.
	 * 
	 * @param key               name of the header
	 * @param value             value of the header
	 * @param passInAllRequests boolean indicating whether the header is to be
	 *                          passed in all the requests to the backend like
	 *                          $metadata call etc. made as part of the Query
	 *                          Request call.
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder withHeader(String key, String value, boolean passInAllRequests) {

		if (passInAllRequests)
			destinationRelevantHeaders.put(key, value);// These headers are added to the metadata request.

		if (key.equals("SAP-PASSPORT") && !passInAllRequests)
			destinationRelevantHeaders.put(key, value);// The header "SAP-PASSPORT" is added to metadata request even
														// though the 'passInAllRequests' us false.

		headers.put(key, value);// All headers must be considered for the actual OData operation.
		return this;

	}

	/**
	 * Sets $inlinecount=allpages as a query parameter.
	 * 
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder inlineCount() {
		inlineCount = "allpages";
		return this;
	}

	/**
	 * Selects navigation properties to expand.
	 * 
	 * @param expands List of navigation properties to expand
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder expand(final String... expands) {
		Collections.addAll(this.expands, expands);
		return this;
	}

	/**
	 * Sets top value.
	 * 
	 * @param n the top value required
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder top(Integer n) {
		if (n != null && n >= 0) {
			top = n;
		}
		return this;
	}

	/**
	 * Sets skip value.
	 * 
	 * @param n the skip value required
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder skip(Integer n) {
		if (n != null && n >= 0) {
			skip = n;
		}
		return this;
	}

	/**
	 * Adds a Filter expression to the OData query.
	 * 
	 * @param filter Object of FilterExpression that represents an OData filter
	 *               expression.
	 * @return ODataQueryBuilder
	 */
	public final ODataQueryBuilder filter(final FilterExpression filter) {
		filters.add(filter);
		return this;
	}

	/**
	 * Adds a query parameter to the OData request.
	 * 
	 * @param key
	 * @param value
	 * @return ODataQueryBuilder
	 */
	public ODataQueryBuilder param(final String key, final Object value) {
		if (key.equals("$inlinecount") && value.toString().equals("allpages"))
			inlineCount();
		else
			queryParams.put(key, value);
		return this;
	}

	/**
	 * Used to tell the framework to not get the metadata before the actual query.
	 * This is not honoured if keys are set or if filter is set because the
	 * framework uses metadata for these use cases.
	 * 
	 * @return
	 */
	public ODataQueryBuilder withoutMetadata() {
		useMetadata = false;
		return this;
	}

	/**
	 * Set Keys for the entity set being queried
	 * 
	 * @param keys
	 * @return
	 */
	public ODataQueryBuilder keys(Map keys) {
		if(navigationProperty != null){
			navigationProperty.keys(keys);
		}else{
			this.keys = keys;
		}
		return this;
	}

	
	public ODataQueryBuilder useMetadata(boolean useMetadata) {
		this.useMetadata = useMetadata;
		return this;
	}

	/**
	 * Builds an OData query from this builder.
	 * 
	 * @return ODataQuery
	 */
	public ODataQuery build() {
		if (keys != null) // If keys is not null it means that its a READ call which does not support
							// inlineCount, hence simply ignoring inlinecount.
			inlineCount = null;
		if (keys != null || !filters.isEmpty()) // If keys or filter is set, then we need the metadata for serialization
												// hence manually setting useMetadata to true.
			useMetadata = true;
		return new ODataQueryImpl(servicePath, entity, keys, new ODataQueryResolver(), errorHandler, headers,
				destinationRelevantHeaders, useMetadata, cacheMetadata, metadataFilePath, cacheKey, 
				isCacheRefresh, navigationProperties);
	}

	public class ODataQueryResolver {
		/*
		 * String getPath() { if( StringUtils.isNotEmpty(id) ) { return "(" + id + ")";
		 * } return null; }
		 */

		public String getQuery() {
			try {
				return getQuery(null);
			} catch (EdmException e) {
				logger.error("Error while forming the query", e);
				return null;
			}
		}

		public String getQuery(EdmEntityType entityType) throws EdmException {
			final ArrayList modifiers = new ArrayList<>();
			addModifierFilter(modifiers, filters, entityType);
			addModifier(modifiers, queryParams);
			addModifier(modifiers, "$select", selects);
			addModifier(modifiers, "$expand", expands);
			addModifier(modifiers, "$top", top);
			addModifier(modifiers, "$skip", skip);
			addModifier(modifiers, "$format", "json");
			addModifier(modifiers, "$inlinecount", inlineCount);
			return StringUtils.join(modifiers, QUERY_PARAM_SEPARATOR);
		}

		private void addModifier(final ArrayList mods, final String label, final Object val) {
			if (val != null) {
				mods.add(label + "=" + val.toString());
			}
		}

		private void addModifier(final ArrayList mods, final String label, final Iterable values) {
			final StringBuilder valueBlock = new StringBuilder();
			if (values != null && values.iterator().hasNext()) {

				final List encodedItems = Lists.newArrayList();
				for (final String val : values) {
					encodedItems.add(val);
				}
				mods.add(label + "=" + StringUtils.join(encodedItems, ','));
			}
		}

		private void addModifierFilter(final ArrayList mods, final List values,
				EdmEntityType entityType) throws EdmException {
			if (values != null) {
				final Iterator iterator = values.iterator();
				FilterExpression filter = null;
				while (iterator.hasNext()) {
					filter = filter == null ? iterator.next() : filter.and(iterator.next());
				}
				if (filter != null) {
					addModifier(mods, "$filter", filter.toString(entityType));
				}
			}
		}

		private void addModifier(final ArrayList mods, final Map properties) {
			if (properties != null) {
				for (final Map.Entry property : properties.entrySet()) {
					addModifier(mods, property.getKey(), property.getValue().toString());
				}
			}
		}
	}

	@Override
	public ODataQueryBuilder navigateTo(String navigation) {
		this.navigationProperty = new ODataNavigationRequest(navigation);
		navigationProperties.add(navigationProperty);
		return this;
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy