Exemple #1
0
def create_mapproxy_image(yaml_file, img_file):

    with open(yaml_file, 'rb') as f:
        yaml_text = yaml.load(f)

    captured = []

    # Inline function that accesses captured. Do not refactor out of this function.
    def start_response(status, headers, exc_info=None):
        captured[:] = [status, headers, exc_info]
        return output.append

    output = []
    bbox_req, lay_name = get_path_info_params(yaml_text)

    path_info = (
        '/service?LAYERS={0}&FORMAT=image%2Fpng&SRS=EPSG%3A4326'
        '&EXCEPTIONS=application%2Fvnd.ogc.se_inimage&TRANSPARENT=TRUE&SERVICE=WMS&VERSION=1.1.1&'
        'REQUEST=GetMap&STYLES=&BBOX={1}&WIDTH=200&HEIGHT=150').format(
            lay_name, bbox_req)

    conf_options = load_default_config()
    # Merge both
    load_config(conf_options, config_dict=yaml_text)
    conf = ProxyConfiguration(conf_options, seed=False, renderd=False)

    # Create a MapProxy App
    app = MapProxyApp(conf.configured_services(), conf.base_config)
    # Get a response from MapProxyAppy as if it was running standalone.
    environ = environ_from_url(path_info)
    app_iter = None

    try:
        app_iter = app(environ, start_response)
    except:
        return 1

    if app_iter is None:
        return 1

    try:
        with open(img_file, 'wb') as img:
            img.write(app_iter.next())
    except:
        return 1

    content = 'error'
    with open(img_file, 'rb') as img:
        content = img.read()

    if 'error' in content:
        os.remove(img_file)
        return 1

    return 0
def mapit(mapproxy_conf,
          layername,
          outputmimeformat,
          lonlatbbox,
          imagesize,
          outputfilename=None,
          time=None):
    """
    This automates the creation of a map image.
    :param mapproxy_conf: The mapproxy in-memory configuration dictionary.
    :param layername: The name of the layer to image.
    :param outputmimeformat: The mime format (i.e. image/png, image/jpeg) of the output
    :param lonlatbbox: The bounding box as a tuple, (west, south, east, north)
    :param imagesize: The images size as a tuple, (width, height)
    :param outputfilename: An optional output filename, if None, then no file will be written.
    :param time: An optional time string for layers that have the time dimension
    :return: The binary data for the image generated.
    """
    if time is None:
        time = ''
    mapproxy_conf = copy.deepcopy(mapproxy_conf)  # dup it for replacing time
    for source in mapproxy_conf['sources']:
        mapproxy_conf['sources'][source]['url'] = mapproxy_conf['sources'][
            source]['url'].replace("{Time}", time)

    transparent = 'false'
    if 'png' in outputmimeformat.lower():
        transparent = 'true'

    querystring = "LAYERS=%s&FORMAT=%s&SRS=EPSG:4326&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&TRANSPARENT=%s&STYLES=&BBOX=%f,%f,%f,%f&WIDTH=%d&HEIGHT=%d" % (
        layername, outputmimeformat, transparent, lonlatbbox[0], lonlatbbox[1],
        lonlatbbox[2], lonlatbbox[3], imagesize[0], imagesize[1])

    conf = ProxyConfiguration(mapproxy_conf,
                              conf_base_dir='',
                              seed=False,
                              renderd=False)

    services = conf.configured_services()

    myreq = {
        'QUERY_STRING': querystring,
        'SERVER_NAME': '',
        'SERVER_PORT': '',
        'wsgi.url_scheme': '',
    }

    response = services[0].handle(Request(myreq))
    data = response.data

    if outputfilename:
        f = open(outputfilename, "w")
        f.write(data)
    return data
Exemple #3
0
def get_mapproxy(layer, seed=False, ignore_warnings=True, renderd=False):
    """Creates a mapproxy config for a given layers
    """
    bbox = [
        float(layer.bbox_x0),
        float(layer.bbox_y0),
        float(layer.bbox_x1),
        float(layer.bbox_y1)
    ]

    url = str(layer.service.url)

    layer_name = simple_name(layer.name)

    srs = 'EPSG:4326'
    bbox_srs = 'EPSG:4326'
    grid_srs = 'EPSG:3857'

    if layer.type == 'ESRI:ArcGIS:MapServer' or layer.type == 'ESRI:ArcGIS:ImageServer':
        url = str(layer.service.url).split('?')[0] + 'WMSServer?'

        # blindly replace it with /arcgis/
        url = url.replace("/ArcGIS/rest/", "/arcgis/")
        # same for uppercase
        url = url.replace("/arcgis/rest/", "/arcgis/")
        # and for old versions
        url = url.replace("ArcX/rest/services", "arcx/services")
        # in uppercase or lowercase
        url = url.replace("arcx/rest/services", "arcx/services")

        srs = 'EPSG:3857'
        bbox_srs = 'EPSG:3857'

    if layer.type == 'Hypermap:WARPER':
        url = str(layer.url.replace("maps//wms", "maps/wms"))
        grid_srs = 'EPSG:900913'

    if layer.type == 'Hypermap:WorldMap':
        url = str(layer.url.replace("maps//wms", "maps/wms"))

    default_source = {
        'type': 'wms',
        'coverage': {
            'bbox': bbox,
            'srs': srs,
            'bbox_srs': bbox_srs,
            'supported_srs': ['EPSG:4326', 'EPSG:900913', 'EPSG:3857'],
        },
        'req': {
            'layers': layer_name,
            'url': url,
            'transparent': True,
        },
    }

    if layer.type == 'ESRI:ArcGIS:MapServer':
        default_source = {
            'type': 'tile',
            'url':
            str(layer.service.url).split('?')[0] + 'tile/%(z)s/%(y)s/%(x)s',
            'grid': 'default_grid',
            'transparent': True,
        }

    # A source is the WMS config
    sources = {'default_source': default_source}

    # A grid is where it will be projects (Mercator in our case)
    grids = {
        'default_grid': {
            'tile_size': [256, 256],
            'srs': grid_srs,
            'origin': 'nw',
        }
    }

    # A cache that does not store for now. It needs a grid and a source.
    caches = {
        'default_cache': {
            'cache': {
                'type':
                'file',
                'directory_layout':
                'tms',
                'directory':
                os.path.join(
                    tempfile.gettempdir(),
                    'mapproxy',
                    'layer',
                    '%s' % layer.id,
                    'map',
                    'wmts',
                    layer_name,
                    'default_grid',
                ),
            },
            'grids': ['default_grid'],
            'sources': ['default_source']
        },
    }

    # The layer is connected to the cache
    layers = [
        {
            'name': layer_name,
            'sources': ['default_cache'],
            'title': str(layer.title),
        },
    ]

    # Services expose all layers.
    # WMS is used for reprojecting
    # TMS is used for easy tiles
    # Demo is used to test our installation, may be disabled in final version
    services = {
        'wms': {
            'image_formats': ['image/png'],
            'md': {
                'abstract': 'This is the Harvard HyperMap Proxy.',
                'title': 'Harvard HyperMap Proxy'
            },
            'srs': ['EPSG:4326', 'EPSG:3857'],
            'versions': ['1.1.1']
        },
        'wmts': {
            'restful':
            True,
            'restful_template':
            '/{Layer}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png',
        },
        'tms': {
            'origin': 'nw',
        },
        'demo': None,
    }

    global_config = {
        'http': {
            'ssl_no_cert_checks': True
        },
    }

    # Start with a sane configuration using MapProxy's defaults
    conf_options = load_default_config()

    # Populate a dictionary with custom config changes
    extra_config = {
        'caches': caches,
        'grids': grids,
        'layers': layers,
        'services': services,
        'sources': sources,
        'globals': global_config,
    }

    yaml_config = yaml.dump(extra_config, default_flow_style=False)
    # If you want to test the resulting configuration. Turn on the next
    # line and use that to generate a yaml config.
    # assert False

    # Merge both
    load_config(conf_options, config_dict=extra_config)

    # Make sure the config is valid.
    errors, informal_only = validate_options(conf_options)
    for error in errors:
        log.warn(error)
    if not informal_only or (errors and not ignore_warnings):
        raise ConfigurationError('invalid configuration')

    errors = validate_references(conf_options)
    for error in errors:
        log.warn(error)

    conf = ProxyConfiguration(conf_options, seed=seed, renderd=renderd)

    # Create a MapProxy App
    app = MapProxyApp(conf.configured_services(), conf.base_config)

    # Wrap it in an object that allows to get requests by path as a string.
    return TestApp(app), yaml_config
Exemple #4
0
def create_mapproxy_app(slug: str, user: User = None) -> TestApp:
    mapproxy_config_key = get_mapproxy_config_template(slug, user=user)
    mapproxy_config = cache.get(mapproxy_config_key)
    conf_dict = cache.get_or_set(f"base-config-{slug}", lambda: get_conf_dict(slug), 360)
    if not mapproxy_config:
        # TODO: place this somewhere else consolidate settings.
        base_config: Dict[str, Any] = {
            "services": {
                "demo": None,
                "tms": None,
                "wmts": {
                    "featureinfo_formats": [
                        {"mimetype": "application/json", "suffix": "json"},
                        {"mimetype": "application/gml+xml; version=3.1", "suffix": "gml"},
                    ]
                },
            },
            # Cache based on slug so that the caches don't overwrite each other.
            "caches": {slug: {"cache": {"type": "file"}, "sources": ["default"], "grids": ["default"]}},
            "layers": [{"name": slug, "title": slug, "sources": [slug]}],
            "globals": {"cache": {"base_dir": getattr(settings, "TILE_CACHE_DIR")}},
        }
        if conf_dict["sources"].get("info"):
            base_config["caches"][slug]["sources"] += ["info"]
        if conf_dict["sources"].get("footprint"):
            base_config["caches"][get_footprint_layer_name(slug)] = {
                "cache": {"type": "file"},
                "sources": ["footprint"],
                "grids": ["default"],
            }
            base_config["layers"] += [
                {
                    "name": get_footprint_layer_name(slug),
                    "title": get_footprint_layer_name(slug),
                    "sources": [get_footprint_layer_name(slug)],
                }
            ]
        base_config, conf_dict = add_restricted_regions_to_config(base_config, conf_dict, slug, None)
        try:
            mapproxy_config = load_default_config()
            load_config(mapproxy_config, config_dict=base_config)
            load_config(mapproxy_config, config_dict=conf_dict)
            mapproxy_configuration = ProxyConfiguration(mapproxy_config)

            if settings.REGIONAL_JUSTIFICATION_TIMEOUT_DAYS:
                regional_justification_timeout = settings.REGIONAL_JUSTIFICATION_TIMEOUT_DAYS * 86400
            else:
                regional_justification_timeout = None
            mapproxy_configs_set = cache.get_or_set(mapproxy_config_keys_index, set())
            mapproxy_configs_set.add(mapproxy_config_key)
            cache.set(mapproxy_config_keys_index, mapproxy_configs_set)
            cache.set(mapproxy_config_key, mapproxy_config, regional_justification_timeout)

        except ConfigurationError as e:
            logger.error(e)
            raise
    else:
        try:
            mapproxy_configuration = ProxyConfiguration(mapproxy_config)
        except ConfigurationError as e:
            logger.error(e)
            raise

    cert_info = conf_dict.get("cert_info")
    auth_requests.patch_https(cert_info=cert_info)

    cred_var = conf_dict.get("cred_var")
    auth_requests.patch_mapproxy_opener_cache(slug=slug, cred_var=cred_var)

    app = MapProxyApp(mapproxy_configuration.configured_services(), mapproxy_config)

    return TestApp(app)
Exemple #5
0
def get_mapproxy(layer, seed=False, ignore_warnings=True, renderd=False):
    """Creates a mapproxy config for a given layer-like object.
       Compatible with django-registry and GeoNode.
    """
    bbox = list(wkt2geom(layer.wkt_geometry))

    # TODO: Check for correct url
    url = 'http://test.registry.org'
    # url = str(layer.service.url)

    layer_name = str(layer.title)

    srs = 'EPSG:4326'
    bbox_srs = 'EPSG:4326'
    grid_srs = 'EPSG:3857'

    default_source = {
        'type': 'wms',
        'coverage': {
            'bbox': bbox,
            'srs': srs,
            'bbox_srs': bbox_srs,
            'supported_srs': ['EPSG:4326', 'EPSG:900913', 'EPSG:3857'],
        },
        'req': {
            'layers': str(layer.title),
            'url': url,
            'transparent': True,
        },
    }

    if layer.type == 'ESRI:ArcGIS:MapServer' or layer.type == 'ESRI:ArcGIS:ImageServer':
        # blindly replace it with /arcgis/
        url = url.replace("/ArcGIS/rest/", "/arcgis/")
        # same for uppercase
        url = url.replace("/arcgis/rest/", "/arcgis/")
        # and for old versions
        url = url.replace("ArcX/rest/services", "arcx/services")
        # in uppercase or lowercase
        url = url.replace("arcx/rest/services", "arcx/services")

        srs = 'EPSG:3857'
        bbox_srs = 'EPSG:3857'

        default_source = {
            'type': 'arcgis',
            'req': {
                'url': url,
                'grid': 'default_grid',
                'transparent': True,
            },
        }

    # A source is the WMS config
    sources = {'default_source': default_source}

    # A grid is where it will be projects (Mercator in our case)
    grids = {
        'default_grid': {
            'tile_size': [256, 256],
            'srs': grid_srs,
            'origin': 'nw',
        }
    }

    # A cache that does not store for now. It needs a grid and a source.
    caches = {
        'default_cache': {
            'disable_storage': True,
            'grids': ['default_grid'],
            'sources': ['default_source']
        },
    }

    # The layer is connected to the cache
    layers = [
        {
            'name': layer_name,
            'sources': ['default_cache'],
            'title': str(layer.title),
        },
    ]

    # Services expose all layers.
    # WMS is used for reprojecting
    # TMS is used for easy tiles
    # Demo is used to test our installation, may be disabled in final version
    services = {
        'wms': {
            'image_formats': ['image/png'],
            'md': {
                'abstract': 'This is the Harvard HyperMap Proxy.',
                'title': 'Harvard HyperMap Proxy'
            },
            'srs': ['EPSG:4326', 'EPSG:3857'],
            'versions': ['1.1.1']
        },
        'wmts': {
            'restful':
            True,
            'restful_template':
            '/{Layer}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png',
        },
        'tms': {
            'origin': 'nw',
        },
        'demo': None,
    }

    global_config = {
        'http': {
            'ssl_no_cert_checks': True
        },
    }

    # Start with a sane configuration using MapProxy's defaults
    conf_options = load_default_config()

    # Populate a dictionary with custom config changes
    extra_config = {
        'caches': caches,
        'grids': grids,
        'layers': layers,
        'services': services,
        'sources': sources,
        'globals': global_config,
    }

    yaml_config = yaml.dump(extra_config, default_flow_style=False)
    # If you want to test the resulting configuration. Turn on the next
    # line and use that to generate a yaml config.
    # assert False

    # Merge both
    load_config(conf_options, config_dict=extra_config)

    # TODO: Make sure the config is valid.
    errors, informal_only = validate_options(conf_options)
    for error in errors:
        LOGGER.warn(error)
    if not informal_only or (errors and not ignore_warnings):
        raise ConfigurationError('invalid configuration')

    errors = validate_references(conf_options)
    for error in errors:
        LOGGER.warn(error)

    conf = ProxyConfiguration(conf_options, seed=seed, renderd=renderd)

    # Create a MapProxy App
    app = MapProxyApp(conf.configured_services(), conf.base_config)

    return app, yaml_config
Exemple #6
0
def get_mapproxy(layer, seed=False, ignore_warnings=True, renderd=False):
    """Creates a mapproxy config for a given layers
    """
    bbox = [float(layer.bbox_x0), float(layer.bbox_y0), float(layer.bbox_x1), float(layer.bbox_y1)]

    url = str(layer.service.url)

    layer_name = simple_name(layer.name)

    srs = 'EPSG:4326'
    bbox_srs = 'EPSG:4326'
    grid_srs = 'EPSG:3857'

    if layer.type == 'ESRI:ArcGIS:MapServer' or layer.type == 'ESRI:ArcGIS:ImageServer':
        url = str(layer.service.url).split('?')[0] + 'WMSServer?'

        # blindly replace it with /arcgis/
        url = url.replace("/ArcGIS/rest/", "/arcgis/")
        # same for uppercase
        url = url.replace("/arcgis/rest/", "/arcgis/")
        # and for old versions
        url = url.replace("ArcX/rest/services", "arcx/services")
        # in uppercase or lowercase
        url = url.replace("arcx/rest/services", "arcx/services")

        srs = 'EPSG:3857'
        bbox_srs = 'EPSG:3857'

    if layer.type == 'Hypermap:WARPER':
        url = str(layer.url.replace("maps//wms", "maps/wms"))
        grid_srs = 'EPSG:900913'

    if layer.type == 'Hypermap:WorldMap':
        url = str(layer.url.replace("maps//wms", "maps/wms"))

    default_source = {
             'type': 'wms',
             'coverage': {
              'bbox': bbox,
              'srs': srs,
              'bbox_srs': bbox_srs,
              'supported_srs': ['EPSG:4326', 'EPSG:900913', 'EPSG:3857'],
              },
             'req': {
                'layers': layer_name,
                'url': url,
                'transparent': True,
              },
           }

    if layer.type == 'ESRI:ArcGIS:MapServer':
        default_source = {
                  'type': 'tile',
                  'url': str(layer.service.url).split('?')[0] + 'tile/%(z)s/%(y)s/%(x)s',
                  'grid': 'default_grid',
                  'transparent': True,
        }

    # A source is the WMS config
    sources = {
      'default_source': default_source
    }

    # A grid is where it will be projects (Mercator in our case)
    grids = {
             'default_grid': {
                 'tile_size': [256, 256],
                 'srs': grid_srs,
                 'origin': 'nw',
                 }
             }

    # A cache that does not store for now. It needs a grid and a source.
    caches = {'default_cache':
              {
               'cache':
               {
                   'type': 'file',
                   'directory_layout': 'tms',
                   'directory': os.path.join(tempfile.gettempdir(),
                                             'mapproxy',
                                             'layer',
                                             '%s' % layer.id,
                                             'map',
                                             'wmts',
                                             layer_name,
                                             'default_grid',
                                             ),
               },
               'grids': ['default_grid'],
               'sources': ['default_source']},
              }

    # The layer is connected to the cache
    layers = [
        {'name': layer_name,
         'sources': ['default_cache'],
         'title': str(layer.title),
         },
    ]

    # Services expose all layers.
    # WMS is used for reprojecting
    # TMS is used for easy tiles
    # Demo is used to test our installation, may be disabled in final version
    services = {
      'wms': {'image_formats': ['image/png'],
              'md': {'abstract': 'This is the Harvard HyperMap Proxy.',
                     'title': 'Harvard HyperMap Proxy'},
              'srs': ['EPSG:4326', 'EPSG:3857'],
              'versions': ['1.1.1']},
      'wmts': {
              'restful': True,
              'restful_template':
              '/{Layer}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png',
              },
      'tms': {
              'origin': 'nw',
              },
      'demo': None,
    }

    global_config = {
      'http': {'ssl_no_cert_checks': True},
    }

    # Start with a sane configuration using MapProxy's defaults
    conf_options = load_default_config()

    # Populate a dictionary with custom config changes
    extra_config = {
        'caches': caches,
        'grids': grids,
        'layers': layers,
        'services': services,
        'sources': sources,
        'globals': global_config,
    }

    yaml_config = yaml.dump(extra_config, default_flow_style=False)
    # If you want to test the resulting configuration. Turn on the next
    # line and use that to generate a yaml config.
    # assert False

    # Merge both
    load_config(conf_options, config_dict=extra_config)

    # Make sure the config is valid.
    errors, informal_only = validate_options(conf_options)
    for error in errors:
        log.warn(error)
    if not informal_only or (errors and not ignore_warnings):
        raise ConfigurationError('invalid configuration')

    errors = validate_references(conf_options)
    for error in errors:
        log.warn(error)

    conf = ProxyConfiguration(conf_options, seed=seed, renderd=renderd)

    # Create a MapProxy App
    app = MapProxyApp(conf.configured_services(), conf.base_config)

    # Wrap it in an object that allows to get requests by path as a string.
    return TestApp(app), yaml_config
Exemple #7
0
def get_mapproxy(layer, seed=False, ignore_warnings=True, renderd=False):
    """Creates a mapproxy config for a given layers
    """
    bbox = [float(layer.bbox_x0), float(layer.bbox_y0), float(layer.bbox_x1), float(layer.bbox_y1)]

    url = str(layer.service.url)

    layer_name = simple_name(layer.name)

    srs = "EPSG:4326"
    bbox_srs = "EPSG:4326"
    grid_srs = "EPSG:3857"

    if layer.service.type == "ESRI_MapServer" or layer.service.type == "ESRI_ImageServer":
        url = str(layer.service.url).split("?")[0] + "WMSServer?"

        # blindly replace it with /arcgis/
        url = url.replace("/ArcGIS/rest/", "/arcgis/")
        # same for uppercase
        url = url.replace("/arcgis/rest/", "/arcgis/")
        # and for old versions
        url = url.replace("ArcX/rest/services", "arcx/services")
        # in uppercase or lowercase
        url = url.replace("arcx/rest/services", "arcx/services")

        srs = "EPSG:3857"
        bbox_srs = "EPSG:3857"

    if layer.service.type == "WARPER":
        url = str(layer.url.replace("maps//wms", "maps/wms"))
        grid_srs = "EPSG:900913"

    if layer.service.type == "WM":
        url = str(layer.url.replace("maps//wms", "maps/wms"))

    default_source = {
        "type": "wms",
        "coverage": {
            "bbox": bbox,
            "srs": srs,
            "bbox_srs": bbox_srs,
            "supported_srs": ["EPSG:4326", "EPSG:900913", "EPSG:3857"],
        },
        "req": {"layers": layer_name, "url": url, "transparent": True},
    }

    if layer.service.type == "ESRI_MapServer":
        default_source = {
            "type": "tile",
            "url": str(layer.service.url).split("?")[0] + "tile/%(z)s/%(y)s/%(x)s",
            "grid": "default_grid",
            "transparent": True,
        }

    # A source is the WMS config
    sources = {"default_source": default_source}

    # A grid is where it will be projects (Mercator in our case)
    grids = {"default_grid": {"tile_size": [256, 256], "srs": grid_srs, "origin": "nw"}}

    # A cache that does not store for now. It needs a grid and a source.
    caches = {
        "default_cache": {
            "cache": {
                "type": "file",
                "directory_layout": "tms",
                "directory": os.path.join(
                    tempfile.gettempdir(),
                    "mapproxy",
                    "layer",
                    "%s" % layer.id,
                    "map",
                    "wmts",
                    layer_name,
                    "default_grid",
                ),
            },
            "grids": ["default_grid"],
            "sources": ["default_source"],
        }
    }

    # The layer is connected to the cache
    layers = [{"name": layer_name, "sources": ["default_cache"], "title": str(layer.title)}]

    # Services expose all layers.
    # WMS is used for reprojecting
    # TMS is used for easy tiles
    # Demo is used to test our installation, may be disabled in final version
    services = {
        "wms": {
            "image_formats": ["image/png"],
            "md": {"abstract": "This is the Harvard HyperMap Proxy.", "title": "Harvard HyperMap Proxy"},
            "srs": ["EPSG:4326", "EPSG:3857"],
            "versions": ["1.1.1"],
        },
        "wmts": {"restful": True, "restful_template": "/{Layer}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png"},
        "tms": {"origin": "nw"},
        "demo": None,
    }

    # Start with a sane configuration using MapProxy's defaults
    conf_options = load_default_config()

    # Populate a dictionary with custom config changes
    extra_config = {"caches": caches, "grids": grids, "layers": layers, "services": services, "sources": sources}

    yaml_config = yaml.dump(extra_config, default_flow_style=False)
    # If you want to test the resulting configuration. Turn on the next
    # line and use that to generate a yaml config.
    # assert False

    # Merge both
    load_config(conf_options, config_dict=extra_config)

    # Make sure the config is valid.
    errors, informal_only = validate_options(conf_options)
    for error in errors:
        log.warn(error)
    if not informal_only or (errors and not ignore_warnings):
        raise ConfigurationError("invalid configuration")

    errors = validate_references(conf_options)
    for error in errors:
        log.warn(error)

    conf = ProxyConfiguration(conf_options, seed=seed, renderd=renderd)

    # Create a MapProxy App
    app = MapProxyApp(conf.configured_services(), conf.base_config)

    # Wrap it in an object that allows to get requests by path as a string.
    return TestApp(app), yaml_config