/// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;

            HttpStatusCode? statusCode1 = VerificationHelper.VerifyCanonicalFunction(context, CanonicalFunctionType.Supported, out passed, out info);
            if (true != passed)
            {
                if (info != null)
                {
                    info.SetDetailsName(this.Name);
                }
                return passed;
            }

            HttpStatusCode? statusCode2 = VerificationHelper.VerifyCanonicalFunction(context, CanonicalFunctionType.Unsupported, out passed, out info);
            passed = statusCode2 == HttpStatusCode.NotImplemented ? true : false;
            if (info != null)
            {
                info.SetDetailsName(this.Name);
            }

            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            info = null;
            List<ExtensionRuleResultDetail> details = new List<ExtensionRuleResultDetail>();
            ExtensionRuleViolationInfo info1 = null, info2 = null;

            bool? isVerifySortEntitiesOrderByAsc = VerificationHelper.VerifySortEntities(context, SortedType.ASC, out info1);
            bool? isVerifySortEntitiesOrderByDesc = VerificationHelper.VerifySortEntities(context, SortedType.DESC, out info2);
            if (info1 != null)
            {
                details.AddRange(info1.Details);
            }
            if (info2 != null)
            {
                details.AddRange(info2.Details);
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, details);
            info.SetDetailsName(this.Name);

            return true == isVerifySortEntitiesOrderByAsc && true == isVerifySortEntitiesOrderByDesc;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;

            Uri metadataServiceUrl = new Uri(context.Destination.AbsoluteUri.TrimEnd('/') + @"/$metadata" + @"/");
            var req = WebRequest.Create(metadataServiceUrl) as HttpWebRequest;
            Response response = WebHelper.Get(req, RuleEngineSetting.Instance().DefaultMaximumPayloadSize);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, metadataServiceUrl.AbsoluteUri, "GET", string.Empty, response);

            if (response != null && response.StatusCode == HttpStatusCode.OK && response.ResponsePayload.IsMetadata())
            {
                passed = true;
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The response is not the metadata service document. Please refer to section 15 of [OData-CSDL].";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;

            HttpStatusCode? statusCode1 = VerificationHelper.VerifySortEntities(context, SortedType.ASC, out passed, out info);
            if (info != null)
            {
                info.SetDetailsName(this.Name);
            }
            if (true != passed)
            {
                return passed;
            }

            HttpStatusCode? statusCode2 = VerificationHelper.VerifySortEntities(context, SortedType.DESC, out passed, out info);
            if (info != null)
            {
                info.SetDetailsName(this.Name);
            }

            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            var response = WebHelper.Get(context.Destination, string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, context.Destination.AbsoluteUri, "GET", StringHelper.MergeHeaders(string.Empty, context.RequestHeaders), response);

            if (response != null && response.StatusCode != null)
            {
                if (context.PayloadFormat == RuleEngine.PayloadFormat.JsonLight)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = "Get above URI with accept header 'application/json', the response is not JSON format.";
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = String.Format("No response returned from above URI with accept header 'application/json'.");
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;

            // Verify the lambda operator "any".
            HttpStatusCode? statusCode1 = VerificationHelper.VerifyLambdaOperators(context, LambdaOperatorType.Any, out passed, out info);

            if (true != passed)
            {
                if (info != null)
                {
                    info.SetDetailsName(this.Name);
                }

                return passed;
            }

            // Verify the lambda operator "all".
            HttpStatusCode? statusCode2 = VerificationHelper.VerifyLambdaOperators(context, LambdaOperatorType.All, out passed, out info);
            if (info != null)
            {
                info.SetDetailsName(this.Name);
            }

            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);

            var restrictions = AnnotationsHelper.GetChangeTracking(context.MetadataDocument, context.VocCapabilities);

            if (string.IsNullOrEmpty(restrictions.Item1) ||
                null == restrictions.Item2 || !restrictions.Item2.Any() ||
                null == restrictions.Item3 || !restrictions.Item3.Any())
            {
                detail.ErrorMessage = "Cannot find an entity-container or any entity-sets which support the change tracking function.";
                info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

                return passed;
            }

            string url = string.Format("{0}/{1}", context.ServiceBaseUri, restrictions.Item1);
            List<KeyValuePair<string, string>> requestHeaders = new List<KeyValuePair<string,string>>();

            foreach (KeyValuePair<string, string> kvp in context.RequestHeaders)
            {
                requestHeaders.Add(new KeyValuePair<string, string>(kvp.Key, kvp.Value));
            }

            // Add odata.track-changes preference in request header.
            KeyValuePair<string, string> prefer = new KeyValuePair<string, string>("Prefer", "odata.track-changes");
            requestHeaders.Add(prefer);
            Response response = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, requestHeaders);
            detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, requestHeaders), response);

            if (response != null && !string.IsNullOrEmpty(response.ResponseHeaders))
            {
                string preferHeader = response.ResponseHeaders.GetHeaderValue("Preference-Applied");

                if (string.IsNullOrEmpty(preferHeader))
                {
                    passed = false;
                    detail.ErrorMessage = "The response header returned by the service does not contain 'odata.track-changes', when request with the header 'Prefer:odata.track-changes'.";
                }
                else if (preferHeader.Contains("odata.track-changes"))
                {
                    passed = true;
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = "The service does not support Delta change tracking.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            string selectedName = string.Empty;
            string functionactionName = "forFunctionAction";

            // Use the XPath query language to access the metadata document and get all Namespace and Alias value.
            XElement metadata = XElement.Parse(context.MetadataDocument);
            string xpath = @"//*[local-name()='DataServices']/*[local-name()='Schema']";
            List<string> appropriateNamespace = MetadataHelper.GetPropertyValues(context, xpath, "Namespace");

            // Use the XPath query language to access the metadata document and get all Function names and all Action names.
            xpath = @"//*[local-name()='EntityContainer']/*[local-name()='FunctionImport']";
            List<string> functionImportNames = MetadataHelper.GetPropertyValues(context, xpath, "Name");

            xpath = @"//*[local-name()='EntityContainer']/*[local-name()='ActionImport']";
            List<string> actionImportNames = MetadataHelper.GetPropertyValues(context, xpath, "Name");

            if (!functionImportNames.Contains(functionactionName) && !actionImportNames.Contains(functionactionName))
            {
                selectedName = functionactionName;
            }
            else
            {
                selectedName = functionactionName + "New";
            }

            string url = string.Format("{0}/?$select={1}.{2}", context.Destination, appropriateNamespace[0], selectedName);
            var req = WebRequest.Create(url) as HttpWebRequest;
            var response = WebHelper.Get(req, RuleEngineSetting.Instance().DefaultMaximumPayloadSize);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, url, "GET", string.Empty, response);

            if (response != null && response.StatusCode != null)
            {
                if (response.StatusCode != HttpStatusCode.OK)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = "The OData service MUST fail any request that contains actions or functions that it does not understand.";
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = String.Format("No response returned from above URI.");
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return passed;
        }
        /// <summary>
        /// Verifies whether the payload of current request session complies to the specified RelaxNG schema or not
        /// </summary>
        /// <param name="context">Context object representing the current OData interop session</param>
        /// <param name="result">Output parameter of validation result</param>
        /// <returns>True if passed; false if failed</returns>
        public bool Verify(ServiceContext context, out TestResult result)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            return this.Verify(context.ResponsePayload, out result);
        }
 public SimpleJobPlanner(ServiceContext rootCtx, string formatHint, int maxPayloadSize, string category="core")
 {
     this.rootCtx = rootCtx;
     this.metaResource = rootCtx.DestinationBasePath + "/$metadata";
     this.acceptHeaderValue = formatHint.MapFormatToAcceptValue();
     this.maxPayloadSize = maxPayloadSize;
     this.category = category;
     var payloadFormat = this.rootCtx.ServiceDocument.GetFormatFromPayload();
     this.feeds = ContextHelper.GetFeeds(this.rootCtx.ServiceDocument, payloadFormat).ToArray();
 }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            bool? passed = null;
            info = null;
            HttpStatusCode? statusCode = VerificationHelper.VerifyCount(context, out passed, out info);
            if (info != null)
            {
                info.SetDetailsName(this.Name);
            }

            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;

            return passed;
        }
 /// <summary>
 /// Create a facade view of interop validation engine for the current context 
 /// </summary>
 /// <param name="context">the current request context object</param>
 /// <param name="resultProvider">consumer of validation results generated by interop rule engine</param>
 /// <param name="logger">logger object</param>
 /// <param name="ruleList">Pre-set rule list</param>
 public RuleEngineWrapper(ServiceContext context, IResultProvider resultProvider, ILogger logger, List<string> ruleList = null)
 {
     this.context = context;
     this.resultConsumer = resultProvider;
     this.logger = logger;
     this.rules = this.context.GetRules().ToList();
     if (null != ruleList && ruleList.Count > 0)
     {
         this.rules = (from r in this.rules where ruleList.Contains(r.Name) select r).ToList();
     }
     this.categories = (from r in this.rules select r.Category).Distinct().ToList();
 }
        /// <summary>
        /// Verifies the specified payload of interop request context against current regular expression rule
        /// </summary>
        /// <param name="context">interop request session whose payload is to be verified</param>
        /// <param name="result">output parameter of verification result</param>
        /// <returns>true if passed; false if failed</returns>
        /// <exception cref="ArgumentNullExption">Throws excpetion when context parameter is null</exception>
        /// <exception cref="ArgumentException">Throws exception when context payload is not of Json</exception>
        public bool Verify(ServiceContext context, out TestResult result)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (context.PayloadFormat != PayloadFormat.Json
                && context.PayloadFormat != PayloadFormat.JsonLight)
            {
                throw new ArgumentException(Resource.PayloadFormatUnexpected);
            }

            return this.Verify(context.ResponsePayload, out result);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;

            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name);
            detail.ErrorMessage = @"This negative rule is not verified.";
            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);

            return passed;
        }
        public new bool Verify(ServiceContext context, out TestResult result)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool passed  = this.Verify(context.ResponseHttpHeaders, out result);

            if (!passed)
            {
                result.TextInvolved = this.GetHeaderLineOfName(context.ResponseHttpHeaders);
            }

            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;

            var response = WebHelper.Get(context.Destination, Constants.AcceptHeaderJson, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, context.Destination.AbsoluteUri, "GET", StringHelper.MergeHeaders(Constants.AcceptHeaderJson, context.RequestHeaders), response);

            if (response != null && response.StatusCode != null)
            {
                // Extracts OData-Version value from ResponseHttpHeaders.
                string oDataVersion = context.ResponseHttpHeaders.GetHeaderValue(Constants.ODataVersion).TrimEnd(';');

                if (!string.IsNullOrEmpty(oDataVersion))
                {
                    float version = float.Parse(oDataVersion);

                    // The minimal OData version of conformance validation is 4.0.
                    if (version >= 4.0)
                    {
                        passed = true;
                    }
                    else
                    {
                        passed = false;
                        detail.ErrorMessage = string.Format(@"The OData version is {0} which should be not less than minimal version 4.0.", version);
                    }
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = @"Can not get the OData-Version header from response headers.";
                }
            }
            else
            {
                passed = null;
                detail.ErrorMessage = "Cannot get response from above URI.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;
            VerificationHelper.VerifySearch(context, out passed, out info);
            if (info != null)
            {
                info.SetDetailsName(this.Name);
            }

            return passed;
        }
        /// <summary>
        /// Substitudes macros in xslt instructions with corresponding property values of the current interop request context
        /// </summary>
        /// <param name="context">The interop request context</param>
        /// <param name="xslt">xslt instructions</param>
        /// <returns>xslt instructions with all the macroes substituded</returns>
        public static string Preprocess(ServiceContext context, string xslt)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (string.IsNullOrEmpty(xslt))
            {
                throw new ArgumentException(Resource.ArgumentNotNullOrEmpty, "xslt");
            }

            return xslt.Replace("$ENTITYTYPE$", context.EntityTypeShortName)
                .Replace("$NSENTITYTYPE$", context.EntityTypeFullName)
                .Replace("$URI$", context.DestinationBasePath)
                .Replace("$LSURI$", context.DestinationBaseLastSegment);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;

            string url = context.Destination.AbsoluteUri.TrimEnd('/') + @"/$metadata";
            var response = WebHelper.Get(new Uri(url), string.Empty, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, context.RequestHeaders);
            ExtensionRuleResultDetail detail = new ExtensionRuleResultDetail(this.Name, url, "GET", StringHelper.MergeHeaders(string.Empty, context.RequestHeaders), response);

            if (response != null && response.StatusCode != null)
            {
                // Get EntityContainer from metadata payload.
                string xpath = @"//*[local-name()='EntityContainer']";
                XElement metadata = XElement.Parse(response.ResponsePayload);
                IEnumerable<XElement> entityContainer = metadata.XPathSelectElements(xpath, ODataNamespaceManager.Instance);
                List<string> propNames = new List<string>();

                foreach (var prop in entityContainer)
                {
                    propNames.Add(prop.Attribute("Name").Value);
                }

                if (propNames.Count > 0)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail.ErrorMessage = "There is no EntityContainer in metadata document.";
                }
            }
            else
            {
                passed = false;
                detail.ErrorMessage = String.Format("No response returned from above URI.");
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail);
            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool svcDocResult = false;
            bool metadataResult = false;
            bool errorResponseResult = false;
            bool feedAndEntryResult = false;
            ExtensionRuleViolationInfo infoForOne = null;
            List<ExtensionRuleResultDetail> details = new List<ExtensionRuleResultDetail>();

            svcDocResult = VerificationHelper.VerifySvcDoc(context, out infoForOne);
            if (infoForOne != null)
            {
                details.AddRange(infoForOne.Details);
            }

            metadataResult = VerificationHelper.VerifyMetadata(context, out infoForOne);
            if (infoForOne != null)
            {
                details.AddRange(infoForOne.Details);
            }

            errorResponseResult = VerificationHelper.VerifyError(context, out infoForOne);
            if (infoForOne != null)
            {
                details.AddRange(infoForOne.Details);
            }

            feedAndEntryResult = VerificationHelper.VerifyFeedAndEntry(context, out infoForOne);
            if (infoForOne != null)
            {
                details.AddRange(infoForOne.Details);
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, details);
            info.SetDetailsName(this.Name);

            return svcDocResult && metadataResult && feedAndEntryResult && errorResponseResult;
        }
        /// <summary>
        /// Verifies the specified interop request context with the rule
        /// </summary>
        /// <param name="context">The interop request context</param>
        /// <param name="result">Output parameter of validation result</param>
        /// <returns>True if passed; false if failed</returns>
        public bool Verify(ServiceContext context, out TestResult result)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (context.PayloadFormat != PayloadFormat.Xml && context.PayloadFormat != PayloadFormat.Atom)
            {
                throw new ArgumentException(Resource.PayloadFormatUnexpected);
            }

            if (!context.HasMetadata)
            {
                throw new ArgumentException(Resource.MetadataUnavailable);
            }

            string xsltMaterialized = XsltRulePreprocessor.Preprocess(context, this.xslt);
            return XsltRngVerifier.Verify(xsltMaterialized, context.ResponsePayload, context.MetadataDocument, out result);
        }
        /// <summary>
        /// Verifies whether the specific interop request context pass the validation or not
        /// </summary>
        /// <param name="context">Current interop request context</param>
        /// <param name="result">Output parameter of TestResult object</param>
        /// <returns>True if passed; false if failed</returns>
        public bool Verify(ServiceContext context, out TestResult result)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? flagExec;
            ExtensionRuleViolationInfo info;
            flagExec = this.semanticExtensionVerifier(context, out info);
            bool passed = flagExec.HasValue ? flagExec.Value : true;
            result = new TestResult();

            if (info != null)
            {
                result.ErrorDetail = info.Message;
                result.LineNumberInError = info.PayloadLineNumberInError;
                if (info.Endpoint != null)
                {
                    result.TextInvolved = info.Endpoint.AbsoluteUri;
                }

                if (info.Details != null)
                {
                    result.Details = new List<ExtensionRuleResultDetail>();

                    foreach (ExtensionRuleResultDetail detail in info.Details)
                    {
                        result.Details.Add(detail.Clone());
                    }
                }
            }

            if (!flagExec.HasValue)
            {
                result.Classification = Constants.ClassificationNotApplicable;
            }

            return passed;
        }
        /// <summary>
        /// Executes one rule to validate the current interop request contecxt
        /// </summary>
        /// <param name="context">the current interop request context</param>
        /// <param name="rule">the rule to be validated</param>
        /// <returns>TestResult object of the validation</returns>
        /// <exception cref="Exception">Throws various exception when rule engine encounters unrecoverable errors like bad dynamic rules</exception>
        public static TestResult ExecuteRule(ServiceContext context, Rule rule)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            if (rule == null)
            {
                throw new ArgumentNullException("rule");
            }

            bool passed = true;
            TestResult result;

            TestResult dummy;

            //Set the test service's current verified rule
            if (DataService.serviceInstance != null)
            {
                DataService.serviceInstance.SwitchRule(rule.Name);
            }

            if (rule.Condition == null || rule.Condition.Verify(context, out dummy))
            {
                passed = rule.Action.Verify(context, out result);
            }
            else
            {
                result = new TestResult();
            }

            result.JobId = context.JobId;
            result.SetProperties(rule, passed);

            return result;
        }
        /// <summary>
        /// Gets web response of Uri request with custom header DataServiceVersion
        /// </summary>
        /// <param name="uri">Uri of request</param>
        /// <param name="inJsonFormat">Flag of whether response in Json format is expected</param>
        /// <param name="headers">Headers sent in web request</param>
        /// <param name="maximumPayloadSize">Maximum size of response payload in byte to be received</param>
        /// <param name="ctx">The reference context whose request headers are carried over</param>
        /// <returns>Web response</returns>
        public static Response GetWithHeaders(Uri uri, bool inJsonFormat, IEnumerable<KeyValuePair<string, string>> headers, int maximumPayloadSize, ServiceContext context)
        {
            var req = WebRequest.Create(uri);
            var reqHttp = req as HttpWebRequest;

            if (context.RequestHeaders != null)
            {
                foreach (var head in context.RequestHeaders)
                {
                    reqHttp.Headers[head.Key] = head.Value;
                }
            }

            if (headers != null)
            {
                foreach (var head in headers)
                {
                    reqHttp.Headers[head.Key] = head.Value;
                }
            }

            string acceptHeaderValue = inJsonFormat ? Constants.AcceptHeaderJson : Constants.AcceptHeaderAtom;
            return WebResponseHelper.Get(reqHttp, acceptHeaderValue, maximumPayloadSize);
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;

            string url = string.Format("{0}/?$find=*", context.Destination);
            var req = WebRequest.Create(url) as HttpWebRequest;
            Response response = WebHelper.Get(req, RuleEngineSetting.Instance().DefaultMaximumPayloadSize);
            ExtensionRuleResultDetail detail1 = new ExtensionRuleResultDetail(this.Name, url, "GET", string.Empty, response);

            if (response != null && response.StatusCode != null)
            {

                if (response.StatusCode != HttpStatusCode.OK)
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    detail1.ErrorMessage = "Services SHOULD fail the above URI because it contains query options 'find' which services does not understand.";
                }
            }
            else
            {
                passed = false;
                detail1.ErrorMessage = "No response returned from above URI.";
            }

            info = new ExtensionRuleViolationInfo(context.Destination, context.ResponsePayload, detail1);
            return passed;
        }
        /// <summary>
        /// Verify Metadata.Core.4265
        /// </summary>
        /// <param name="context">Service context</param>
        /// <param name="info">out parameter to return violation information when rule fail</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;
            var metadata = XElement.Parse(context.MetadataDocument);
            string xPath = "//*[local-name()='Function']";
            var functionElems = metadata.XPathSelectElements(xPath, ODataNamespaceManager.Instance);
            if (null != functionElems && functionElems.Any())
            {
                foreach (var functionElem in functionElems)
                {
                    if (null != functionElem.Attribute("Name"))
                    {
                        string nameAttribVal = functionElem.GetAttributeValue("Name");
                        if (nameAttribVal.IsSimpleIdentifier())
                        {
                            passed = true;
                        }
                        else
                        {
                            passed = false;
                            break;
                        }
                    }
                    else
                    {
                        passed = false;
                        break;
                    }
                }
            }

            info = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);

            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;
            ServiceStatus serviceStatus = ServiceStatus.GetInstance();
            TermDocuments termDocs = TermDocuments.GetInstance();
            DataFactory dFactory = DataFactory.Instance();
            var detail1 = new ExtensionRuleResultDetail(this.Name);
            var detail2 = new ExtensionRuleResultDetail(this.Name);
            var detail3 = new ExtensionRuleResultDetail(this.Name);
            var detail4 = new ExtensionRuleResultDetail(this.Name);
            var detail5 = new ExtensionRuleResultDetail(this.Name);
            var detail6 = new ExtensionRuleResultDetail(this.Name);
            var entityType = MetadataHelper.GetConcurrencyEntityType(serviceStatus.MetadataDocument);
            if (null == entityType)
            {
                detail1.ErrorMessage = "Cannot find an entity which contains a concurrency property in the service.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            var normalPropNames = entityType.NormalProperties.Where(np => "Edm.String" == np.PropertyType && !np.IsKey).Select(np => np.PropertyName);
            if (normalPropNames.Any())
            {
                string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
                string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
                var additionalInfos = new List<AdditionalInfo>();
                var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, null, out additionalInfos);
                string reqDataStr = reqData.ToString();
                bool isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
                var resp = WebHelper.CreateEntity(url, reqData, isMediaType, ref additionalInfos);
                detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);

                if (HttpStatusCode.Created == resp.StatusCode)
                {
                    var entityId = additionalInfos.Last().EntityId;
                    var hasEtag = additionalInfos.Last().HasEtag;
                    if (!hasEtag)
                    {
                        detail1.ErrorMessage = "The new inserted entity does not contain an @odata.etag annotation.";
                        info = new ExtensionRuleViolationInfo(new Uri(entityId), resp.ResponsePayload, detail2);

                        return passed;
                    }

                    resp = WebHelper.GetEntity(entityId, serviceStatus.DefaultHeaders);
                    detail2 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, StringHelper.MergeHeaders(string.Empty, serviceStatus.DefaultHeaders), resp);
                    if (HttpStatusCode.OK == resp.StatusCode)
                    {
                        reqDataStr = dFactory.ConstructUpdatedEntityData(reqData, normalPropNames).ToString();
                        var header = new KeyValuePair<string, string>("If-Match", additionalInfos.Last().ODataEtag);
                        var headers = new List<KeyValuePair<string, string>>() { header };
                        resp = WebHelper.UpdateEntity(entityId, reqDataStr, HttpMethod.Patch, headers);
                        detail3 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Patch, StringHelper.MergeHeaders(string.Empty, headers), resp, string.Empty, reqDataStr);
                        if (HttpStatusCode.NoContent == resp.StatusCode)
                        {
                            resp = WebHelper.GetEntity(entityId, serviceStatus.DefaultHeaders);
                            detail4 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, StringHelper.MergeHeaders(string.Empty, serviceStatus.DefaultHeaders), resp);
                            if (HttpStatusCode.OK == resp.StatusCode)
                            {
                                resp = WebHelper.DeleteEntity(entityId, hasEtag);
                                detail5 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Delete, StringHelper.MergeHeaders(string.Empty, new List<KeyValuePair<string, string>>() { header }), resp);
                                if (HttpStatusCode.NoContent == resp.StatusCode)
                                {
                                    resp = WebHelper.GetEntity(entityId);
                                    detail6 = new ExtensionRuleResultDetail(this.Name, entityId, HttpMethod.Get, string.Empty, resp);
                                    if (HttpStatusCode.NotFound == resp.StatusCode)
                                    {
                                        passed = true;
                                    }
                                    else
                                    {
                                        passed = false;
                                        detail6.ErrorMessage = "It still can get the deleted entity from above URI.";
                                    }
                                }
                                else
                                {
                                    passed = false;
                                    detail5.ErrorMessage = "Delete entity failed.";
                                }
                            }
                            else
                            {
                                passed = false;
                                detail4.ErrorMessage = "Can not get the created entity from above URI.";
                            }
                        }
                        else
                        {
                            passed = false;
                            detail3.ErrorMessage = "Update entity failed.";
                        }
                    }
                }
                else
                {
                    passed = false;
                    detail1.ErrorMessage = "Created the new entity failed for above URI.";
                }
            }

            var details = new List<ExtensionRuleResultDetail>()
            {
                detail1, detail2, detail3, detail4, detail5, detail6
            }.RemoveNullableDetails();
            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            return passed;
        }
        /// <summary>
        /// Verifies the extension rule.
        /// </summary>
        /// <param name="context">The Interop service context</param>
        /// <param name="info">out parameter to return violation information when rule does not pass</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;
            ServiceStatus serviceStatus = ServiceStatus.GetInstance();
            TermDocuments termDocs = TermDocuments.GetInstance();
            DataFactory dFactory = DataFactory.Instance();
            ExtensionRuleResultDetail detail1 = new ExtensionRuleResultDetail(this.Name);
            ExtensionRuleResultDetail detail2 = new ExtensionRuleResultDetail(this.Name);
            List<string> keyPropertyTypes = new List<string>() { "Edm.Int32", "Edm.Int16", "Edm.Int64", "Edm.Guid", "Edm.String" };
            List<EntityTypeElement> entityTypeElements = MetadataHelper.GetEntityTypes(serviceStatus.MetadataDocument, 1, keyPropertyTypes, null, NavigationRoughType.None).ToList();

            if (null == entityTypeElements || 0 == entityTypeElements.Count())
            {
                detail1.ErrorMessage = "To verify this rule it expects an entity type with Int32/Int64/Int16/Guid/String key property, but there is no this entity type in metadata so can not verify this rule.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            // To get test entity which is deleteable and insertable
            EntityTypeElement entityType = null;
            foreach (var entityEle in entityTypeElements)
            {
                var matchEntity = entityEle.EntitySetName.GetRestrictions(serviceStatus.MetadataDocument, termDocs.VocCapabilitiesDoc,
                    new List<Func<string, string, string, List<NormalProperty>, List<NavigProperty>, bool>>()
                    {
                        AnnotationsHelper.GetInsertRestrictions, AnnotationsHelper.GetDeleteRestrictions
                    });

                if (!string.IsNullOrEmpty(matchEntity.Item1)
                     && matchEntity.Item2 != null && matchEntity.Item2.Any()
                     && matchEntity.Item3 != null && matchEntity.Item3.Any())
                {
                    entityType = entityEle;
                    break;
                }
            }

            if (null == entityType)
            {
                detail1.ErrorMessage = "To verify this rule it expects an entity type with deleteable and insertable restrictions, but there is no entity type in metadata which is insertable and deleteable.";
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            string entitySetUrl = entityType.EntitySetName.MapEntitySetNameToEntitySetURL();
            if (string.IsNullOrEmpty(entitySetUrl))
            {
                detail1.ErrorMessage = string.Format("Cannot find the entity-set URL which is matched with {0}.", entityType.EntityTypeShortName);
                info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, detail1);

                return passed;
            }

            string url = serviceStatus.RootURL.TrimEnd('/') + @"/" + entitySetUrl;
            var additionalInfos = new List<AdditionalInfo>();
            var reqData = dFactory.ConstructInsertedEntityData(entityType.EntitySetName, entityType.EntityTypeShortName, null, out additionalInfos);
            string reqDataStr = reqData.ToString();
            bool isMediaType = !string.IsNullOrEmpty(additionalInfos.Last().ODataMediaEtag);
            var resp = WebHelper.CreateEntity(url, reqData, isMediaType, ref additionalInfos);
            detail1 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Post, string.Empty, resp, string.Empty, reqDataStr);

            // If create successfully, the resource is updatable and deletable.
            if (resp.StatusCode.HasValue && resp.StatusCode == HttpStatusCode.Created)
            {
                // Get feed response.
                resp = WebHelper.Get(new Uri(url), Constants.V4AcceptHeaderJsonFullMetadata, RuleEngineSetting.Instance().DefaultMaximumPayloadSize, serviceStatus.DefaultHeaders);
                detail2 = new ExtensionRuleResultDetail(this.Name, url, HttpMethod.Get, StringHelper.MergeHeaders(Constants.V4AcceptHeaderJsonFullMetadata, serviceStatus.DefaultHeaders), resp);
                if (resp.StatusCode.HasValue && resp.StatusCode == HttpStatusCode.OK)
                {
                    JObject jo;
                    resp.ResponsePayload.TryToJObject(out jo);
                    var entries = JsonParserHelper.GetEntries(jo);
                    foreach (JObject entry in entries)
                    {
                        if (entry[Constants.V4OdataEditLink] == null)
                        {
                            passed = false;
                            detail2.ErrorMessage = "Not all entities from above URI contain edit links.";
                            break;
                        }
                    }

                    if (passed == null)
                    {
                        passed = true;
                    }
                }
                else
                {
                    passed = false;
                    detail2.ErrorMessage = "Get feed resource failed from above URI.";
                }

                // Restore the service.
                var resps = WebHelper.DeleteEntities(additionalInfos);
            }
            else
            {
                passed = null;
                detail1.ErrorMessage = "Created new entity failed, it is not an updatable resource and can not verify this rule.";
            }

            var details = new List<ExtensionRuleResultDetail>() { detail1, detail2 }.RemoveNullableDetails();
            info = new ExtensionRuleViolationInfo(new Uri(serviceStatus.RootURL), serviceStatus.ServiceDocument, details);

            return passed;
        }
        /// <summary>
        /// Verify Entry.Core.4634
        /// </summary>
        /// <param name="context">Service context</param>
        /// <param name="info">out paramater to return violation information when rule fail</param>
        /// <returns>true if rule passes; false otherwise</returns>
        public override bool? Verify(ServiceContext context, out ExtensionRuleViolationInfo info)
        {
            if (context == null)
            {
                throw new ArgumentNullException("context");
            }

            bool? passed = null;
            info = null;

            const string relAttribPrefixName = @"http://docs.oasis-open.org/odata/ns/related/";
            string xpath = string.Format(@"//*[local-name()='EntityType' and @Name='{0}']/*[local-name()='NavigationProperty']", context.EntityTypeShortName);
            XElement metadata = XElement.Parse(context.MetadataDocument);
            var navigProps = metadata.XPathSelectElements(xpath, ODataNamespaceManager.Instance);
            List<string> relURLFullNames = new List<string>();

            foreach (var np in navigProps)
            {
                relURLFullNames.Add(relAttribPrefixName + np.Attribute(@"Name").Value);
            }

            xpath = @"/atom:entry/atom:link[@type='application/atom+xml;type=entry' or @type='application/atom+xml;type=feed']";
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(context.ResponsePayload);
            XmlNodeList linkElements = xmlDoc.SelectNodes(xpath, ODataNamespaceManager.Instance);

            foreach (XmlNode le in linkElements)
            {
                if (relURLFullNames.Contains(le.Attributes["rel"].Value) && Uri.IsWellFormedUriString(le.Attributes["rel"].Value, UriKind.Absolute))
                {
                    passed = true;
                }
                else
                {
                    passed = false;
                    info = new ExtensionRuleViolationInfo(this.ErrorMessage, context.Destination, context.ResponsePayload);
                    break;
                }
            }

            return passed;
        }