Пример #1
0
        /// <summary>
        /// Creates a <see cref="ODataJsonLightBatchPayloadItemPropertiesCache"/>
        /// and subsequently scans the JSON object for known properties and caches them.
        /// </summary>
        /// <param name="jsonBatchReader">The JSON batch reader.</param>
        /// <returns>A <see cref="ODataJsonLightBatchPayloadItemPropertiesCache"/> instance.</returns>
        internal static ODataJsonLightBatchPayloadItemPropertiesCache Create(ODataJsonLightBatchReader jsonBatchReader)
        {
            Debug.Assert(jsonBatchReader != null, $"{nameof(jsonBatchReader)} != null");

            ODataJsonLightBatchPayloadItemPropertiesCache jsonLightBatchPayloadItemPropertiesCache = new ODataJsonLightBatchPayloadItemPropertiesCache(jsonBatchReader);

            jsonLightBatchPayloadItemPropertiesCache.ScanJsonProperties();

            return(jsonLightBatchPayloadItemPropertiesCache);
        }
Пример #2
0
        /// <summary>
        /// Asynchronously creates a <see cref="ODataJsonLightBatchPayloadItemPropertiesCache"/>
        /// and subsequently scans the JSON object for known properties and caches them.
        /// </summary>
        /// <param name="jsonBatchReader">The JSON batch reader.</param>
        /// <returns>
        /// A task that represents the asynchronous write operation.
        /// The value of the TResult parameter contains a <see cref="ODataJsonLightBatchPayloadItemPropertiesCache"/> instance.
        /// </returns>
        internal static async Task <ODataJsonLightBatchPayloadItemPropertiesCache> CreateAsync(ODataJsonLightBatchReader jsonBatchReader)
        {
            Debug.Assert(jsonBatchReader != null, $"{nameof(jsonBatchReader)} != null");

            ODataJsonLightBatchPayloadItemPropertiesCache jsonLightBatchPayloadItemPropertiesCache = new ODataJsonLightBatchPayloadItemPropertiesCache(jsonBatchReader);

            await jsonLightBatchPayloadItemPropertiesCache.ScanJsonPropertiesAsync()
            .ConfigureAwait(false);

            return(jsonLightBatchPayloadItemPropertiesCache);
        }
        /// <summary>
        /// Implementation of the reader logic when in state 'Operation'.
        /// </summary>
        /// <returns>The batch reader state after the read.</returns>
        protected override ODataBatchReaderState ReadAtOperationImplementation()
        {
            if (this.JsonLightInputContext.JsonReader.NodeType != JsonNodeType.StartObject)
            {
                // No more requests in the batch.
                return(HandleMessagesEnd());
            }

            // Load the message properties if there is no cached item for processing.
            if (this.messagePropertiesCache == null)
            {
                // Load the message details since operation is detected.
                this.messagePropertiesCache = ODataJsonLightBatchPayloadItemPropertiesCache.Create(this);
            }

            // Calculate and return next state with changeset state detection.
            return(DetectChangesetStates(this.messagePropertiesCache));
        }
        /// <summary>
        /// Asynchronous implementation of the reader logic when in state 'Start'.
        /// </summary>
        /// <returns>
        /// A task that represents the asynchronous read operation.
        /// The value of the TResult parameter contains the batch reader state after the read.
        /// </returns>
        protected override async Task <ODataBatchReaderState> ReadAtStartImplementationAsync()
        {
            Debug.Assert(this.State == ODataBatchReaderState.Initial, $"{nameof(this.State)} == {nameof(ODataBatchReaderState.Initial)}");

            if (mode == ReaderMode.NotDetected)
            {
                // The stream should be positioned at the beginning of the batch envelope.
                // Need to detect whether we are reading request or response. Stay in Initial state upon return.
                await DetectReaderModeAsync()
                .ConfigureAwait(false);

                return(ODataBatchReaderState.Initial);
            }
            else
            {
                // The stream should be positioned at the beginning of requests array.
                await this.StartReadingBatchArrayAsync()
                .ConfigureAwait(false);

                Debug.Assert(this.messagePropertiesCache == null, $"{nameof(this.messagePropertiesCache)} == null");
                this.messagePropertiesCache = await ODataJsonLightBatchPayloadItemPropertiesCache.CreateAsync(this)
                                              .ConfigureAwait(false);

                string currentGroup = (string)this.messagePropertiesCache.GetPropertyValue(
                    ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup);

                if (currentGroup == null)
                {
                    return(ODataBatchReaderState.Operation);
                }
                else
                {
                    HandleNewAtomicGroupStart(
                        (string)this.messagePropertiesCache.GetPropertyValue(
                            ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameId),
                        currentGroup);
                    return(ODataBatchReaderState.ChangesetStart);
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Returns the cached <see cref="ODataBatchOperationResponseMessage"/> for reading the content of a
        /// batch response.
        /// </summary>
        /// <returns>The message that can be used to read the content of the batch response from.</returns>
        protected override ODataBatchOperationResponseMessage CreateOperationResponseMessageImplementation()
        {
            Debug.Assert(this.mode == ReaderMode.Responses, "this.mode == ReaderMode.Responses");
            Debug.Assert(this.messagePropertiesCache != null, "this.responsePropertiesCache != null");

            // body. Use empty stream when request body is not present.
            Stream bodyContentStream =
                (Stream)this.messagePropertiesCache.GetPropertyValue(ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameBody)
                ?? new ODataJsonLightBatchBodyContentReaderStream(this);

            int statusCode = (int)
                             this.messagePropertiesCache.GetPropertyValue(ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameStatus);

            string contentId = (string)this.messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameId);

            string groupId = (string)this.messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup);

            ODataBatchOperationHeaders headers = (ODataBatchOperationHeaders)
                                                 this.messagePropertiesCache.GetPropertyValue(ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameHeaders);

            // Reset the response property cache since all data in cache has been processed.
            // So that new instance can be created during subsequent read in operation state.
            this.messagePropertiesCache = null;

            // In responses we don't need to use our batch URL resolver, since there are no cross referencing URLs
            // so use the URL resolver from the batch message instead.
            ODataBatchOperationResponseMessage responseMessage = BuildOperationResponseMessage(
                () => bodyContentStream,
                statusCode,
                headers,
                contentId,
                groupId);

            //// NOTE: Content-IDs for cross referencing are only supported in request messages; in responses
            //// we allow a Content-ID header but don't process it (i.e., don't add the content ID to the URL resolver).
            return(responseMessage);
        }
Пример #6
0
        /// <summary>
        /// Examine changeset states for the current message and setup reader state accordingly if
        /// changeset related state transition is detected.
        /// </summary>
        /// <param name="messagePropertiesCache">Current message properties.</param>
        /// <returns>The next state for the reader.</returns>
        private ODataBatchReaderState DetectChangesetStates(ODataJsonLightBatchPayloadItemPropertiesCache messagePropertiesCache)
        {
            // Validate message Id.
            string valueId = (string)messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameId);

            string currentGroup = (string)messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup);

            // ChangesetEnd check first; If not, check for changesetStart.
            bool changesetEnd   = this.atomicGroups.IsChangesetEnd(currentGroup);
            bool changesetStart = false;

            if (!changesetEnd)
            {
                if (currentGroup != null)
                {
                    // Add message Id to atomic group (create new group if needed).
                    // Also detect changeset start.
                    changesetStart = this.atomicGroups.AddMessageIdAndGroupId(valueId, currentGroup);
                }
            }

            // If we have changeset state change detected, set the state here.
            ODataBatchReaderState nextState = ODataBatchReaderState.Operation;

            if (changesetEnd)
            {
                nextState = ODataBatchReaderState.ChangesetEnd;
            }
            else if (changesetStart)
            {
                nextState = ODataBatchReaderState.ChangesetStart;
            }

            return(nextState);
        }
Пример #7
0
        /// <summary>
        /// Implementation of the reader logic when in state 'Start'.
        /// </summary>
        /// <returns>The batch reader state after the read.</returns>
        protected override ODataBatchReaderState ReadAtStartImplementation()
        {
            Debug.Assert(this.State == ODataBatchReaderState.Initial, "this.State == ODataBatchReaderState.Initial");
            if (mode == ReaderMode.NotDetected)
            {
                // The stream should be positioned at the beginning of the batch envelope.
                // Need to detect whether we are reading request or response. Stay in Initial state upon return.
                DetectReaderMode();
                return(ODataBatchReaderState.Initial);
            }
            else
            {
                // The stream should be positioned at the beginning of requests array.
                this.StartReadingBatchArray();

                Debug.Assert(this.messagePropertiesCache == null, "this.messagePropertiesCache == null");
                this.messagePropertiesCache =
                    new ODataJsonLightBatchPayloadItemPropertiesCache(this);

                string currentGroup = (string)this.messagePropertiesCache.GetPropertyValue(
                    ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup);

                if (currentGroup == null)
                {
                    return(ODataBatchReaderState.Operation);
                }
                else
                {
                    HandleNewAtomicGroupStart(
                        (string)
                        this.messagePropertiesCache.GetPropertyValue(
                            ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameId),
                        currentGroup);
                    return(ODataBatchReaderState.ChangesetStart);
                }
            }
        }
Пример #8
0
        /// <summary>
        /// Returns the cached <see cref="ODataBatchOperationRequestMessage"/> for reading the content of an operation
        /// in a batch request.
        /// </summary>
        /// <returns>The message that can be used to read the content of the batch request operation from.</returns>
        protected override ODataBatchOperationRequestMessage CreateOperationRequestMessageImplementation()
        {
            Debug.Assert(this.mode == ReaderMode.Requests, "this.mode == ReaderMode.Requests");
            Debug.Assert(this.messagePropertiesCache != null, "this.messagePropertiesCache != null");

            // id
            string id = (string)this.messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameId);

            // atomicityGroup
            string atomicityGroupId = (string)this.messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup);

            if (id != null)
            {
                this.requestIds.Add(id);
            }

            if (atomicityGroupId != null)
            {
                this.requestIds.Add(atomicityGroupId);
            }

            // dependsOn
            // Flatten the dependsOn list by converting every groupId into request Ids, so that the caller
            // can decide, at the earliest opportunity, whether the depending request can be invoked.
            // Note that the forward reference of dependsOn id is not allowed, so the atomicGroups should have accurate
            // information of atomicGroup that needs to be flattened.
            IList <string> dependsOnReqIds = null;
            List <string>  dependsOn       = (List <string>) this.messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameDependsOn);

            if (dependsOn != null && dependsOn.Count != 0)
            {
                ValidateDependsOnId(dependsOn, atomicityGroupId, id);
                dependsOnReqIds = atomicGroups.GetFlattenedMessageIds(dependsOn);
            }

            // header
            ODataBatchOperationHeaders headers =
                (ODataBatchOperationHeaders)this.messagePropertiesCache.GetPropertyValue(
                    ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameHeaders);

            // Add the atomicityGroup request header.
            if (atomicityGroupId != null)
            {
                headers.Add(ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameAtomicityGroup, atomicityGroupId);
            }

            // body. Use empty stream when request body is not present.
            Stream bodyContentStream =
                (Stream)this.messagePropertiesCache.GetPropertyValue(
                    ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameBody)
                ?? new ODataJsonLightBatchBodyContentReaderStream(this);

            // method. Support case-insensitive value of HTTP methods.
            string httpMethod = (string)this.messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameMethod);

            ValidateRequiredProperty(httpMethod, ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameMethod);

            httpMethod = httpMethod.ToUpperInvariant();

            // url
            string url = (string)this.messagePropertiesCache.GetPropertyValue(
                ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameUrl);

            ValidateRequiredProperty(url, ODataJsonLightBatchPayloadItemPropertiesCache.PropertyNameUrl);

            // escape any colons in the query string portion of the url
            int queryOptionSeparator = url.IndexOf('?');
            int firstColon           = url.IndexOf(':');

            if (queryOptionSeparator > 0 && firstColon > 0 && queryOptionSeparator < firstColon)
            {
                url = url.Substring(0, queryOptionSeparator) + url.Substring(queryOptionSeparator).Replace(":", "%3A");
            }

            Uri requestUri = new Uri(url, UriKind.RelativeOrAbsolute);

            // Reset the request property cache since all data in cache has been processed.
            // So that new instance can be created during subsequent read in operation state.
            this.messagePropertiesCache = null;

            ODataBatchOperationRequestMessage requestMessage = BuildOperationRequestMessage(
                () => bodyContentStream,
                httpMethod,
                requestUri,
                headers,
                id,
                atomicityGroupId,
                dependsOnReqIds,
                /*dependsOnIdsValidationRequired*/ true);

            return(requestMessage);
        }