diff --git a/src/mapfile.py b/src/mapfile.py index bc7a1712ef4bbca3ea60dee1cc0bbf07d56a7023..702f4396ada5ccb4603676566d891b6d6ec6d6e4 100644 --- a/src/mapfile.py +++ b/src/mapfile.py @@ -94,14 +94,16 @@ class Layer(MetadataMixin): extent = self.ms.getExtent() return stores.Extent(extent.minx, extent.miny, extent.maxx, extent.maxy) - def get_fields(self, mf=None): + def get_fields(self): fields = self.get_metadata("gml_include_items", "") if fields == "all": # TODO: Get fields from feature type raise NotImplemented() + elif not fields: + return [] else: - fields = fields.split() + fields = fields.split(",") return fields def iter_fields(self): @@ -126,12 +128,30 @@ class Layer(MetadataMixin): # we need to rename it to something we are sure is not used. sld_layer_name = "__mra_tmp_template" + print new_sld + # This is realy ugly but etree has trouble with namespaces... - xmlsld = etree.fromstring(re.sub(' [a-zzA-Z]+:([a-zA-Z]+=")', ' \\1', new_sld)) - xmlsld.find("NamedLayer/Name").text = sld_layer_name - new_sld = etree.tostring(xmlsld) + # TODO: Do this again in an other way. + fixedxml = re.sub(' [a-zzA-Z0-9]+:([a-zA-Z0-9]+=")', ' \\1', new_sld) + xmlsld = etree.fromstring(fixedxml) + + # Still problems with namespaces... + for child in xmlsld.getchildren(): + if child.tag.endswith("NamedLayer"): + for gchild in child.getchildren(): + if gchild.tag.endswith("Name"): + gchild.text = sld_layer_name + break + else: + continue + break + else: + raise ValueError("Bad sld (No NamedLayer/Name)") + etree.register_namespace("", "http://www.opengis.net/sld") + new_sld = etree.tostring(xmlsld) + ms_template_layer = self.ms.clone() ms_template_layer.name = sld_layer_name mf.ms.insertLayer(ms_template_layer) @@ -307,7 +327,7 @@ class FeatureType(LayerModel): else: self.ms.connectiontype = mapscript.MS_SHAPEFILE url = urlparse.urlparse(cparam["url"]) - self.ms.data = url.path + self.ms.data = tools.get_resource_path(url.path) # TODO: strip extention. #else: @@ -324,6 +344,9 @@ class FeatureType(LayerModel): def configure_layer(self, layer, ws, enabled=True): + # TODO: We must also update all our personal attributes (type, ...) + # because we might not have been cloned. + layer.set_metadatas({ "wfs_name": layer.get_metadata("wms_name"), "wfs_title": layer.get_metadata("wms_title"), @@ -401,7 +424,7 @@ class Coverage(LayerModel): #if cparam["dbtype"] in ["tif", "tiff"]: self.ms.connectiontype = mapscript.MS_RASTER url = urlparse.urlparse(cparam["url"]) - self.ms.data = url.path + self.ms.data = tools.get_resource_path(url.path) # TODO: strip extention. #else: # raise ValueError("Unhandled type '%s'" % cparam["dbtype"]) @@ -417,6 +440,9 @@ class Coverage(LayerModel): def configure_layer(self, layer, ws, enabled=True): + # TODO: We must also update all our personal attributes (type, ...) + # because we might not have been cloned. + layer.set_metadatas({ "wfs_name": layer.get_metadata("wms_name"), "wfs_title": layer.get_metadata("wms_title"), diff --git a/src/server.py b/src/server.py index 028c3bf153761493f1a6d99704519ac1970a5733..9fb012511dc74a13fe15a13ed46926c49c8b4bc5 100755 --- a/src/server.py +++ b/src/server.py @@ -526,7 +526,7 @@ class styles(object): name = params.name if name == None: raise webapp.BadRequest(message="no parameter 'name' given.") - with webapp.mightConflict("style", mapfile=map_name): + with webapp.mightConflict(message="style {exception} already exists."): if name in tools.iter_styles(mf): raise webapp.KeyExists(name) @@ -623,7 +623,10 @@ class layers(object): else: raise webapp.BadRequest(message="Resource href is not handled by MRA.") - _, _, map_name, _, ws_name, st_type, st_name, r_type, r_name = path.split("/") + try: + _, map_name, _, ws_name, st_type, st_name, r_type, r_name = path.rsplit("/", 7) + except ValueError: + raise webapp.NotFound(message="ressource '%s' was not found." % path) r_name = r_name.rsplit(".", 1)[0] @@ -656,7 +659,7 @@ class layer(object): return {"layer" : ({ "name": l_name, "path": "/", - "type": "VECTOR", + "type": layer.get_type_name(), "defaultStyle": { "name": layer.ms.classgroup, "href": "%s/maps/%s/styles/%s.%s" % (web.ctx.home, map_name, layer.ms.classgroup, format), @@ -688,18 +691,23 @@ class layer(object): # This means we can have one mapfile for each workspace # and if eveything uses urls it should work *almost* as is. r_href = data["resource"]["href"] - _, _, map_name, _, ws_name, st_type, st_name, r_type, r_name = r_href.split("/") + try: + _, map_name, _, ws_name, st_type, st_name, r_type, r_name = r_href.rsplit("/", 7) + except ValueError: + raise webapp.NotFound(message="ressource '%s' was not found." % r_href) + r_name = r_name.rsplit(".", 1)[0] ws = mf.get_workspace(ws_name) with webapp.mightNotFound(r_type, workspace=ws_name): try: - model = ws.get_model(r_name, st_type, st_name) + model = ws.get_model(r_name, r_type[:-1], st_name) except ValueError: - webapp.NotFound("Invalid layer model '%s'" % st_type) + raise webapp.NotFound("Invalid layer model '%s'" % st_type) with webapp.mightNotFound("layer", mapfile=map_name): - model.configure_layer(ws, mf, l_name, l_enabled) + layer = mf.get_layer(l_name) + model.configure_layer(layer, ws, l_enabled) mf.save() @@ -735,7 +743,10 @@ class layerstyles(object): else: raise webapp.BadRequest(message="Resource href (%s) is not handled by MRA." % url.path) - _, _, map_name, _, s_name = path.split("/") + try: + _, map_name, _, s_name = path.rsplit("/", 3) + except ValueError: + raise webapp.NotFound(message="ressource '%s' was not found." % path) s_name = s_name.rsplit(".", 1)[0] @@ -774,10 +785,10 @@ class layerfields(object): with webapp.mightNotFound("layer", mapfile=map_name): layer = mf.get_layer(l_name) - return {"fields": [{ + return {"fields": [{ "name": layer.get_metadata("gml_%s_alias" % field, None), "fieldType": layer.get_metadata("gml_%s_type" % field, None), - } for field in layer.iter_fields(mf)] + } for field in layer.iter_fields()] } @@ -922,5 +933,5 @@ app = web.application(urls, globals()) if __name__ == "__main__": app.run() -u + application = app.wsgifunc() diff --git a/tests/testScenario.py b/tests/testScenario.py index 7756ae1d000ecb1666ae03cd73b89e8361962cf7..ce64d9e8735505db028cb0aaf30e1d6bb25ebd56 100644 --- a/tests/testScenario.py +++ b/tests/testScenario.py @@ -27,15 +27,9 @@ from utils import APIRequest import sys +import random - -def test_scenario(): - - target = "http://localhost:8080" - map_name = "tests" - - # Clean the test file, now we are sure it is empty. - APIRequest("PUT", target + "/tests/" + map_name) +def _test_workspaces(target, map_name, delete=True): # GET workspaces. @@ -62,7 +56,7 @@ def test_scenario(): name, title = "testDS1", "test datastore 1" _, r = APIRequest("POST", ws["dataStores"]["href"], {"dataStore":{"name":name, "title":title}}, get_response=True) - ds_link = r.getheader("Location").split(".", 1)[0] + ds_link = r.getheader("Location").rsplit(".", 1)[0] ds = APIRequest("GET", ds_link)["dataStore"] assert ds["name"] == name @@ -96,7 +90,7 @@ def test_scenario(): name, title = "timezones", "test feature type 1" _, r = APIRequest("POST", ds["href"], {"featureType":{"name":name, "title":title}}, get_response=True) - ft_link = r.getheader("Location").split(".", 1)[0] + ft_link = r.getheader("Location").rsplit(".", 1)[0] ft = APIRequest("GET", ft_link)["featureType"] assert ft["name"] == name @@ -112,13 +106,15 @@ def test_scenario(): # DELETE stuff - APIRequest("DELETE", ft_link) - fts = APIRequest("GET", ds_link + "/featuretypes")["featureTypes"] - assert len(fts) == 0 + if delete: - APIRequest("DELETE", ds_link) - dss = APIRequest("GET", ws["dataStores"]["href"])["dataStores"] - assert len(dss) == 0 + APIRequest("DELETE", ft_link) + fts = APIRequest("GET", ds_link + "/featuretypes")["featureTypes"] + assert len(fts) == 0 + + APIRequest("DELETE", ds_link) + dss = APIRequest("GET", ws["dataStores"]["href"])["dataStores"] + assert len(dss) == 0 @@ -136,7 +132,7 @@ def test_scenario(): name, title = "testCS1", "test coverageStore 1" _, r = APIRequest("POST", ws["coverageStores"]["href"], {"coverageStore":{"name":name, "title":title}}, get_response=True) - cs_link = r.getheader("Location").split(".", 1)[0] + cs_link = r.getheader("Location").rsplit(".", 1)[0] cs = APIRequest("GET", cs_link)["coverageStore"] assert cs["name"] == name @@ -170,27 +166,172 @@ def test_scenario(): name, title = "HYP_LR", "test coverage 1" _, r = APIRequest("POST", cs["href"], {"coverage":{"name":name, "title":title}}, get_response=True) - ft_link = r.getheader("Location").split(".", 1)[0] + c_link = r.getheader("Location").rsplit(".", 1)[0] - ft = APIRequest("GET", ft_link)["coverage"] + ft = APIRequest("GET", c_link)["coverage"] assert ft["name"] == name assert ft["title"] == title # PUT a coverage ft["title"] = title.upper() - APIRequest("PUT", ft_link, {"coverage":ft}) + APIRequest("PUT", c_link, {"coverage":ft}) - ft = APIRequest("GET", ft_link)["coverage"] + ft = APIRequest("GET", c_link)["coverage"] assert ft["title"] == title.upper() # DELETE stuff - APIRequest("DELETE", ft_link) - fts = APIRequest("GET", cs_link + "/coverages")["coverages"] - assert len(fts) == 0 - APIRequest("DELETE", cs_link) - css = APIRequest("GET", ws["coverageStores"]["href"])["coverageStores"] - assert len(css) == 0 + if delete: + + APIRequest("DELETE", c_link) + fts = APIRequest("GET", cs_link + "/coverages")["coverages"] + assert len(fts) == 0 + + APIRequest("DELETE", cs_link) + css = APIRequest("GET", ws["coverageStores"]["href"])["coverageStores"] + assert len(css) == 0 + + + return ws + +def _test_styles(target, map_name): + + # Lets DELETE all the styles. + styles = APIRequest("GET", target + "/maps/" + map_name + "/styles")["styles"] + for style in styles: + if style["name"].startswith("__test_"): + APIRequest("DELETE", style["href"]) + styles = APIRequest("GET", target + "/maps/" + map_name + "/styles")["styles"] + assert len([style for style in styles if style["name"].startswith("__test_")]) == 0 + + # Lets POST a style and GET it. + name = "__test_awesome_style_name" + data = open("./files/style.sld").read() + noise = "".join(map(str, random.sample(xrange(10000000), 60))) + # We add some noise, so we can check PUT later. + + styles = APIRequest("POST", target + "/maps/" + map_name + "/styles?name=" + name, + encode=None, content_type="application/vnd.ogc.sld+xml", data=data+noise) + + styles = APIRequest("GET", target + "/maps/" + map_name + "/styles")["styles"] + assert len(styles) == 1 + assert styles[0]["name"] == name + + st_link = styles[0]["href"].rsplit(".", 1)[0] + style = APIRequest("GET", st_link) + + content = APIRequest("GET", style["href"], encode=None, decode=None) + assert content == data+noise + + # Use PUT to remove the noise in the file. + styles = APIRequest("PUT", style["href"], encode=None, content_type="application/vnd.ogc.sld+xml", data=data) + content = APIRequest("GET", style["href"], encode=None, decode=None) + assert content == data + + +def _test_layers(target, map_name): + # We need to setup something to work with first. + + # GET workspaces. + wss = APIRequest("GET", target + "/maps/" + map_name + "/workspaces")["workspaces"] + ws = APIRequest("GET", wss[0]["href"])["workspace"] + # DataStores. + name, title = "testDS1", "test datastore 1" + _, r = APIRequest("POST", ws["dataStores"]["href"], {"dataStore":{"name":name, "title":title}}, + get_response=True) + ds_link = r.getheader("Location").rsplit(".", 1)[0] + # PUT file + APIRequest("PUT", ds_link + "/file.shp", open("./files/timezones_shp.zip", "rb"), + encode=None, content_type="application/zip") + ds = APIRequest("GET", ds_link)["dataStore"] + # POST a featuretype + name, title = "timezones", "test feature type 1" + _, r = APIRequest("POST", ds["href"], {"featureType":{"name":name, "title":title}}, + get_response=True) + ft_link = r.getheader("Location").rsplit(".", 1)[0] + # CoverageStores. + name, title = "testCS1", "test coverageStore 1" + _, r = APIRequest("POST", ws["coverageStores"]["href"], {"coverageStore":{"name":name, "title":title}}, + get_response=True) + cs_link = r.getheader("Location").rsplit(".", 1)[0] + # PUT file + APIRequest("PUT", cs_link + "/file.tif", open("./files/HYP_LR.zip", "rb"), + encode=None, content_type="application/zip") + cs = APIRequest("GET", cs_link)["coverageStore"] + # POST a coverage + name, title = "HYP_LR", "test coverage 1" + _, r = APIRequest("POST", cs["href"], {"coverage":{"name":name, "title":title}}, + get_response=True) + c_link = r.getheader("Location").rsplit(".", 1)[0] + + # + # OK, now the actual layer tests. + # + + layers = APIRequest("GET", target + "/maps/" + map_name + "/layers")["layers"] + assert len(layers) == 0 + + # A first layer for featuretype + + name = "FTlayerTest" + _, r = APIRequest("POST", target + "/maps/" + map_name + "/layers", + {"layer":{"name":name, "resource":{"href":ft_link}}}, + get_response=True) + ftl_link = r.getheader("Location").rsplit(".", 1)[0] + + # Check GET. + ftl = APIRequest("GET", ftl_link)["layer"] + assert ftl["name"] == name + assert ftl["type"] == "POLYGON" + + # check GET fields + fields = APIRequest("GET", ftl_link + "/fields")["fields"] + assert len(fields) == 15 + + # check GET layerstyles + styles = APIRequest("GET", ftl_link + "/styles")["styles"] + assert len(styles) == 1 + + APIRequest("POST", ftl_link + "/styles", {"style":{"resource":{"href":styles[0]["href"]}}}) + + # A second layer for coverage + + name = "ClayerTest" + _, r = APIRequest("POST", target + "/maps/" + map_name + "/layers", + {"layer":{"name":name, "resource":{"href":c_link}}}, + get_response=True) + ctl_link = r.getheader("Location").rsplit(".", 1)[0] + + # Check GET. + ctl = APIRequest("GET", ctl_link)["layer"] + assert ctl["name"] == name + assert ctl["type"] == "RASTER" + + # Check GET. + layers = APIRequest("GET", target + "/maps/" + map_name + "/layers")["layers"] + assert len(layers) == 2 + + # check GET fields + fields = APIRequest("GET", ctl_link + "/fields")["fields"] + assert len(fields) == 0 + + # check GET layerstyles + fields = APIRequest("GET", ctl_link + "/styles")["styles"] + assert len(fields) == 0 + + +def test_scenario(): + + target = "http://localhost:8080" + map_name = "tests" + + # Clean the test file, now we are sure it is empty. + APIRequest("PUT", target + "/tests/" + map_name) + + + # _test_workspaces(target, map_name) + # _test_styles(target, map_name) + _test_layers(target, map_name) diff --git a/tests/utils.py b/tests/utils.py index bffdfbd1940834f516288f37facaabb15cd2ffd0..73e4491dfb8f438946638228e88b8e454844f023 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -38,7 +38,8 @@ def deduce_content_type(type): return "application/xml" -def APIRequest(method, url, data=None, encode="json", decode="json", content_type=None, expected_type=None, get_response=False): +def APIRequest(method, url, data=None, encode="json", decode="json", content_type=None, expected_type=None, + get_response=False): if encode == "json": data = json.dumps(data) @@ -55,6 +56,9 @@ def APIRequest(method, url, data=None, encode="json", decode="json", content_typ else: url = surl.path + if surl.query: + url += "?" + surl.query + print >>sys.stderr, method, surl.geturl().replace(surl.path, url) conn = httplib.HTTPConnection(surl.hostname, surl.port) conn.request(method, url, body=data, headers={"Content-Type":content_type})