Repository: ioos/compliance-checker
Pull request #740 (2020-02-14 19:11:07)

Adds validation for naming_authority
Checks that naming_authority is either a URL or a reversed DNS string.


Patch:
diff --git a/compliance_checker/ioos.py b/compliance_checker/ioos.py
index 5d683fb..58a26da 100644
--- a/compliance_checker/ioos.py
+++ b/compliance_checker/ioos.py
@@ -357,6 +357,18 @@ class IOOS1_2_ConventionsValidator(base.RegexValidator):
     validator_regex = r"\bIOOS-1.2\b"
     validator_fail_message = "{} must contain the string \"IOOS 1.2\""
 
+class NamingAuthorityValidator(base.UrlValidator):
+    """
+    Class to check for URL or reversed DNS strings contained within
+    naming_authority
+    """
+    validator_fail_msg = "{} should either be a URL or a reversed DNS name (e.g \"edu.ucar.unidata\")"
+
+    def validator_func(self, input_value):
+        return (super().validator_func(input_value) or
+                # check for reverse DNS strings
+                validators.domain(".".join(input_value.split(".")[::-1])))
+
 class IOOS1_2Check(IOOSNCCheck):
     """
     Class to implement the IOOS Metadata 1.2 Specification
@@ -414,7 +426,7 @@ class IOOS1_2Check(IOOSNCCheck):
             'id',
             ('infoUrl', base.UrlValidator),
             'license',
-            'naming_authority',
+            ('naming_authority', NamingAuthorityValidator),
             'platform',
             'platform_name',
             'platform_vocabulary',
@@ -507,12 +519,6 @@ class IOOS1_2Check(IOOSNCCheck):
 
         return self.cf1_7.check_standard_name(ds)
 
-    def check_feature_type(self, ds):
-        """
-        Wrapper for checking featureType global attribute using the CF module.
-        """
-        return self.cf1_7.check_feature_type(ds)
-
     def check_units(self, ds):
         """
         Wrapper to check units with the CF module.


Test patch:
diff --git a/compliance_checker/tests/test_ioos_profile.py b/compliance_checker/tests/test_ioos_profile.py
index 3f35916..5fb93dd 100644
--- a/compliance_checker/tests/test_ioos_profile.py
+++ b/compliance_checker/tests/test_ioos_profile.py
@@ -1,4 +1,5 @@
-from compliance_checker.ioos import IOOS0_1Check, IOOS1_1Check, IOOS1_2Check
+from compliance_checker.ioos import (IOOS0_1Check, IOOS1_1Check, IOOS1_2Check,
+                                     NamingAuthorityValidator)
 from compliance_checker.tests.resources import STATIC_FILES
 from compliance_checker.tests import BaseTestCase
 from compliance_checker.tests.helpers import MockTimeSeries, MockVariable
@@ -302,12 +303,12 @@ class TestIOOS1_2(BaseTestCase):
     '''
 
     def setUp(self):
-        self.ioos = IOOS1_2Check() 
+        self.ioos = IOOS1_2Check()
 
     def test_check_vars_have_attrs(self):
 
         # create geophysical variable
-        ds = MockTimeSeries() # time, lat, lon, depth 
+        ds = MockTimeSeries() # time, lat, lon, depth
         temp = ds.createVariable("temp", np.float64, dimensions=("time",))
 
         # should fail here
@@ -316,7 +317,7 @@ class TestIOOS1_2(BaseTestCase):
         self.assertLess(scored, out_of)
 
         # should pass
-        ds = MockTimeSeries() # time, lat, lon, depth 
+        ds = MockTimeSeries() # time, lat, lon, depth
         temp = ds.createVariable("temp", np.float64, fill_value=9999999999.) # _FillValue
         temp.setncattr("missing_value", 9999999999.)
         temp.setncattr("standard_name", "sea_surface_temperature")
@@ -352,7 +353,7 @@ class TestIOOS1_2(BaseTestCase):
         self.assertTrue(result.value)
 
     def test_check_gts_ingest(self):
-        ds = MockTimeSeries() # time, lat, lon, depth 
+        ds = MockTimeSeries() # time, lat, lon, depth
 
         # no gts_ingest, should pass
         results = self.ioos.check_gts_ingest(ds)
@@ -379,7 +380,7 @@ class TestIOOS1_2(BaseTestCase):
 
     def test_check_instrument_variables(self):
 
-        ds = MockTimeSeries() # time, lat, lon, depth 
+        ds = MockTimeSeries() # time, lat, lon, depth
 
         # no instrument variable, should pass
         results = self.ioos.check_instrument_variables(ds)
@@ -414,7 +415,7 @@ class TestIOOS1_2(BaseTestCase):
         self.assertLess(scored, out_of)
 
     def test_check_wmo_platform_code(self):
-        ds = MockTimeSeries() # time, lat, lon, depth 
+        ds = MockTimeSeries() # time, lat, lon, depth
 
         # no wmo_platform_code, pass
         result = self.ioos.check_wmo_platform_code(ds)
@@ -434,14 +435,14 @@ class TestIOOS1_2(BaseTestCase):
         ds.setncattr("wmo_platform_code", "abcd1")
         result = self.ioos.check_wmo_platform_code(ds)
         self.assertFalse(result.value)
-    
+
         # invalid length, fail
         ds.setncattr("wmo_platform_code", "123")
         result = self.ioos.check_wmo_platform_code(ds)
         self.assertFalse(result.value)
 
     def test_check_standard_name(self):
-        ds = MockTimeSeries() # time, lat, lon, depth 
+        ds = MockTimeSeries() # time, lat, lon, depth
 
         # no standard names
         results = self.ioos.check_standard_name(ds)
@@ -476,6 +477,23 @@ class TestIOOS1_2(BaseTestCase):
         scored, out_of, messages = get_results(results)
         self.assertEqual(scored, out_of)
 
+    def test_naming_authority_validation(self):
+        test_attr_name = "naming_authority"
+        validator = NamingAuthorityValidator()
+        # check URL - should pass
+        self.assertTrue(validator.validate(test_attr_name,
+                                           "https://ioos.us")[0])
+        # check reverse DNS - should pass
+        self.assertTrue(validator.validate(test_attr_name,
+                                           "edu.ucar.unidata")[0])
+        # email address is neither of the above, so should fail
+        bad_result = validator.validate(test_attr_name,
+                                        "webmaster.ioos.us@noaa.gov")
+        self.assertFalse(bad_result[0])
+        self.assertEqual(bad_result[1],
+                         "naming_authority should either be a URL or a "
+                         "reversed DNS name (e.g \"edu.ucar.unidata\")")
+
     def test_check_platform_cf_role(self):
         """
         Check that cf_role inside platform variables only allows certain