[mapguide-commits] r9121 - in trunk/Tools/Maestro: OSGeo.MapGuide.MaestroAPI OSGeo.MapGuide.MaestroAPI/Resource/Validation OSGeo.MapGuide.MaestroAPI/Schema OSGeo.MapGuide.MaestroAPI.Tests

svn_mapguide at osgeo.org svn_mapguide at osgeo.org
Tue Jan 24 04:41:11 PST 2017


Author: jng
Date: 2017-01-24 04:41:11 -0800 (Tue, 24 Jan 2017)
New Revision: 9121

Modified:
   trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI.Tests/ValidationTests.cs
   trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/FeatureSourceValidator.cs
   trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/ValidationStatusCode.cs
   trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Schema/GeometricPropertyDefinition.cs
   trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.Designer.cs
   trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.resx
Log:
Add some validation rules for ODBC configuration documents, which includes checking for incomplete X/Y column mappings

Fixes #2476

Modified: trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/FeatureSourceValidator.cs
===================================================================
--- trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/FeatureSourceValidator.cs	2017-01-20 11:23:01 UTC (rev 9120)
+++ trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/FeatureSourceValidator.cs	2017-01-24 12:41:11 UTC (rev 9121)
@@ -21,12 +21,15 @@
 #endregion Disclaimer / License
 
 using OSGeo.MapGuide.MaestroAPI.Exceptions;
+using OSGeo.MapGuide.MaestroAPI.Schema;
+using OSGeo.MapGuide.MaestroAPI.SchemaOverrides;
 using OSGeo.MapGuide.MaestroAPI.Services;
 using OSGeo.MapGuide.ObjectModels;
 using OSGeo.MapGuide.ObjectModels.Common;
 using OSGeo.MapGuide.ObjectModels.FeatureSource;
 using System;
 using System.Collections.Generic;
+using System.Linq;
 
 namespace OSGeo.MapGuide.MaestroAPI.Resource.Validation
 {
@@ -219,11 +222,58 @@
                 }
             }
 
+            string configDocXml = feature.GetConfigurationContent(Connection);
+            if (!string.IsNullOrEmpty(configDocXml))
+            {
+                var doc = ConfigurationDocument.LoadXml(configDocXml);
+                var odbcDoc = doc as OdbcConfigurationDocument;
+                if (odbcDoc != null)
+                {
+                    issues.AddRange(ValidateOdbcDoc(feature, odbcDoc));
+                }
+            }
+
             context.MarkValidated(resource.ResourceID);
 
             return issues.ToArray();
         }
 
+        private IEnumerable<ValidationIssue> ValidateOdbcDoc(IFeatureSource fs, OdbcConfigurationDocument odbcDoc)
+        {
+            foreach (var schema in odbcDoc.Schemas)
+            {
+                var featureClasses = schema
+                    .Classes
+                    .Where(c => !string.IsNullOrEmpty(c.DefaultGeometryPropertyName) && c.Properties.Any(p => p.Type == Schema.PropertyDefinitionType.Geometry && p.Name == c.DefaultGeometryPropertyName));
+
+                foreach (var fc in featureClasses)
+                {
+                    var geomProp = fc.Properties.OfType<GeometricPropertyDefinition>().FirstOrDefault(p => p.Name == fc.DefaultGeometryPropertyName);
+                    if (geomProp != null)
+                    {
+                        //Must be point
+                        if (geomProp.GeometricTypes != FeatureGeometricType.Point)
+                        {
+                            yield return new ValidationIssue(fs, ValidationStatus.Error, ValidationStatusCode.Error_OdbcConfig_InvalidLogicalGeometryProperty, string.Format(Strings.ODBC_InvalidGeometryProperty, fc.Name, geomProp.Name));
+                        }
+
+                        var ovTable = odbcDoc.GetOverride(schema.Name, fc.Name);
+                        if (ovTable == null) //If it has geometry, it must have a table override
+                        {
+                            yield return new ValidationIssue(fs, ValidationStatus.Error, ValidationStatusCode.Error_OdbcConfig_NoTableOverrideForFeatureClass, string.Format(Strings.ODBC_NoSuchTableOverrideForFeatureClass, fc.Name));
+                        }
+                        else if (geomProp.GeometricTypes == FeatureGeometricType.Point)
+                        {
+                            if (string.IsNullOrEmpty(ovTable.XColumn) || string.IsNullOrEmpty(ovTable.YColumn))
+                            {
+                                yield return new ValidationIssue(fs, ValidationStatus.Error, ValidationStatusCode.Error_OdbcConfig_IncompleteXYZColumnMapping, string.Format(Strings.ODBC_IncompleteXYZColumnMapping, fc.Name));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
         /// <summary>
         /// Gets the resource type and version this validator supports
         /// </summary>

Modified: trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/ValidationStatusCode.cs
===================================================================
--- trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/ValidationStatusCode.cs	2017-01-20 11:23:01 UTC (rev 9120)
+++ trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Resource/Validation/ValidationStatusCode.cs	2017-01-24 12:41:11 UTC (rev 9121)
@@ -474,6 +474,25 @@
         /// </summary>
         Error_SymbolDefinition_ImageGraphicReferenceResourceDataNotFound,
 
+        /// <summary>
+        /// The ODBC configuration document has a Feature Class that has no corresponding ODBC
+        /// table override definition (that would specify how its geometry property maps to X/Y/Z
+        /// columns)
+        /// </summary>
+        Error_OdbcConfig_NoTableOverrideForFeatureClass = 5801,
+
+        /// <summary>
+        /// The ODBC configuration document contains a logical feature class with a geometry
+        /// property that is not of type Point (other geometry types are not mappable in ODBC)
+        /// </summary>
+        Error_OdbcConfig_InvalidLogicalGeometryProperty,
+
+        /// <summary>
+        /// The ODBC configuration document contains a table override item with an incomplete X or Y column
+        /// mapping
+        /// </summary>
+        Error_OdbcConfig_IncompleteXYZColumnMapping
+
         #endregion errors
     }
 }
\ No newline at end of file

Modified: trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Schema/GeometricPropertyDefinition.cs
===================================================================
--- trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Schema/GeometricPropertyDefinition.cs	2017-01-20 11:23:01 UTC (rev 9120)
+++ trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Schema/GeometricPropertyDefinition.cs	2017-01-24 12:41:11 UTC (rev 9121)
@@ -324,7 +324,7 @@
             var hev = Utility.GetFdoAttribute(node, "hasElevation"); //NOXLATE
             var srs = Utility.GetFdoAttribute(node, "srsName"); //NOXLATE
 
-            this.GeometricTypes = ProcessGeometricTypes(gt.Value);
+            this.GeometricTypes = ProcessGeometricTypes(gt2.Value);
             if (gt2 != null)
                 this.SpecificGeometryTypes = ProcessSpecificGeometryTypes(gt2.Value);
 

Modified: trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.Designer.cs
===================================================================
--- trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.Designer.cs	2017-01-20 11:23:01 UTC (rev 9120)
+++ trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.Designer.cs	2017-01-24 12:41:11 UTC (rev 9121)
@@ -1,17 +1,17 @@
 //------------------------------------------------------------------------------
 // <auto-generated>
 //     This code was generated by a tool.
-//     Runtime Version:4.0.30319.34209
+//     Runtime Version:4.0.30319.42000
 //
 //     Changes to this file may cause incorrect behavior and will be lost if
 //     the code is regenerated.
 // </auto-generated>
 //------------------------------------------------------------------------------
 
-namespace OSGeo.MapGuide.MaestroAPI
-{
-
-
+namespace OSGeo.MapGuide.MaestroAPI {
+    using System;
+    
+    
     /// <summary>
     ///   A strongly-typed resource class, for looking up localized strings, etc.
     /// </summary>
@@ -1987,6 +1987,33 @@
         }
         
         /// <summary>
+        ///   Looks up a localized string similar to Table mapping for Feature class ({0}) has an incomplete column mapping. Either the X or Y or both columns are not specified.
+        /// </summary>
+        public static string ODBC_IncompleteXYZColumnMapping {
+            get {
+                return ResourceManager.GetString("ODBC_IncompleteXYZColumnMapping", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Feature class ({0}) has a logical geometry property ({1}) that is not of the Point geometry type.
+        /// </summary>
+        public static string ODBC_InvalidGeometryProperty {
+            get {
+                return ResourceManager.GetString("ODBC_InvalidGeometryProperty", resourceCulture);
+            }
+        }
+        
+        /// <summary>
+        ///   Looks up a localized string similar to Feature class ({0}) has no corresponding ODBC table override item.
+        /// </summary>
+        public static string ODBC_NoSuchTableOverrideForFeatureClass {
+            get {
+                return ResourceManager.GetString("ODBC_NoSuchTableOverrideForFeatureClass", resourceCulture);
+            }
+        }
+        
+        /// <summary>
         ///   Looks up a localized string similar to All Files.
         /// </summary>
         public static string PickAll {

Modified: trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.resx
===================================================================
--- trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.resx	2017-01-20 11:23:01 UTC (rev 9120)
+++ trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI/Strings.resx	2017-01-24 12:41:11 UTC (rev 9121)
@@ -894,4 +894,13 @@
   <data name="UnseedableTileSet" xml:space="preserve">
     <value>The given Map Definition or Tile Set Definition cannot be seeded</value>
   </data>
+  <data name="ODBC_InvalidGeometryProperty" xml:space="preserve">
+    <value>Feature class ({0}) has a logical geometry property ({1}) that is not of the Point geometry type</value>
+  </data>
+  <data name="ODBC_NoSuchTableOverrideForFeatureClass" xml:space="preserve">
+    <value>Feature class ({0}) has no corresponding ODBC table override item</value>
+  </data>
+  <data name="ODBC_IncompleteXYZColumnMapping" xml:space="preserve">
+    <value>Table mapping for Feature class ({0}) has an incomplete column mapping. Either the X or Y or both columns are not specified</value>
+  </data>
 </root>
\ No newline at end of file

Modified: trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI.Tests/ValidationTests.cs
===================================================================
--- trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI.Tests/ValidationTests.cs	2017-01-20 11:23:01 UTC (rev 9120)
+++ trunk/Tools/Maestro/OSGeo.MapGuide.MaestroAPI.Tests/ValidationTests.cs	2017-01-24 12:41:11 UTC (rev 9121)
@@ -24,12 +24,16 @@
 using OSGeo.MapGuide.MaestroAPI.Resource;
 using OSGeo.MapGuide.MaestroAPI.Resource.Validation;
 using OSGeo.MapGuide.MaestroAPI.Schema;
+using OSGeo.MapGuide.MaestroAPI.SchemaOverrides;
 using OSGeo.MapGuide.MaestroAPI.Services;
 using OSGeo.MapGuide.ObjectModels;
+using OSGeo.MapGuide.ObjectModels.Common;
+using OSGeo.MapGuide.ObjectModels.FeatureSource;
 using OSGeo.MapGuide.ObjectModels.LayerDefinition;
 using OSGeo.MapGuide.ObjectModels.LoadProcedure;
 using System;
 using System.Collections.Generic;
+using System.IO;
 using System.Linq;
 using System.Text;
 using System.Xml;
@@ -243,6 +247,8 @@
         [Test]
         public void TestCase1896()
         {
+            //This test case is for ticket 1896: Maestro layer validation incorrectly report missing primary key
+
             var fs = new FeatureSchema();
             var doc = new XmlDocument();
 
@@ -263,5 +269,170 @@
                 Assert.True(cls.IdentityProperties.Count > 0, "Expected identity properties in: " + cls.QualifiedName);
             }
         }
+
+        [Test]
+        public void TestCase_FsValidator_ValidatesOdbcConfigurationIssues()
+        {
+            var conf = new OdbcConfigurationDocument();
+
+            var sc = new FdoSpatialContextListSpatialContext();
+            sc.CoordinateSystemName = "LL84";
+            sc.CoordinateSystemWkt = "";
+            sc.Description = "Default Spatial Context";
+            sc.Extent = new FdoSpatialContextListSpatialContextExtent()
+            {
+                LowerLeftCoordinate = new FdoSpatialContextListSpatialContextExtentLowerLeftCoordinate()
+                {
+                    X = "-180.0",
+                    Y = "-180.0"
+                },
+                UpperRightCoordinate = new FdoSpatialContextListSpatialContextExtentUpperRightCoordinate()
+                {
+                    X = "180.0",
+                    Y = "180.0"
+                }
+            };
+            sc.ExtentType = FdoSpatialContextListSpatialContextExtentType.Static;
+            sc.Name = "Default";
+            sc.XYTolerance = 0.0001;
+            sc.ZTolerance = 0.0001;
+
+            var schema = new FeatureSchema("Default", "Default Schema");
+            var klass = new ClassDefinition("Test", "Test Class");
+            var id = new DataPropertyDefinition("ID", "Identity");
+            var geom = new GeometricPropertyDefinition("Geometry", "geometry")
+            {
+                GeometricTypes = FeatureGeometricType.Point
+            };
+            geom.SpatialContextAssociation = sc.Name;
+            klass.AddProperty(id, true);
+            klass.AddProperty(geom);
+            klass.DefaultGeometryPropertyName = geom.Name;
+            schema.AddClass(klass);
+
+            conf.AddSchema(schema);
+            conf.AddSpatialContext(sc);
+
+            var odbcTable = new OdbcTableItem()
+            {
+                ClassName = klass.Name,
+                SchemaName = schema.Name,
+                SpatialContextName = sc.Name,
+                XColumn = "X",
+                YColumn = null
+            };
+            conf.AddOverride(odbcTable);
+
+            Func<Stream> xmlStreamFunc = () => new MemoryStream(Encoding.UTF8.GetBytes(conf.ToXml()));
+
+            var mockConn = new Mock<IServerConnection>();
+            var mockFeatSvc = new Mock<IFeatureService>();
+            var mockResSvc = new Mock<IResourceService>();
+
+            var resId = "Library://Test.FeatureSource";
+            var dataName = "config.xml";
+
+            mockFeatSvc.Setup(fs => fs.TestConnection(It.Is<string>(arg => arg == resId))).Returns("true");
+            mockFeatSvc.Setup(fs => fs.GetSpatialContextInfo(It.Is<string>(arg => arg == resId), It.IsAny<bool>())).Returns(new FdoSpatialContextList
+            {
+                SpatialContext = new System.ComponentModel.BindingList<FdoSpatialContextListSpatialContext>()
+                {
+                    sc
+                }
+            });
+            mockFeatSvc.Setup(fs => fs.GetSchemas(It.Is<string>(arg => arg == resId))).Returns(new[] { schema.Name });
+
+            mockResSvc.Setup(rs => rs.GetResourceData(It.Is<string>(arg => arg == resId), It.Is<string>(arg => arg == dataName))).Returns(xmlStreamFunc);
+
+            mockConn.Setup(c => c.FeatureService).Returns(mockFeatSvc.Object);
+            mockConn.Setup(c => c.ResourceService).Returns(mockResSvc.Object);
+
+            var mockFs = new Mock<IFeatureSource>();
+            mockFs.Setup(fs => fs.ConfigurationDocument).Returns(dataName);
+            mockFs.Setup(fs => fs.ResourceID).Returns(resId);
+            mockFs.Setup(fs => fs.Extension).Returns(Enumerable.Empty<IFeatureSourceExtension>());
+            mockFs.Setup(fs => fs.ResourceType).Returns(ResourceTypes.FeatureSource.ToString());
+            mockFs.Setup(fs => fs.Provider).Returns("OSGeo.ODBC");
+            mockFs.Setup(fs => fs.Serialize()).Returns(string.Empty);
+
+            //XYZ column misconfigurations
+
+            var context = new ResourceValidationContext(mockConn.Object);
+            var validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            var issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(1, issues.Count());
+            Assert.AreEqual(ValidationStatusCode.Error_OdbcConfig_IncompleteXYZColumnMapping, issues.First().StatusCode);
+            
+            odbcTable.XColumn = null;
+            odbcTable.YColumn = "Y";
+
+            context = new ResourceValidationContext(mockConn.Object);
+            validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(1, issues.Count());
+            Assert.AreEqual(ValidationStatusCode.Error_OdbcConfig_IncompleteXYZColumnMapping, issues.First().StatusCode);
+
+            odbcTable.XColumn = null;
+            odbcTable.YColumn = "Y";
+            odbcTable.ZColumn = "Z";
+
+            context = new ResourceValidationContext(mockConn.Object);
+            validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(1, issues.Count());
+            Assert.AreEqual(ValidationStatusCode.Error_OdbcConfig_IncompleteXYZColumnMapping, issues.First().StatusCode);
+
+            odbcTable.XColumn = "X";
+            odbcTable.YColumn = null;
+            odbcTable.ZColumn = "Z";
+
+            context = new ResourceValidationContext(mockConn.Object);
+            validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(1, issues.Count());
+            Assert.AreEqual(ValidationStatusCode.Error_OdbcConfig_IncompleteXYZColumnMapping, issues.First().StatusCode);
+
+            odbcTable.XColumn = "X";
+            odbcTable.YColumn = "Y";
+
+            context = new ResourceValidationContext(mockConn.Object);
+            validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(0, issues.Count());
+
+            //Bogus mapping class target
+            odbcTable.ClassName = "IDontExist";
+
+            context = new ResourceValidationContext(mockConn.Object);
+            validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(1, issues.Count());
+            Assert.AreEqual(ValidationStatusCode.Error_OdbcConfig_NoTableOverrideForFeatureClass, issues.First().StatusCode);
+
+            //Bogus logical geometry property
+            odbcTable.ClassName = klass.Name;
+            geom.GeometricTypes = FeatureGeometricType.Surface;
+
+            context = new ResourceValidationContext(mockConn.Object);
+            validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(1, issues.Count());
+            Assert.AreEqual(ValidationStatusCode.Error_OdbcConfig_InvalidLogicalGeometryProperty, issues.First().StatusCode);
+
+            //All good
+            geom.GeometricTypes = FeatureGeometricType.Point;
+            context = new ResourceValidationContext(mockConn.Object);
+            validator = new FeatureSourceValidator();
+            validator.Connection = mockConn.Object;
+            issues = validator.Validate(context, mockFs.Object, false);
+            Assert.AreEqual(0, issues.Count());
+        }
     }
 }



More information about the mapguide-commits mailing list