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

com.microsoft.azure.cosmosdb.rx.internal.RxDocumentServiceRequest Maven / Gradle / Ivy

/*
 * The MIT License (MIT)
 * Copyright (c) 2018 Microsoft Corporation
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.microsoft.azure.cosmosdb.rx.internal;

import java.io.InputStream;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import com.microsoft.azure.cosmosdb.internal.directconnectivity.WFConstants;
import org.apache.commons.lang3.StringUtils;

import com.microsoft.azure.cosmosdb.Resource;
import com.microsoft.azure.cosmosdb.SqlQuerySpec;
import com.microsoft.azure.cosmosdb.internal.HttpConstants;
import com.microsoft.azure.cosmosdb.internal.OperationType;
import com.microsoft.azure.cosmosdb.internal.PathInfo;
import com.microsoft.azure.cosmosdb.internal.PathsHelper;
import com.microsoft.azure.cosmosdb.internal.QueryCompatibilityMode;
import com.microsoft.azure.cosmosdb.internal.ResourceId;
import com.microsoft.azure.cosmosdb.internal.ResourceType;
import com.microsoft.azure.cosmosdb.internal.Utils;
import com.microsoft.azure.cosmosdb.internal.routing.PartitionKeyRangeIdentity;

import rx.Observable;
import rx.observables.StringObservable;

/**
 * This is core Transport/Connection agnostic request to the Azure Cosmos DB database service.
 */
public class RxDocumentServiceRequest {
    private static final char PREFER_HEADER_SEPERATOR = ';';
    private static final String PREFER_HEADER_VALUE_FORMAT = "%s=%s";

    public volatile boolean forcePartitionKeyRangeRefresh;
    public volatile boolean forceCollectionRoutingMapRefresh;
    private String resourceId;
    private final ResourceType resourceType;
    private final Map headers;
    private volatile String continuation;
    private boolean isMedia = false;
    private final boolean isNameBased;
    private final OperationType operationType;
    private final String resourceAddress;
    public volatile boolean forceNameCacheRefresh;
    private volatile URI endpointOverride = null;
    private final String activityId;
    private volatile String resourceFullName;

    private volatile String originalSessionToken;
    private volatile PartitionKeyRangeIdentity partitionKeyRangeIdentity;
    private volatile Integer defaultReplicaIndex;

    public DocumentServiceRequestContext requestContext;

    private Observable contentObservable;
    private byte[] byteContent;
    
    // NOTE: TODO: these fields are copied from .Net SDK
    // some of these fields are missing from the main java sdk service request
    // so it means most likely the corresponding features are also missing from the main sdk
    // we need to wire this up.
    public boolean UseGatewayMode;
    public boolean clearSessionTokenOnSessionReadFailure;

    private volatile boolean isDisposed = false;
    public volatile String entityId;
    public volatile String queryString;
    public volatile boolean isFeed;
    public volatile AuthorizationTokenType authorizationTokenType;

    public boolean isReadOnlyRequest() {
        return this.operationType == OperationType.Read
                || this.operationType == OperationType.ReadFeed
                || this.operationType == OperationType.Head
                || this.operationType == OperationType.HeadFeed
                || this.operationType == OperationType.Query
                || this.operationType == OperationType.SqlQuery;
    }

    public boolean isReadOnlyScript() {
        String isReadOnlyScript = this.headers.get(HttpConstants.HttpHeaders.IS_READ_ONLY_SCRIPT);
        if(StringUtils.isEmpty(isReadOnlyScript)) {
            return false;
        } else {
            return this.operationType.equals(OperationType.ExecuteJavaScript) && isReadOnlyScript.equalsIgnoreCase(Boolean.TRUE.toString());
        }
    }

    /**
     * @param operationType          the operation type.
     * @param resourceIdOrFullName   the request id or full name.
     * @param resourceType           the resource type.
     * @param byteContent            the byte content.
     * @param headers                the headers.
     * @param isNameBased            whether request is name based.
     * @param authorizationTokenType the request authorizationTokenType.
     */
    private RxDocumentServiceRequest(OperationType operationType,
                                     String resourceIdOrFullName,
                                     ResourceType resourceType,
                                     byte[] byteContent,
                                     Map headers,
                                     boolean isNameBased,
                                     AuthorizationTokenType authorizationTokenType) {
        this.operationType = operationType;
        this.forceNameCacheRefresh = false;
        this.resourceType = resourceType;
        this.byteContent = byteContent;
        this.headers = headers != null ? headers : new HashMap<>();
        this.activityId = Utils.randomUUID().toString();
        this.isFeed = false;
        this.isNameBased = isNameBased;
        if (isNameBased) {
            this.resourceAddress = resourceIdOrFullName;
        } else {
            this.resourceId = resourceIdOrFullName;
            this.resourceAddress = resourceIdOrFullName;
        }
        this.authorizationTokenType = authorizationTokenType;
        this.requestContext = new DocumentServiceRequestContext();
        if (StringUtils.isNotEmpty(this.headers.get(WFConstants.BackendHeaders.PARTITION_KEY_RANGE_ID)))
            this.partitionKeyRangeIdentity = PartitionKeyRangeIdentity.fromHeader(this.headers.get(WFConstants.BackendHeaders.PARTITION_KEY_RANGE_ID));
    }

    /**
     * Creates a AbstractDocumentServiceRequest
     * 
     * @param operationType     the operation type.
     * @param resourceIdOrFullName        the request id or full name.
     * @param resourceType      the resource type.
     * @param path              the path.
     * @param headers           the headers
     */
    private RxDocumentServiceRequest(OperationType operationType,
            String resourceIdOrFullName,
            ResourceType resourceType,
            String path,
            Map headers) {
        this.requestContext = new DocumentServiceRequestContext();
        this.operationType = operationType;
        this.resourceType = resourceType;
        this.requestContext.sessionToken = null;
        this.headers = headers != null ? headers : new HashMap<>();
        this.activityId = Utils.randomUUID().toString();
        this.isFeed = false;
        PathInfo pathInfo = new PathInfo(false, null, null, false);
        if (StringUtils.isNotEmpty(path)) {
            if (PathsHelper.tryParsePathSegments(path, pathInfo, null)) {
                this.isNameBased = pathInfo.isNameBased;
                this.isFeed = pathInfo.isFeed;
                resourceIdOrFullName = pathInfo.resourceIdOrFullName;
                if (!this.isNameBased) {
                if (resourceType == ResourceType.Media) {
                    this.resourceId = getAttachmentIdFromMediaId(resourceIdOrFullName);
                } else {
                    this.resourceId = resourceIdOrFullName;
                }

                this.resourceAddress = resourceIdOrFullName;

                    // throw exception when the address parsing fail
                    // do not parse address for offer resource
                    if (StringUtils.isNotEmpty(this.resourceId) && !ResourceId.tryParse(this.resourceId).getLeft()
                            && !resourceType.equals(ResourceType.Offer) && !resourceType.equals(ResourceType.Media)
                            && !resourceType.equals(ResourceType.MasterPartition)
                            && !resourceType.equals(ResourceType.ServerPartition)
                            && !resourceType.equals(ResourceType.DatabaseAccount)
                            && !resourceType.equals(ResourceType.RidRange)) {
                        throw new IllegalArgumentException(
                                String.format(RMResources.InvalidResourceUrlQuery, path, HttpConstants.QueryStrings.URL));
                    }
                } else {
                    this.resourceAddress = resourceIdOrFullName;
                    this.resourceId = null;
                }
            } else {
                throw new IllegalArgumentException(RMResources.NotFound);
            }
        } else {
            this.isNameBased = false;
            this.resourceAddress = resourceIdOrFullName;
        }

        if (StringUtils.isNotEmpty(this.headers.get(HttpConstants.HttpHeaders.PARTITION_KEY_RANGE_ID))) {
            this.partitionKeyRangeIdentity = PartitionKeyRangeIdentity
                    .fromHeader(this.headers.get(HttpConstants.HttpHeaders.PARTITION_KEY_RANGE_ID));
        }
    }

    /**
     * Creates a DocumentServiceRequest
     *
     * @param resourceId        the resource Id.
     * @param resourceType      the resource type.
     * @param content           the byte content observable\
     * @param contentObservable the byte content observable
     * @param headers           the request headers.
     */
    private RxDocumentServiceRequest(OperationType operationType,
            String resourceId,
            ResourceType resourceType,
            Observable contentObservable,
            byte[] content,
            String path,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        this( operationType,
             resourceId,
             resourceType,
             path,
             headers);
        this.authorizationTokenType = authorizationTokenType;
        this.byteContent = content;
        this.contentObservable = contentObservable;
    }

    /**
     * Creates a DocumentServiceRequest with an HttpEntity.
     *
     * @param resourceType          the resource type.
     * @param path                  the relative URI path.
     * @param contentObservable     the byte content observable
     * @param headers               the request headers.
     */
    private RxDocumentServiceRequest(OperationType operationType,
            ResourceType resourceType,
            String path,
            Observable contentObservable,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        this(operationType, extractIdFromUri(path), resourceType, contentObservable, null, path, headers, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest with an HttpEntity.
     *
     * @param resourceType the resource type.
     * @param path         the relative URI path.
     * @param byteContent  the byte content.
     * @param headers      the request headers.
     */
    private RxDocumentServiceRequest(OperationType operationType,
            ResourceType resourceType,
            String path,
            byte[] byteContent,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        this(operationType, extractIdFromUri(path), resourceType, null, byteContent, path, headers, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest with an HttpEntity.
     *
     * @param resourceType          the resource type.
     * @param path                  the relative URI path.
     * @param headers               the request headers.
     */
    private RxDocumentServiceRequest(OperationType operationType,
            ResourceType resourceType,
            String path,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        this(operationType, extractIdFromUri(path), resourceType, null , null, path, headers, authorizationTokenType);
    }

    public void setContentBytes(byte[] bytes) {
        this.byteContent = bytes;
    }

    /**
     * Creates a DocumentServiceRequest with a stream.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param content      the content observable
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            Observable content,
            Map headers) {
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, content, headers, AuthorizationTokenType.PrimaryMasterKey);
    }

    /**
     * Creates a DocumentServiceRequest with a stream.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param content      the content observable
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            Observable content,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, content, headers, authorizationTokenType);
    }

    /** Creates a DocumentServiceRequest with a stream.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param inputStream  the input stream.
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            InputStream inputStream,
            Map headers) {
        // StringObservable is mis-named. It doesn't make any assumptions on character set
        // and handles bytes only
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, StringObservable.from(inputStream), headers, AuthorizationTokenType.PrimaryMasterKey);
    }

    /** Creates a DocumentServiceRequest with a stream.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param inputStream  the input stream.
     * @param headers      the request headers.
     * @param authorizationTokenType      the request authorizationTokenType.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            InputStream inputStream,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        // StringObservable is mis-named. It doesn't make any assumptions on character set
        // and handles bytes only
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, StringObservable.from(inputStream), headers, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest with a resource.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param resource     the resource of the request.
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            Resource resource,
            Map headers) {

        return new RxDocumentServiceRequest(operation, resourceType, relativePath,
                // TODO: this re-encodes, can we improve performance here?
                resource.toJson().getBytes(StandardCharsets.UTF_8), headers, AuthorizationTokenType.PrimaryMasterKey);
    }

    /**
     * Creates a DocumentServiceRequest with a query.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param query        the query.
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            String query,
            Map headers) {
        return new RxDocumentServiceRequest(operation, resourceType, relativePath,
                query.getBytes(StandardCharsets.UTF_8), headers, AuthorizationTokenType.PrimaryMasterKey);
    }

    /**
     * Creates a DocumentServiceRequest with a query.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param query        the query.
     * @param headers      the request headers.
     * @param authorizationTokenType      the request authorizationTokenType.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            String query,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        return new RxDocumentServiceRequest(operation, resourceType, relativePath,
                query.getBytes(StandardCharsets.UTF_8), headers, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest with a query.
     *
     * @param resourceType           the resource type.
     * @param relativePath           the relative URI path.
     * @param querySpec              the query.
     * @param queryCompatibilityMode the QueryCompatibilityMode mode.
     * @param headers                the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(ResourceType resourceType,
            String relativePath,
            SqlQuerySpec querySpec,
            QueryCompatibilityMode queryCompatibilityMode,
            Map headers) {
        OperationType operation;
        String queryText;
        switch (queryCompatibilityMode) {
        case SqlQuery:
            if (querySpec.getParameters() != null && querySpec.getParameters().size() > 0) {
                throw new IllegalArgumentException(
                        String.format("Unsupported argument in query compatibility mode '{%s}'",
                                queryCompatibilityMode.name()));
            }

            operation = OperationType.SqlQuery;
            queryText = querySpec.getQueryText();
            break;

        case Default:
        case Query:
        default:
            operation = OperationType.Query;
            queryText = querySpec.toJson();
            break;
        }

        Observable body = StringObservable.encode(Observable.just(queryText), StandardCharsets.UTF_8);
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, body, headers, AuthorizationTokenType.PrimaryMasterKey);
    }

    /**
     * Creates a DocumentServiceRequest without body.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            Map headers) {
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, headers, AuthorizationTokenType.PrimaryMasterKey);
    }

    /**
     * Creates a DocumentServiceRequest without body.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param headers      the request headers.
     * @param authorizationTokenType      the request authorizationTokenType.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            ResourceType resourceType,
            String relativePath,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, headers, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest without body.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            Resource resource,
            ResourceType resourceType,
            String relativePath,
            Map headers) {
        byte[] resourceContent = resource.toJson().getBytes(StandardCharsets.UTF_8);
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, resourceContent, headers, AuthorizationTokenType.PrimaryMasterKey);
    }

    /**
     * Creates a DocumentServiceRequest without body.
     *
     * @param operation    the operation type.
     * @param resourceType the resource type.
     * @param relativePath the relative URI path.
     * @param headers      the request headers.
     * @param authorizationTokenType      the request authorizationTokenType.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            Resource resource,
            ResourceType resourceType,
            String relativePath,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        byte[] resourceContent = resource.toJson().getBytes(StandardCharsets.UTF_8);
        return new RxDocumentServiceRequest(operation, resourceType, relativePath, resourceContent, headers, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest with a resourceId.
     *
     * @param operation    the operation type.
     * @param resourceId   the resource id.
     * @param resourceType the resource type.
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            String resourceId,
            ResourceType resourceType,
            Map headers) {
        return new RxDocumentServiceRequest(operation, resourceId,resourceType, null, headers, false, AuthorizationTokenType.PrimaryMasterKey) ;
    }

    /**
     * Creates a DocumentServiceRequest with a resourceId.
     *
     * @param operation    the operation type.
     * @param resourceId   the resource id.
     * @param resourceType the resource type.
     * @param headers      the request headers.
     * @param authorizationTokenType      the request authorizationTokenType.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            String resourceId,
            ResourceType resourceType,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        return new RxDocumentServiceRequest(operation, resourceId, resourceType, null, headers, false, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest with a resourceId.
     *
     * @param operation    the operation type.
     * @param resourceId   the resource id.
     * @param resourceType the resource type.
     * @param headers      the request headers.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            String resourceId,
            ResourceType resourceType,
            Resource resource,
            Map headers) {
        byte[] resourceContent = resource.toJson().getBytes(StandardCharsets.UTF_8);
        return new RxDocumentServiceRequest(operation, resourceId, resourceType, resourceContent, headers, false, AuthorizationTokenType.PrimaryMasterKey);
    }

    /**
     * Creates a DocumentServiceRequest with a resourceId.
     *
     * @param operation    the operation type.
     * @param resourceId   the resource id.
     * @param resourceType the resource type.
     * @param headers      the request headers.
     * @param authorizationTokenType      the request authorizationTokenType.
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
            String resourceId,
            ResourceType resourceType,
            Resource resource,
            Map headers,
            AuthorizationTokenType authorizationTokenType) {
        byte[] resourceContent = resource.toJson().getBytes(StandardCharsets.UTF_8);
        return new RxDocumentServiceRequest(operation, resourceId, resourceType, resourceContent, headers, false, authorizationTokenType);
    }

    /**
     * Creates a DocumentServiceRequest with operationType and resourceType
     * @param operation     the operation type
     * @param resourceType  the resource type
     * @return the created document service request.
     */
    public static RxDocumentServiceRequest create(OperationType operation,
                                                  ResourceType resourceType) {
        return new RxDocumentServiceRequest(operation, null, resourceType, null, null);
    }

    public static RxDocumentServiceRequest createFromName(
            OperationType operationType,
            String resourceFullName,
            ResourceType resourceType) {
        return new RxDocumentServiceRequest(operationType,
                resourceFullName,
                resourceType,
                null,
                new HashMap<>(),
                true,
                AuthorizationTokenType.PrimaryMasterKey
        );
    }

    public static RxDocumentServiceRequest createFromName(
            OperationType operationType,
            String resourceFullName,
            ResourceType resourceType,
            AuthorizationTokenType authorizationTokenType) {
        return new RxDocumentServiceRequest(operationType,
                resourceFullName,
                resourceType,
                null,
                new HashMap<>(),
                true,
                authorizationTokenType
        );
    }

    public static RxDocumentServiceRequest createFromName(
            OperationType operationType,
            Resource resource,
            String resourceFullName,
            ResourceType resourceType) {
        byte[] resourceContent = resource.toJson().getBytes(StandardCharsets.UTF_8);
        return new RxDocumentServiceRequest(operationType,
                resourceFullName,
                resourceType,
                resourceContent,
                new HashMap<>(),
                true,
                AuthorizationTokenType.PrimaryMasterKey
        );
    }

    public static RxDocumentServiceRequest createFromName(
            OperationType operationType,
            Resource resource,
            String resourceFullName,
            ResourceType resourceType,
            AuthorizationTokenType authorizationTokenType) {
        byte[] resourceContent = resource.toJson().getBytes(StandardCharsets.UTF_8);
        return new RxDocumentServiceRequest(operationType,
                resourceFullName,
                resourceType,
                resourceContent,
                new HashMap<>(),
                true,
                authorizationTokenType
        );
    }

    private static String extractIdFromUri(String path) {
        if (path.length() == 0) {
            return path;
        }

        if (path.charAt(path.length() - 1) != '/') {
            path = path + '/';
        }

        if (path.charAt(0) != '/') {
            path = '/' + path;
        }
        // This is a hack. We need a padding '=' so that path.split("/")
        // returns even number of string pieces.
        // TODO(pushi): Improve the code and remove the hack.
        path = path + '=';

        // The path will be in the form of 
        // /[resourceType]/[resourceId]/ or
        // /[resourceType]/[resourceId]/[resourceType]/
        // The result of split will be in the form of
        // [[[resourceType], [resourceId] ... ,[resourceType], ""]
        // In the first case, to extract the resourceId it will the element
        // before last ( at length -2 ) and the the type will before it
        // ( at length -3 )
        // In the second case, to extract the resource type it will the element
        // before last ( at length -2 )
        String[] pathParts = StringUtils.split(path, "/");
        if (pathParts.length % 2 == 0) {
            // request in form /[resourceType]/[resourceId]/.
            return pathParts[pathParts.length - 2];
        } else {
            // request in form /[resourceType]/[resourceId]/[resourceType]/.
            return pathParts[pathParts.length - 3];
        }
    }

    static String getAttachmentIdFromMediaId(String mediaId) {
        // '/' was replaced with '-'.
        byte[] buffer = Utils.Base64Decoder.decode(mediaId.replace('-', '/').getBytes());

        final int resoureIdLength = 20;
        String attachmentId;

        if (buffer.length > resoureIdLength) {
            // We are cuting off the storage index.
            byte[] newBuffer = new byte[resoureIdLength];
            System.arraycopy(buffer, 0, newBuffer, 0, resoureIdLength);
            attachmentId = Utils.encodeBase64String(newBuffer).replace('/', '-');
        } else {
            attachmentId = mediaId;
        }

        return attachmentId;
    }

    /**
     * Gets the resource id.
     *
     * @return the resource id.
     */
    public String getResourceId() {
        return this.resourceId;
    }

    /**
     * Sets the resource id.
     *
     */
    public void setResourceId(String resourceId) {
        this.resourceId = resourceId;
    }

    /**
     * Gets the resource type.
     *
     * @return the resource type.
     */
    public ResourceType getResourceType() {
        return this.resourceType;
    }

    /**
     * Gets the request headers.
     *
     * @return the request headers.
     */
    public Map getHeaders() {
        return this.headers;
    }

    /**
     * Gets the continuation.
     *
     * @return the continuation.
     */
    public String getContinuation() {
        return this.continuation;
    }

    public void setContinuation(String continuation) {
        this.continuation = continuation;
    }

    public boolean getIsMedia() {
        return this.isMedia;
    }

    public void setIsMedia(boolean isMedia) {
        this.isMedia = isMedia;
    }

    public boolean getIsNameBased() {
        return this.isNameBased;
    }

    public OperationType getOperationType() {
        return this.operationType;
    }

    public String getResourceAddress() {
        return resourceAddress;
    }

    public boolean isForceNameCacheRefresh() {
        return forceNameCacheRefresh;
    }

    public void setForceNameCacheRefresh(boolean forceNameCacheRefresh) {
        this.forceNameCacheRefresh = forceNameCacheRefresh;
    }

    public URI getEndpointOverride() {
        return this.endpointOverride;
    }

    public void setEndpointOverride(URI endpointOverride) {
        this.endpointOverride = endpointOverride;
    }

    public String getActivityId() {
        return this.activityId;
    }

    public PartitionKeyRangeIdentity getPartitionKeyRangeIdentity() {
        return partitionKeyRangeIdentity;
    }

    public void routeTo(PartitionKeyRangeIdentity partitionKeyRangeIdentity) {
        this.setPartitionKeyRangeIdentity(partitionKeyRangeIdentity);
    }

    public void setPartitionKeyRangeIdentity(PartitionKeyRangeIdentity partitionKeyRangeIdentity) {
        this.partitionKeyRangeIdentity = partitionKeyRangeIdentity;
        if (partitionKeyRangeIdentity != null) {
            this.headers.put(HttpConstants.HttpHeaders.PARTITION_KEY_RANGE_ID, partitionKeyRangeIdentity.toHeader());
        } else {
            this.headers.remove(HttpConstants.HttpHeaders.PARTITION_KEY_RANGE_ID);
        }
    }

    public String getOriginalSessionToken() {
        return originalSessionToken;
    }

    public void setOriginalSessionToken(String originalSessionToken) {
        this.originalSessionToken = originalSessionToken;
    }

    public void setDefaultReplicaIndex(Integer defaultReplicaIndex) {
        this.defaultReplicaIndex = defaultReplicaIndex;
    }

    public Integer getDefaultReplicaIndex() {
        return defaultReplicaIndex;
    }

    public boolean isChangeFeedRequest() {
        return this.headers.containsKey(HttpConstants.HttpHeaders.A_IM);
    }

    public boolean isWritingToMaster() {
        return operationType.isWriteOperation() && resourceType.isMasterResource();
    }

    public boolean isReadingFromMaster() {
        if (resourceType == ResourceType.Offer ||
                resourceType == ResourceType.Database ||
                resourceType == ResourceType.User ||
                resourceType == ResourceType.Permission ||
                resourceType == ResourceType.Topology ||
                resourceType == ResourceType.DatabaseAccount ||
                resourceType == ResourceType.PartitionKeyRange ||
                (resourceType == ResourceType.DocumentCollection
                        && (operationType == OperationType.ReadFeed
                        || operationType == OperationType.Query
                        || operationType == OperationType.SqlQuery))) {
            return true;
        }
        return false;
    }

    public boolean isValidAddress(ResourceType resourceType) {
        ResourceType resourceTypeToValidate = ResourceType.Unknown;

        if(resourceType != ResourceType.Unknown) {
            resourceTypeToValidate = resourceType;
        } else {
            if(!this.isFeed) {
                resourceTypeToValidate =this.resourceType;
            } else {
                if(this.resourceType == ResourceType.Database) {
                    return true;
                } else if(this.resourceType == ResourceType.DocumentCollection ||
                          this.resourceType == ResourceType.User) {
                    resourceTypeToValidate = ResourceType.Database;
                } else if(this.resourceType == ResourceType.Permission) {
                    resourceTypeToValidate = ResourceType.User;
                } else if(this.resourceType == ResourceType.Document ||
                        this.resourceType == ResourceType.StoredProcedure ||
                        this.resourceType == ResourceType.UserDefinedFunction ||
                        this.resourceType == ResourceType.Trigger ||
                        this.resourceType == ResourceType.Conflict ||
                        this.resourceType == ResourceType.PartitionKeyRange) {
                  resourceTypeToValidate = ResourceType.DocumentCollection;
              } else if(this.resourceType == ResourceType.Attachment) {
                  resourceTypeToValidate = ResourceType.Document;
              }  else {
                  return false;
              }
            }
        }

        if (this.isNameBased) {
            return PathsHelper.validateResourceFullName(resourceType != ResourceType.Unknown ? resourceType : resourceTypeToValidate, this.resourceAddress);
        } else {
            return PathsHelper.validateResourceId(resourceTypeToValidate, this.resourceId);
        }
    }

    public void addPreferHeader(String preferHeaderName, String preferHeaderValue) {
        String headerToAdd = String.format(PREFER_HEADER_VALUE_FORMAT, preferHeaderName, preferHeaderValue);
        String preferHeader = this.headers.get(HttpConstants.HttpHeaders.PREFER);
        if(StringUtils.isNotEmpty(preferHeader)) {
            preferHeader += PREFER_HEADER_SEPERATOR + headerToAdd;
        } else {
            preferHeader = headerToAdd;
        }
        this.headers.put(HttpConstants.HttpHeaders.PREFER, preferHeader);
    }

    public static RxDocumentServiceRequest CreateFromResource(RxDocumentServiceRequest request, Resource modifiedResource) {
        RxDocumentServiceRequest modifiedRequest;
        if (!request.getIsNameBased()) {
            modifiedRequest = RxDocumentServiceRequest.create(request.getOperationType(),
                                                              request.getResourceId(),
                                                              request.getResourceType(),
                                                              modifiedResource,
                                                              request.headers);
        } else {
            modifiedRequest = RxDocumentServiceRequest.createFromName(request.getOperationType(),
                                                                      modifiedResource,
                                                                      request.getResourceAddress(),
                                                                      request.getResourceType());
        }
        return modifiedRequest;
    }

    public void clearRoutingHints() {
        this.partitionKeyRangeIdentity = null;
        this.requestContext.resolvedPartitionKeyRange = null;
    }

    public Observable getContentObservable() {
        return contentObservable;
    }

    public byte[] getContent() {
        return byteContent;
    }

    public RxDocumentServiceRequest clone() {
        RxDocumentServiceRequest rxDocumentServiceRequest = RxDocumentServiceRequest.create(this.getOperationType(), this.resourceId,this.getResourceType(),this.getHeaders());
        rxDocumentServiceRequest.setContentBytes(this.getContent());
        rxDocumentServiceRequest.setContinuation(this.getContinuation());
        rxDocumentServiceRequest.setDefaultReplicaIndex(this.getDefaultReplicaIndex());
        rxDocumentServiceRequest.setEndpointOverride(this.getEndpointOverride());
        rxDocumentServiceRequest.setForceNameCacheRefresh(this.isForceNameCacheRefresh());
        rxDocumentServiceRequest.setIsMedia(this.getIsMedia());
        rxDocumentServiceRequest.setOriginalSessionToken(this.getOriginalSessionToken());
        rxDocumentServiceRequest.setPartitionKeyRangeIdentity(this.getPartitionKeyRangeIdentity());
        rxDocumentServiceRequest.contentObservable = this.getContentObservable();
        rxDocumentServiceRequest.forceCollectionRoutingMapRefresh = this.forceCollectionRoutingMapRefresh;
        rxDocumentServiceRequest.forcePartitionKeyRangeRefresh = this.forcePartitionKeyRangeRefresh;
        rxDocumentServiceRequest.UseGatewayMode = this.UseGatewayMode;
        rxDocumentServiceRequest.queryString = this.queryString;
        rxDocumentServiceRequest.clearSessionTokenOnSessionReadFailure = this.clearSessionTokenOnSessionReadFailure;
        rxDocumentServiceRequest.requestContext = this.requestContext;
        return rxDocumentServiceRequest;
    }

    public void Dispose() {
        if (this.isDisposed) {
            return;
        }

        if (this.byteContent != null) {
            this.byteContent = null;
        }

        this.isDisposed = true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy