public void UnionWithSchemaPrecedenceOfSchemaAndPipelineExtractorsHavingSchemaPropertyExtractorsToBeIgnored() { var schemaExtractors = new PropertyExtractorCollection( new PropertyExtractor(new XmlQualifiedName("prop1", "urn"), ExtractionMode.Ignore), new PropertyExtractor(new XmlQualifiedName("prop2", "urn"), ExtractionMode.Ignore), new ConstantExtractor(new XmlQualifiedName("cso-prop", "urn"), "constant", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("xso-prop", "urn"), "*/other-node", ExtractionMode.Write), new ConstantExtractor(new XmlQualifiedName("c-prop", "urn"), "constant", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("x-prop", "urn"), "*/other-node", ExtractionMode.Write)); var pipelineExtractors = new PropertyExtractorCollection( ExtractorPrecedence.Schema, new PropertyExtractor(new XmlQualifiedName("prop1", "urn"), ExtractionMode.Clear), new PropertyExtractor(new XmlQualifiedName("prop2", "urn"), ExtractionMode.Clear), new ConstantExtractor(new XmlQualifiedName("cpo-prop", "urn"), "constant", ExtractionMode.Promote), new XPathExtractor(new XmlQualifiedName("xpo-prop", "urn"), "*/other-node", ExtractionMode.Promote), new ConstantExtractor(new XmlQualifiedName("c-prop", "urn"), "constant", ExtractionMode.Promote), new XPathExtractor(new XmlQualifiedName("x-prop", "urn"), "*/other-node", ExtractionMode.Promote)); var expectedExtractors = schemaExtractors .Where(pe => pe.ExtractionMode != ExtractionMode.Ignore) .Concat( new PropertyExtractor[] { new ConstantExtractor(new XmlQualifiedName("cpo-prop", "urn"), "constant", ExtractionMode.Promote), new XPathExtractor(new XmlQualifiedName("xpo-prop", "urn"), "*/other-node", ExtractionMode.Promote) }); schemaExtractors.Union(pipelineExtractors).Should().BeEquivalentTo(expectedExtractors); }
public void WriteXml() { var xml = $"<s0:Properties xmlns:s0=\"{SchemaAnnotation.NAMESPACE}\" xmlns:s1=\"urn\">" + "<s1:Property1 xpath=\"*/some-node\" />" + "<s1:Property2 mode=\"promote\" xpath=\"*/other-node\" />" + "<s1:Property3 mode=\"promote\" value=\"constant\" />" + "<s1:Property4 mode=\"clear\" />" + "</s0:Properties>"; var builder = new StringBuilder(); using (var writer = XmlWriter.Create(builder, new XmlWriterSettings { OmitXmlDeclaration = true })) { var sut = new PropertyExtractorCollection( new XPathExtractor(new XmlQualifiedName("Property1", "urn"), "*/some-node", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("Property2", "urn"), "*/other-node", ExtractionMode.Promote), new ConstantExtractor(new XmlQualifiedName("Property3", "urn"), "constant", ExtractionMode.Promote), new PropertyExtractor(new XmlQualifiedName("Property4", "urn"), ExtractionMode.Clear)); sut.WriteXml(writer !); } builder.ToString().Should().Be(xml); }
public void ReadXmlWithoutProperties() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}' />"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); sut.ReadXml(reader); sut.Should().BeEmpty(); } }
public void ReadXmlForConstantExtractorThrowsWhenValueIsEmpty() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property1 value=''/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)).Should().Throw <ArgumentNullException>().Where(e => e.ParamName == "value"); } }
public void ReadXmlForConstantExtractorThrowsWhenModeAttributeIsInvalid() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property1 mode='demote' value='constant'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)).Should().Throw <ArgumentException>().WithMessage("ExtractionMode 'Demote' is not supported by ConstantExtractor.*"); } }
public void UnionWithSchemaPrecedenceOfSchemaAndEmptyPipelineExtractors() { var schemaExtractors = new PropertyExtractorCollection( new ConstantExtractor(new XmlQualifiedName("cso-prop", "urn"), "constant", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("xso-prop", "urn"), "*/other-node", ExtractionMode.Write), new ConstantExtractor(new XmlQualifiedName("c-prop", "urn"), "constant", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("x-prop", "urn"), "*/other-node", ExtractionMode.Write)); var pipelineExtractors = new PropertyExtractorCollection(ExtractorPrecedence.Schema); schemaExtractors.Union(pipelineExtractors).Should().BeEquivalentTo(schemaExtractors); }
public void ReadXmlForXPathExtractorWithPromotedAttribute() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property2 promoted='true' xpath='*/other-node'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); sut.ReadXml(reader); sut.Should().BeEquivalentTo(new XPathExtractor(new XmlQualifiedName("Property2", "urn"), "*/other-node", ExtractionMode.Promote)); } }
public void ReadXmlForConstantExtractor() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property1 value='constant'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); sut.ReadXml(reader); sut.Should().BeEquivalentTo(new ConstantExtractor(new XmlQualifiedName("Property1", "urn"), "constant", ExtractionMode.Write)); } }
public void UnionWithSchemaOnlyPrecedenceOfEmptySchemaAndPipelineExtractors() { var schemaExtractors = PropertyExtractorCollection.Empty; var pipelineExtractors = new PropertyExtractorCollection( ExtractorPrecedence.SchemaOnly, new ConstantExtractor(new XmlQualifiedName("cpo-prop", "urn"), "constant", ExtractionMode.Promote), new XPathExtractor(new XmlQualifiedName("xpo-prop", "urn"), "*/other-node", ExtractionMode.Promote), new ConstantExtractor(new XmlQualifiedName("c-prop", "urn"), "constant", ExtractionMode.Promote), new XPathExtractor(new XmlQualifiedName("x-prop", "urn"), "*/other-node", ExtractionMode.Promote)); schemaExtractors.Union(pipelineExtractors).Should().BeEquivalentTo(pipelineExtractors); }
public void ReadXmlForPropertyExtractorThrowsWhenPromotedAttributeIsPresent() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property1 promoted='true'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)) .Should().Throw <ConfigurationErrorsException>().WithMessage("ExtractionMode is missing for PropertyExtractor without a Value or an XPath.*"); } }
public void ReadXmlForExtractorPrecedence() { var xml = $"<san:Properties precedence='schemaOnly' xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property1 mode='clear'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); sut.ReadXml(reader); sut.Precedence.Should().Be(ExtractorPrecedence.SchemaOnly); } }
public void ReadXmlForQNameValueExtractorFallsBackOnXPathExtractorWhenQNameValueExtractionModeIsDefault() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property3 mode='promote' qnameValue='name' xpath='*/extra-node'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); sut.ReadXml(reader); sut.Should().BeEquivalentTo(new XPathExtractor(new XmlQualifiedName("Property3", "urn"), "*/extra-node", ExtractionMode.Promote)); } }
public void ReadXmlThrowsWhenRootElementNamespaceIsInvalid() { const string xml = "<san:Properties xmlns:s0='urn' xmlns:san='urn:schemas.stateless.be:biztalk:2012:12:extractors'><s0:PropertyName xpath='*'/></san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)) .Should().Throw <ConfigurationErrorsException>() .WithInnerException <XmlException>() .WithMessage($"Element 'Properties' with namespace name '{SchemaAnnotation.NAMESPACE}' was not found. Line 1, position 2."); } }
public void ConvertTo() { var xml = $"<s0:Properties xmlns:s0=\"{SchemaAnnotation.NAMESPACE}\" xmlns:s1=\"urn\">" + "<s1:Property1 xpath=\"*/some-node\" />" + "<s1:Property2 mode=\"promote\" xpath=\"*/other-node\" />" + "</s0:Properties>"; var sut = new PropertyExtractorCollectionConverter(); var extractorCollection = new PropertyExtractorCollection( new XPathExtractor(new XmlQualifiedName("Property1", "urn"), "*/some-node", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("Property2", "urn"), "*/other-node", ExtractionMode.Promote)); sut.ConvertTo(extractorCollection, typeof(string)).Should().Be(xml); }
public void ReadXmlForPropertyExtractorThrowsWhenModeAttributeIsInvalid() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property1 mode='promote'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)) .Should().Throw <ArgumentException>() .WithMessage("Invalid ExtractionMode, only Clear and Ignore are supported for PropertyExtractor without a Value or an XPath.*"); } }
public static PropertyExtractorCollection Deserialize(string xml) { if (xml.IsNullOrEmpty()) { return(PropertyExtractorCollection.Empty); } using (var reader = XmlReader.Create(new StringReader(xml), new XmlReaderSettings { IgnoreWhitespace = true, IgnoreComments = true })) { var collection = new PropertyExtractorCollection(); collection.ReadXml(reader); return(collection); } }
public void ReadXmlThrowsWhenDuplicatePropertyToExtract() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:PropertyName xpath='*'/><s0:PropertyName xpath='*'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)) .Should().Throw <ConfigurationErrorsException>() .WithInnerException <XmlException>() .WithMessage("The following properties are declared multiple times: [urn:PropertyName]."); } }
public void ReadXmlThrowsWhenPropertyToExtractHasNoNamespace() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<PropertyName xpath='*'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)) .Should().Throw <ConfigurationErrorsException>() .WithInnerException <XmlException>() .WithMessage("The following properties are not associated with the target namespace URI of some property schema: [PropertyName]."); } }
public static string Serialize(PropertyExtractorCollection extractors) { if (extractors == null || !extractors.Any()) { return(null); } using (var stringWriter = new StringWriter()) using (var writer = XmlWriter.Create(stringWriter, new XmlWriterSettings { OmitXmlDeclaration = true })) { extractors.WriteXml(writer); writer.Flush(); return(stringWriter.ToString()); } }
public void ReadXmlForXPathExtractorThrowsWhenXPathIsEmpty() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:PropertyName xpath=''/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); Invoking(() => sut.ReadXml(reader)) .Should().Throw <ConfigurationErrorsException>() .WithInnerException <XPathException>() // ReSharper disable once StringLiteralTypo .WithMessage("Bad Query string encoundered in XPath:*"); } }
public void WriteXmlForExtractorPrecedence() { var xml = $"<s0:Properties precedence=\"pipelineOnly\" xmlns:s0=\"{SchemaAnnotation.NAMESPACE}\" xmlns:s1=\"urn\">" + "<s1:Property1 xpath=\"*/some-node\" />" + "</s0:Properties>"; var builder = new StringBuilder(); using (var writer = XmlWriter.Create(builder, new XmlWriterSettings { OmitXmlDeclaration = true })) { var sut = new PropertyExtractorCollection( ExtractorPrecedence.PipelineOnly, new XPathExtractor(new XmlQualifiedName("Property1", "urn"), "*/some-node", ExtractionMode.Write)); sut.WriteXml(writer !); } builder.ToString().Should().Be(xml); }
/// <summary> /// Merges two sets of <see cref="PropertyExtractor"/>-derived extractors by honoring their <see cref="Precedence"/> and assuming that this <see /// cref="PropertyExtractorCollection"/> instance contains the <see cref="PropertyExtractor"/>s configured by XML schema annotations and that <see /// cref="PropertyExtractorCollection"/> being merged into contains the <see cref="PropertyExtractor"/>s configured by the pipeline. /// </summary> /// <param name="pipelineExtractors"> /// The <see cref="PropertyExtractor"/>s configured by the pipeline. /// </param> /// <returns> /// The result of the merge between two sets of <see cref="PropertyExtractor"/>-derived extractors. /// </returns> /// <remarks> /// <para> /// The merge algorithm proceeds as follows: /// <list type="bullet"> /// <item> /// The <see cref="Precedence"/> of the <see cref="PropertyExtractor"/>s configured by XML schema annotations is irrelevant and discarded, no matter /// what its value may be. In other words, only the <see cref="Precedence"/> of the <see cref="PropertyExtractor"/>s configured by the pipeline is /// actually relevant. If unspecified, the <see cref="Precedence"/> of the <see cref="PropertyExtractor"/>s configured by the pipeline will be assumed /// to be given to the <see cref="ExtractorPrecedence.Schema"/>. /// </item> /// <item> /// If the <see cref="Precedence"/> of the pipeline <see cref="PropertyExtractor"/>s is given to the <see cref="ExtractorPrecedence.Schema"/> then any /// <see cref="PropertyExtractor"/> <b>redefined</b> by the pipeline will be ignored. In other words, for these particular <see /// cref="PropertyExtractor"/>s <b>redefined</b> by the pipeline, only their configuration done by the schema annotations will be retained. The <see /// cref="PropertyExtractor"/>s defined either only by the annotations or only by the pipeline will of course be part of the resulting merge. /// </item> /// <item> /// If the <see cref="Precedence"/> of the pipeline <see cref="PropertyExtractor"/>s is set to <see cref="ExtractorPrecedence.SchemaOnly"/> then any /// <see cref="PropertyExtractor"/> defined by the pipeline will be ignored provided that there are <see cref="PropertyExtractor"/>s configured by XML /// schema annotations. In other words, if there are <see cref="PropertyExtractor"/>s configured by annotations then the merge operation will return /// only these and discard any <see cref="PropertyExtractor"/>s configured by the pipeline. But if there are no <see cref="PropertyExtractor"/>s /// configured by annotations then the merge operation will return only the <see cref="PropertyExtractor"/>s configured by the the pipeline. /// </item> /// <item> /// If the <see cref="Precedence"/> of the pipeline <see cref="PropertyExtractor"/>s is given to the <see cref="ExtractorPrecedence.Pipeline"/> then /// any <see cref="PropertyExtractor"/> <b>redefined</b> by the pipeline will have precedence over the one defined by the annotations. In other words, /// for these particular <see cref="PropertyExtractor"/>s <b>redefined</b> by the pipeline, only their configuration done by the pipeline will be /// retained. The <see cref="PropertyExtractor"/>s defined either only by the annotations or only by the pipeline will of course be part of the /// resulting merge. /// </item> /// <item> /// If the <see cref="Precedence"/> of the pipeline <see cref="PropertyExtractor"/>s is set to <see cref="ExtractorPrecedence.PipelineOnly"/> then any /// <see cref="PropertyExtractor"/> defined by XML schema annotations will be ignored provided that there are <see cref="PropertyExtractor"/>s /// configured by the pipeline. In other words, if there are <see cref="PropertyExtractor"/>s configured by the pipeline then the merge operation will /// return only these and discard any <see cref="PropertyExtractor"/>s configured by annotations. But if there are no <see cref="PropertyExtractor"/>s /// configured by the pipeline then the merge operation will return only the <see cref="PropertyExtractor"/>s configured by annotations. /// </item> /// <item> /// Notice that <see cref="PropertyExtractor"/>s whose <see cref="PropertyExtractor.ExtractionMode"/> is set to <see cref="ExtractionMode.Ignore"/> /// will be filtered out of the resulting set of <see cref="PropertyExtractor"/>s only after the merge operation have proceeded. This could therefore /// lead to interesting results if the <see cref="Precedence"/> of the pipeline's <see cref="PropertyExtractor"/>s is either set to <see /// cref="ExtractorPrecedence.Pipeline"/> or <see cref="ExtractorPrecedence.Schema"/>. /// </item> /// </list> /// </para> /// </remarks> public IEnumerable <PropertyExtractor> Union(PropertyExtractorCollection pipelineExtractors) { if (pipelineExtractors == null) { throw new ArgumentNullException(nameof(pipelineExtractors)); } // notice that Linq.Union enumerates first and second in that order and yields each element that has not // already been yielded; see https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.union var schemaExtractors = Extractors; var mergedExtractors = pipelineExtractors.Precedence switch { ExtractorPrecedence.Schema => schemaExtractors.Union(pipelineExtractors, _lambdaComparer), ExtractorPrecedence.SchemaOnly => schemaExtractors.Any() ? schemaExtractors : pipelineExtractors, ExtractorPrecedence.Pipeline => pipelineExtractors.Union(schemaExtractors, _lambdaComparer), ExtractorPrecedence.PipelineOnly => pipelineExtractors.Any() ? pipelineExtractors : schemaExtractors, _ => throw new InvalidOperationException($"Unknown ExtractorPrecedence value '{pipelineExtractors.Precedence}'.") }; // filter out extractors to be ignored return(mergedExtractors.Where(pe => pe.ExtractionMode != ExtractionMode.Ignore).ToArray()); }
public void ReadXml() { var xml = $"<san:Properties xmlns:s0='urn' xmlns:san='{SchemaAnnotation.NAMESPACE}'>" + "<s0:Property1 xpath='*/some-node'/>" + "<s0:Property2 promoted='true' xpath='*/other-node'/>" + "<s0:Property3 mode='write' value='constant'/>" + "<s0:Property4 mode='clear'/>" + "</san:Properties>"; using (var reader = XmlReader.Create(new StringReader(xml))) { var sut = new PropertyExtractorCollection(); sut.ReadXml(reader); sut.Should().BeEquivalentTo( new XPathExtractor(new XmlQualifiedName("Property1", "urn"), "*/some-node", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("Property2", "urn"), "*/other-node", ExtractionMode.Promote), new ConstantExtractor(new XmlQualifiedName("Property3", "urn"), "constant", ExtractionMode.Write), new PropertyExtractor(new XmlQualifiedName("Property4", "urn"), ExtractionMode.Clear) ); } }
public void UnionWithPipelinePrecedenceOfSchemaAndPipelineExtractors() { var schemaExtractors = new PropertyExtractorCollection( new ConstantExtractor(new XmlQualifiedName("cso-prop", "urn"), "constant", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("xso-prop", "urn"), "*/other-node", ExtractionMode.Write), new ConstantExtractor(new XmlQualifiedName("c-prop", "urn"), "constant", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("x-prop", "urn"), "*/other-node", ExtractionMode.Write)); var pipelineExtractors = new PropertyExtractorCollection( ExtractorPrecedence.Pipeline, new ConstantExtractor(new XmlQualifiedName("cpo-prop", "urn"), "constant", ExtractionMode.Promote), new XPathExtractor(new XmlQualifiedName("xpo-prop", "urn"), "*/other-node", ExtractionMode.Promote), new ConstantExtractor(new XmlQualifiedName("c-prop", "urn"), "constant", ExtractionMode.Promote), new XPathExtractor(new XmlQualifiedName("x-prop", "urn"), "*/other-node", ExtractionMode.Promote)); var expectedExtractors = pipelineExtractors.Concat( new PropertyExtractor[] { new ConstantExtractor(new XmlQualifiedName("cso-prop", "urn"), "constant", ExtractionMode.Write), new XPathExtractor(new XmlQualifiedName("xso-prop", "urn"), "*/other-node", ExtractionMode.Write) }); schemaExtractors.Union(pipelineExtractors).Should().BeEquivalentTo(expectedExtractors); }
public PropertyExtractorCollectionSerializerSurrogate(PropertyExtractorCollection extractors) : base(extractors?.Precedence ?? throw new ArgumentNullException(nameof(extractors)), extractors) { }