Compare commits

...

19 Commits

Author SHA1 Message Date
Mario de Frutos
f85c4f1bbf Merge pull request #110 from CartoDB/qps_for_mapzen_services
QPS manager for Mapzen
2016-03-14 16:37:31 +01:00
Mario de Frutos
60f17fdb09 QPS manager for Mapzen 2016-03-14 11:21:26 +01:00
Mario de Frutos
3bf81b426b Merge pull request #103 from CartoDB/polyline_decoder
Polyline decoder
2016-03-02 11:10:59 +01:00
Mario de Frutos
f4082b3a00 Added integration tests 2016-03-02 11:09:20 +01:00
Mario de Frutos
df08b27068 Removed dependency with polyline and use our implementation 2016-03-02 10:49:02 +01:00
Mario de Frutos
4d04882c18 Polyline decoder 2016-03-02 10:43:27 +01:00
Mario de Frutos
2b084ee2ab Merge pull request #100 from CartoDB/ethervoid-patch-1
Update reference.md
2016-03-01 12:15:14 +01:00
Mario de Frutos
7ddd2dfc8a Update reference.md 2016-03-01 12:15:05 +01:00
Mario de Frutos
d99a0c80d3 Merge pull request #91 from CartoDB/mapzen_integration
Mapzen integration
2016-03-01 11:45:58 +01:00
Mario de Frutos
9a36445661 Sentinel id could be removed safely from the config
Removing the sentinel_master_id property from the cdb_conf
redis config, the extension is going to use a single redis instance
instead of Sentinel
2016-02-26 10:49:10 +01:00
Mario de Frutos
df195a9539 Client part of the mapzen integration 2016-02-26 10:49:10 +01:00
Mario de Frutos
e6b0e3794b Mapzen integration for the server side 2016-02-26 10:49:10 +01:00
Mario de Frutos
d2e73a69fa Python library supports Mapzen routing 2016-02-26 10:49:10 +01:00
Luis Bosque
2a807af6df Fix upgrade/downgrade paths to 0.5.0 2016-02-26 10:49:09 +01:00
Luis Bosque
6c5b1b4e99 Use strictredis if sentinel_master_id is not null
Also, use only a redis_host and redis_port attributes either if the
connection is to redis or to sentinel
2016-02-26 10:49:09 +01:00
Luis Bosque
472d8c2360 Install cdb_geocoder extension with non-admin user 2016-02-26 10:49:09 +01:00
Luis Bosque
ab32065e1d Script to help to enable the extension in dev env 2016-02-26 10:49:09 +01:00
Luis Bosque
04617332a2 Add _redis_conf_params type alter
In order to add or remove redis_host attribute to upgrade/downgrade path
2016-02-26 10:49:09 +01:00
Luis Bosque
93463b3032 Support using a common redis connection
If sentinel_host or sentinel_cluster_id is not provided it will try to
connect with a redis_host parameter
2016-02-26 10:49:08 +01:00
93 changed files with 3051 additions and 26 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,5 @@
.DS_Store .DS_Store
*.pyc *.pyc
cartodb_services.egg-info/
build/
dist/

View File

@@ -13,10 +13,13 @@ NEW_EXTENSION_ARTIFACT = $(EXTENSION)--$(EXTVERSION).sql
DATA = $(NEW_EXTENSION_ARTIFACT) \ DATA = $(NEW_EXTENSION_ARTIFACT) \
cdb_dataservices_client--0.0.1.sql \ cdb_dataservices_client--0.0.1.sql \
cdb_dataservices_client--0.1.0.sql \ cdb_dataservices_client--0.1.0.sql \
cdb_dataservices_client--0.2.0.sql \
cdb_dataservices_client--0.1.0--0.0.1.sql \ cdb_dataservices_client--0.1.0--0.0.1.sql \
cdb_dataservices_client--0.0.1--0.1.0.sql \ cdb_dataservices_client--0.0.1--0.1.0.sql \
cdb_dataservices_client--0.2.0--0.1.0.sql \ cdb_dataservices_client--0.2.0--0.1.0.sql \
cdb_dataservices_client--0.1.0--0.2.0.sql cdb_dataservices_client--0.1.0--0.2.0.sql \
cdb_dataservices_client--0.2.0--0.3.0.sql \
cdb_dataservices_client--0.3.0--0.2.0.sql
REGRESS = $(notdir $(basename $(wildcard test/$(EXTVERSION)/sql/*test.sql))) REGRESS = $(notdir $(basename $(wildcard test/$(EXTVERSION)/sql/*test.sql)))

View File

@@ -0,0 +1,38 @@
CREATE TYPE cdb_dataservices_client.simple_route AS (
shape geometry(LineString,4326),
length real,
duration integer
);
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_route_point_to_point (origin geometry(Point, 4326), destination geometry(Point, 4326), mode text, options text[] DEFAULT ARRAY[]::text[], units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_client.simple_route AS $$
DECLARE
ret cdb_dataservices_client.simple_route;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT * FROM cdb_dataservices_client._cdb_route_point_to_point(username, orgname, origin, destination, mode, options, units) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_route_point_to_point (username text, organization_name text, origin geometry(Point, 4326), destination geometry(Point, 4326), mode text, options text[] DEFAULT ARRAY[]::text[], units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_client.simple_route AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT * FROM cdb_dataservices_server.cdb_route_point_to_point (username, organization_name, origin, destination, mode, options, units);
$$ LANGUAGE plproxy;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_route_point_to_point(origin geometry(Point, 4326), destination geometry(Point, 4326), mode text, options text[], units text) TO publicuser;

View File

@@ -0,0 +1,3 @@
DROP FUNCTION IF EXISTS cdb_dataservices_client.cdb_route_point_to_point (geometry(Point, 4326), geometry(Point, 4326), text, text[], text);
DROP FUNCTION IF EXISTS cdb_dataservices_client._cdb_route_point_to_point (text, text, geometry(Point, 4326), geometry(Point, 4326), text, text[], text);
DROP TYPE IF EXISTS cdb_dataservices_client.simple_route;

View File

@@ -0,0 +1,556 @@
--DO NOT MODIFY THIS FILE, IT IS GENERATED AUTOMATICALLY FROM SOURCES
-- Complain if script is sourced in psql, rather than via CREATE EXTENSION
\echo Use "CREATE EXTENSION cdb_dataservices_client" to load this file. \quit
--
-- Geocoder server connection config
--
-- The purpose of this function is provide to the PL/Proxy functions
-- the connection string needed to connect with the server
CREATE OR REPLACE FUNCTION cdb_dataservices_client._server_conn_str()
RETURNS text AS $$
DECLARE
db_connection_str text;
BEGIN
SELECT cartodb.cdb_conf_getconf('geocoder_server_config')->'connection_str' INTO db_connection_str;
SELECT trim(both '"' FROM db_connection_str) INTO db_connection_str;
RETURN db_connection_str;
END;
$$ LANGUAGE 'plpgsql';
CREATE TYPE cdb_dataservices_client._entity_config AS (
username text,
organization_name text
);
--
-- Get entity config function
--
-- The purpose of this function is to retrieve the username and organization name from
-- a) schema where he/her is the owner in case is an organization user
-- b) entity_name from the cdb_conf database in case is a non organization user
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_entity_config()
RETURNS record AS $$
DECLARE
result cdb_dataservices_client._entity_config;
is_organization boolean;
username text;
organization_name text;
BEGIN
SELECT cartodb.cdb_conf_getconf('user_config')->'is_organization' INTO is_organization;
IF is_organization IS NULL THEN
RAISE EXCEPTION 'User must have user configuration in the config table';
ELSIF is_organization = TRUE THEN
SELECT nspname
FROM pg_namespace s
LEFT JOIN pg_roles r ON s.nspowner = r.oid
WHERE r.rolname = session_user INTO username;
SELECT cartodb.cdb_conf_getconf('user_config')->>'entity_name' INTO organization_name;
ELSE
SELECT cartodb.cdb_conf_getconf('user_config')->>'entity_name' INTO username;
organization_name = NULL;
END IF;
result.username = username;
result.organization_name = organization_name;
RETURN result;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
CREATE TYPE cdb_dataservices_client.isoline AS (
center geometry(Geometry,4326),
data_range integer,
the_geom geometry(Multipolygon,4326)
);
CREATE TYPE cdb_dataservices_client.simple_route AS (
shape geometry(LineString,4326),
length real,
duration integer
);--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_admin0_polygon (country_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_admin0_polygon(username, orgname, country_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_admin1_polygon (admin1_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_admin1_polygon(username, orgname, admin1_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_admin1_polygon (admin1_name text, country_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_admin1_polygon(username, orgname, admin1_name, country_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_namedplace_point (city_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_namedplace_point(username, orgname, city_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_namedplace_point (city_name text, country_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_namedplace_point(username, orgname, city_name, country_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_namedplace_point (city_name text, admin1_name text, country_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_namedplace_point(username, orgname, city_name, admin1_name, country_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_postalcode_polygon (postal_code text, country_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_postalcode_polygon(username, orgname, postal_code, country_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_postalcode_point (postal_code text, country_name text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_postalcode_point(username, orgname, postal_code, country_name) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_ipaddress_point (ip_address text)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_ipaddress_point(username, orgname, ip_address) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_geocode_street_point (searchtext text, city text DEFAULT NULL, state_province text DEFAULT NULL, country text DEFAULT NULL)
RETURNS Geometry AS $$
DECLARE
ret Geometry;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT cdb_dataservices_client._cdb_geocode_street_point(username, orgname, searchtext, city, state_province, country) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_isodistance (source geometry(Geometry, 4326), mode text, range integer[], options text[] DEFAULT ARRAY[]::text[])
RETURNS SETOF cdb_dataservices_client.isoline AS $$
DECLARE
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
RETURN QUERY
SELECT * FROM cdb_dataservices_client._cdb_isodistance(username, orgname, source, mode, range, options);
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_isochrone (source geometry(Geometry, 4326), mode text, range integer[], options text[] DEFAULT ARRAY[]::text[])
RETURNS SETOF cdb_dataservices_client.isoline AS $$
DECLARE
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
RETURN QUERY
SELECT * FROM cdb_dataservices_client._cdb_isochrone(username, orgname, source, mode, range, options);
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
--
-- Public geocoder API function
--
-- These are the only ones with permissions to publicuser role
-- and should also be the only ones with SECURITY DEFINER
CREATE OR REPLACE FUNCTION cdb_dataservices_client.cdb_route_point_to_point (origin geometry(Point, 4326), destination geometry(Point, 4326), mode text, options text[] DEFAULT ARRAY[]::text[], units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_client.simple_route AS $$
DECLARE
ret cdb_dataservices_client.simple_route;
username text;
orgname text;
BEGIN
IF session_user = 'publicuser' OR session_user ~ 'cartodb_publicuser_*' THEN
RAISE EXCEPTION 'The api_key must be provided';
END IF;
SELECT u, o INTO username, orgname FROM cdb_dataservices_client._cdb_entity_config() AS (u text, o text);
-- JSON value stored "" is taken as literal
IF username IS NULL OR username = '' OR username = '""' THEN
RAISE EXCEPTION 'Username is a mandatory argument, check it out';
END IF;
SELECT * FROM cdb_dataservices_client._cdb_route_point_to_point(username, orgname, origin, destination, mode, options, units) INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql' SECURITY DEFINER;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_admin0_polygon (username text, organization_name text, country_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_admin0_polygon (username, organization_name, country_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_admin1_polygon (username text, organization_name text, admin1_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_admin1_polygon (username, organization_name, admin1_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_admin1_polygon (username text, organization_name text, admin1_name text, country_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_admin1_polygon (username, organization_name, admin1_name, country_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_namedplace_point (username text, organization_name text, city_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_namedplace_point (username, organization_name, city_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_namedplace_point (username text, organization_name text, city_name text, country_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_namedplace_point (username, organization_name, city_name, country_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_namedplace_point (username text, organization_name text, city_name text, admin1_name text, country_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_namedplace_point (username, organization_name, city_name, admin1_name, country_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_postalcode_polygon (username text, organization_name text, postal_code text, country_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_postalcode_polygon (username, organization_name, postal_code, country_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_postalcode_point (username text, organization_name text, postal_code text, country_name text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_postalcode_point (username, organization_name, postal_code, country_name);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_ipaddress_point (username text, organization_name text, ip_address text)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_ipaddress_point (username, organization_name, ip_address);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_geocode_street_point (username text, organization_name text, searchtext text, city text DEFAULT NULL, state_province text DEFAULT NULL, country text DEFAULT NULL)
RETURNS Geometry AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT cdb_dataservices_server.cdb_geocode_street_point (username, organization_name, searchtext, city, state_province, country);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_isodistance (username text, organization_name text, source geometry(Geometry, 4326), mode text, range integer[], options text[] DEFAULT ARRAY[]::text[])
RETURNS SETOF cdb_dataservices_client.isoline AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT * FROM cdb_dataservices_server.cdb_isodistance (username, organization_name, source, mode, range, options);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_isochrone (username text, organization_name text, source geometry(Geometry, 4326), mode text, range integer[], options text[] DEFAULT ARRAY[]::text[])
RETURNS SETOF cdb_dataservices_client.isoline AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT * FROM cdb_dataservices_server.cdb_isochrone (username, organization_name, source, mode, range, options);
$$ LANGUAGE plproxy;
CREATE OR REPLACE FUNCTION cdb_dataservices_client._cdb_route_point_to_point (username text, organization_name text, origin geometry(Point, 4326), destination geometry(Point, 4326), mode text, options text[] DEFAULT ARRAY[]::text[], units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_client.simple_route AS $$
CONNECT cdb_dataservices_client._server_conn_str();
SELECT * FROM cdb_dataservices_server.cdb_route_point_to_point (username, organization_name, origin, destination, mode, options, units);
$$ LANGUAGE plproxy;
-- Make sure by default there are no permissions for publicuser
-- NOTE: this happens at extension creation time, as part of an implicit transaction.
REVOKE ALL PRIVILEGES ON SCHEMA cdb_dataservices_client FROM PUBLIC, publicuser CASCADE;
-- Grant permissions on the schema to publicuser (but just the schema)
GRANT USAGE ON SCHEMA cdb_dataservices_client TO publicuser;
-- Revoke execute permissions on all functions in the schema by default
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_dataservices_client FROM PUBLIC, publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_admin0_polygon(country_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_admin1_polygon(admin1_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_admin1_polygon(admin1_name text, country_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_namedplace_point(city_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_namedplace_point(city_name text, country_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_namedplace_point(city_name text, admin1_name text, country_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_postalcode_polygon(postal_code text, country_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_postalcode_point(postal_code text, country_name text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_ipaddress_point(ip_address text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_geocode_street_point(searchtext text, city text, state_province text, country text) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_isodistance(source geometry(Geometry, 4326), mode text, range integer[], options text[]) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_isochrone(source geometry(Geometry, 4326), mode text, range integer[], options text[]) TO publicuser;
GRANT EXECUTE ON FUNCTION cdb_dataservices_client.cdb_route_point_to_point(origin geometry(Point, 4326), destination geometry(Point, 4326), mode text, options text[], units text) TO publicuser;

View File

@@ -1,5 +1,5 @@
comment = 'CartoDB dataservices client API extension' comment = 'CartoDB dataservices client API extension'
default_version = '0.2.0' default_version = '0.3.0'
requires = 'plproxy, cartodb' requires = 'plproxy, cartodb'
superuser = true superuser = true
schema = cdb_dataservices_client schema = cdb_dataservices_client

View File

@@ -0,0 +1,91 @@
---
- name: cdb_geocode_admin0_polygon
return_type: Geometry
params:
- { name: country_name, type: text }
- name: cdb_geocode_admin1_polygon
return_type: Geometry
params:
- { name: admin1_name, type: text }
- name: cdb_geocode_admin1_polygon
return_type: Geometry
params:
- { name: admin1_name, type: text }
- { name: country_name, type: text }
- name: cdb_geocode_namedplace_point
return_type: Geometry
params:
- { name: city_name, type: text}
- name: cdb_geocode_namedplace_point
return_type: Geometry
params:
- { name: city_name, type: text}
- { name: country_name, type: text}
- name: cdb_geocode_namedplace_point
return_type: Geometry
params:
- { name: city_name, type: text}
- { name: admin1_name, type: text}
- { name: country_name, type: text}
- name: cdb_geocode_postalcode_polygon
return_type: Geometry
params:
- { name: postal_code, type: text}
- { name: country_name, type: text}
- name: cdb_geocode_postalcode_point
return_type: Geometry
params:
- { name: postal_code, type: text}
- { name: country_name, type: text}
- name: cdb_geocode_ipaddress_point
return_type: Geometry
params:
- { name: ip_address, type: text}
- name: cdb_geocode_street_point
return_type: Geometry
params:
- { name: searchtext, type: text}
- { name: city, type: text, default: 'NULL'}
- { name: state_province, type: text, default: 'NULL'}
- { name: country, type: text, default: 'NULL'}
- name: cdb_isodistance
return_type: SETOF cdb_dataservices_client.isoline
multi_row: true
multi_field: true
params:
- { name: source, type: "geometry(Geometry, 4326)" }
- { name: mode, type: text }
- { name: range, type: "integer[]" }
- { name: options, type: "text[]", default: 'ARRAY[]::text[]' }
- name: cdb_isochrone
return_type: SETOF cdb_dataservices_client.isoline
multi_row: true
multi_field: true
params:
- { name: source, type: "geometry(Geometry, 4326)" }
- { name: mode, type: text }
- { name: range, type: "integer[]" }
- { name: options, type: "text[]", default: 'ARRAY[]::text[]' }
- name: cdb_route_point_to_point
return_type: cdb_dataservices_client.simple_route
multi_field: true
params:
- { name: origin, type: "geometry(Point, 4326)" }
- { name: destination, type: "geometry(Point, 4326)" }
- { name: mode, type: text }
- { name: options, type: "text[]", default: 'ARRAY[]::text[]' }
- { name: units, type: "text", default: "'kilometers'"}

View File

@@ -28,6 +28,10 @@ class SqlTemplateRenderer
@function_signature['return_type'] @function_signature['return_type']
end end
def multi_field
@function_signature['multi_field']
end
def multi_row def multi_row
@function_signature['multi_row'] @function_signature['multi_row']
end end

View File

@@ -22,6 +22,9 @@ BEGIN
<% if multi_row %> <% if multi_row %>
RETURN QUERY RETURN QUERY
SELECT * FROM <%= DATASERVICES_CLIENT_SCHEMA %>._<%= name %>(username, orgname, <%= params %>); SELECT * FROM <%= DATASERVICES_CLIENT_SCHEMA %>._<%= name %>(username, orgname, <%= params %>);
<% elsif multi_field %>
SELECT * FROM <%= DATASERVICES_CLIENT_SCHEMA %>._<%= name %>(username, orgname, <%= params %>) INTO ret;
RETURN ret;
<% else %> <% else %>
SELECT <%= DATASERVICES_CLIENT_SCHEMA %>._<%= name %>(username, orgname, <%= params %>) INTO ret; SELECT <%= DATASERVICES_CLIENT_SCHEMA %>._<%= name %>(username, orgname, <%= params %>) INTO ret;
RETURN ret; RETURN ret;

View File

@@ -1,7 +1,7 @@
CREATE OR REPLACE FUNCTION <%= DATASERVICES_CLIENT_SCHEMA %>._<%= name %> (username text, organization_name text, <%= params_with_type_and_default %>) CREATE OR REPLACE FUNCTION <%= DATASERVICES_CLIENT_SCHEMA %>._<%= name %> (username text, organization_name text, <%= params_with_type_and_default %>)
RETURNS <%= return_type %> AS $$ RETURNS <%= return_type %> AS $$
CONNECT <%= DATASERVICES_CLIENT_SCHEMA %>._server_conn_str(); CONNECT <%= DATASERVICES_CLIENT_SCHEMA %>._server_conn_str();
<% if multi_row %> <% if multi_field %>
SELECT * FROM <%= DATASERVICES_SERVER_SCHEMA %>.<%= name %> (username, organization_name, <%= params %>); SELECT * FROM <%= DATASERVICES_SERVER_SCHEMA %>.<%= name %> (username, organization_name, <%= params %>);
<% else %> <% else %>
SELECT <%= DATASERVICES_SERVER_SCHEMA %>.<%= name %> (username, organization_name, <%= params %>); SELECT <%= DATASERVICES_SERVER_SCHEMA %>.<%= name %> (username, organization_name, <%= params %>);

View File

@@ -0,0 +1 @@
../0.2.0/00_header.sql

View File

@@ -0,0 +1 @@
../0.2.0/10_geocoder_server_conn.sql

View File

@@ -0,0 +1 @@
../0.2.0/15_config_management.sql

View File

@@ -0,0 +1,11 @@
CREATE TYPE cdb_dataservices_client.isoline AS (
center geometry(Geometry,4326),
data_range integer,
the_geom geometry(Multipolygon,4326)
);
CREATE TYPE cdb_dataservices_client.simple_route AS (
shape geometry(LineString,4326),
length real,
duration integer
);

View File

@@ -0,0 +1 @@
../0.2.0/80_permissions.sql

View File

@@ -0,0 +1 @@
../../0.2.0/expected/00_installation_test.out

View File

@@ -0,0 +1 @@
../../0.2.0/expected/10_admin0_test.out

View File

@@ -0,0 +1 @@
../../0.2.0/expected/20_admin1_test.out

View File

@@ -0,0 +1 @@
../../0.2.0/expected/30_namedplaces_test.out

View File

@@ -0,0 +1 @@
../../0.2.0/expected/40_postalcodes_test.out

View File

@@ -0,0 +1 @@
../../0.2.0/expected/50_ipaddresses_test.out

View File

@@ -0,0 +1 @@
../../0.2.0/expected/60_street_test.out

View File

@@ -0,0 +1,41 @@
-- Add to the search path the schema
SET search_path TO public,cartodb,cdb_dataservices_client;
-- Mock the server functions
CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_route_point_to_point (username text, orgname text, origin geometry(Point, 4326), destination geometry(Point, 4326), mode TEXT, options text[] DEFAULT ARRAY[]::text[], units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_client.simple_route AS $$
DECLARE
ret cdb_dataservices_client.simple_route;
BEGIN
RAISE NOTICE 'cdb_dataservices_server.cdb_route_point_to_point invoked with params (%, %, %, %, %, %, %)', username, orgname, origin, destination, mode, options, units;
SELECT NULL, 5.33, 100 INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql';
-- Exercise the public and the proxied function
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car');
NOTICE: cdb_dataservices_client._cdb_route_point_to_point(7): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_route_point_to_point invoked with params (test_user, <NULL>, 0101000000D53E1D8F19F455C0185B087250F24440, 0101000000465F419AB1F255C0D8B628B341EE4440, car, {}, kilometers)
CONTEXT: SQL statement "SELECT * FROM cdb_dataservices_client._cdb_route_point_to_point(username, orgname, origin, destination, mode, options, units)"
PL/pgSQL function cdb_route_point_to_point(geometry,geometry,text,text[],text) line 16 at SQL statement
cdb_route_point_to_point
--------------------------
(,5.33,100)
(1 row)
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car', ARRAY['mode_type=shortest']::text[]);
NOTICE: cdb_dataservices_client._cdb_route_point_to_point(7): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_route_point_to_point invoked with params (test_user, <NULL>, 0101000000D53E1D8F19F455C0185B087250F24440, 0101000000465F419AB1F255C0D8B628B341EE4440, car, {mode_type=shortest}, kilometers)
CONTEXT: SQL statement "SELECT * FROM cdb_dataservices_client._cdb_route_point_to_point(username, orgname, origin, destination, mode, options, units)"
PL/pgSQL function cdb_route_point_to_point(geometry,geometry,text,text[],text) line 16 at SQL statement
cdb_route_point_to_point
--------------------------
(,5.33,100)
(1 row)
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car', ARRAY[]::text[], 'miles');
NOTICE: cdb_dataservices_client._cdb_route_point_to_point(7): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_route_point_to_point invoked with params (test_user, <NULL>, 0101000000D53E1D8F19F455C0185B087250F24440, 0101000000465F419AB1F255C0D8B628B341EE4440, car, {}, miles)
CONTEXT: SQL statement "SELECT * FROM cdb_dataservices_client._cdb_route_point_to_point(username, orgname, origin, destination, mode, options, units)"
PL/pgSQL function cdb_route_point_to_point(geometry,geometry,text,text[],text) line 16 at SQL statement
cdb_route_point_to_point
--------------------------
(,5.33,100)
(1 row)

View File

@@ -0,0 +1,128 @@
-- Use regular user role
SET ROLE test_regular_user;
-- Add to the search path the schema
SET search_path TO public,cartodb,cdb_dataservices_client;
-- Exercise the public function
-- it is public, it shall work
SELECT cdb_geocode_admin0_polygon('Spain');
NOTICE: cdb_dataservices_client._cdb_geocode_admin0_polygon(3): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_admin0_polygon invoked with params (test_user, <NULL>, Spain)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_admin0_polygon(username, orgname, country_name)"
PL/pgSQL function cdb_geocode_admin0_polygon(text) line 16 at SQL statement
cdb_geocode_admin0_polygon
----------------------------
(1 row)
SELECT cdb_geocode_admin1_polygon('California');
NOTICE: cdb_dataservices_client._cdb_geocode_admin1_polygon(3): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_admin1_polygon invoked with params (test_user, <NULL>, California)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_admin1_polygon(username, orgname, admin1_name)"
PL/pgSQL function cdb_geocode_admin1_polygon(text) line 16 at SQL statement
cdb_geocode_admin1_polygon
----------------------------
(1 row)
SELECT cdb_geocode_admin1_polygon('California', 'United States');
NOTICE: cdb_dataservices_client._cdb_geocode_admin1_polygon(4): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_admin1_polygon invoked with params (test_user, <NULL>, California, United States)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_admin1_polygon(username, orgname, admin1_name, country_name)"
PL/pgSQL function cdb_geocode_admin1_polygon(text,text) line 16 at SQL statement
cdb_geocode_admin1_polygon
----------------------------
(1 row)
SELECT cdb_geocode_namedplace_point('Elx');
NOTICE: cdb_dataservices_client._cdb_geocode_namedplace_point(3): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_namedplace_point invoked with params (test_user, <NULL>, Elx)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_namedplace_point(username, orgname, city_name)"
PL/pgSQL function cdb_geocode_namedplace_point(text) line 16 at SQL statement
cdb_geocode_namedplace_point
------------------------------
(1 row)
SELECT cdb_geocode_namedplace_point('Elx', 'Valencia');
NOTICE: cdb_dataservices_client._cdb_geocode_namedplace_point(4): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_namedplace_point invoked with params (test_user, <NULL>, Elx, Valencia)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_namedplace_point(username, orgname, city_name, country_name)"
PL/pgSQL function cdb_geocode_namedplace_point(text,text) line 16 at SQL statement
cdb_geocode_namedplace_point
------------------------------
(1 row)
SELECT cdb_geocode_namedplace_point('Elx', 'Valencia', 'Spain');
NOTICE: cdb_dataservices_client._cdb_geocode_namedplace_point(5): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_namedplace_point invoked with params (test_user, <NULL>, Elx, Valencia, Spain)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_namedplace_point(username, orgname, city_name, admin1_name, country_name)"
PL/pgSQL function cdb_geocode_namedplace_point(text,text,text) line 16 at SQL statement
cdb_geocode_namedplace_point
------------------------------
(1 row)
SELECT cdb_geocode_postalcode_polygon('03204', 'Spain');
NOTICE: cdb_dataservices_client._cdb_geocode_postalcode_polygon(4): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_postalcode_polygon invoked with params (test_user, <NULL>, 03204, Spain)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_postalcode_polygon(username, orgname, postal_code, country_name)"
PL/pgSQL function cdb_geocode_postalcode_polygon(text,text) line 16 at SQL statement
cdb_geocode_postalcode_polygon
--------------------------------
(1 row)
SELECT cdb_geocode_postalcode_point('03204', 'Spain');
NOTICE: cdb_dataservices_client._cdb_geocode_postalcode_point(4): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_postalcode_point invoked with params (test_user, <NULL>, 03204, Spain)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_postalcode_point(username, orgname, postal_code, country_name)"
PL/pgSQL function cdb_geocode_postalcode_point(text,text) line 16 at SQL statement
cdb_geocode_postalcode_point
------------------------------
(1 row)
SELECT cdb_geocode_ipaddress_point('8.8.8.8');
NOTICE: cdb_dataservices_client._cdb_geocode_ipaddress_point(3): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_ipaddress_point invoked with params (test_user, <NULL>, 8.8.8.8)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_ipaddress_point(username, orgname, ip_address)"
PL/pgSQL function cdb_geocode_ipaddress_point(text) line 16 at SQL statement
cdb_geocode_ipaddress_point
-----------------------------
(1 row)
SELECT cdb_geocode_street_point('one street, 1');
NOTICE: cdb_dataservices_client._cdb_geocode_street_point(6): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_geocode_geocoder_street_point invoked with params (test_user, <NULL>, one street, 1, <NULL>, <NULL>, <NULL>)
CONTEXT: SQL statement "SELECT cdb_dataservices_client._cdb_geocode_street_point(username, orgname, searchtext, city, state_province, country)"
PL/pgSQL function cdb_geocode_street_point(text,text,text,text) line 16 at SQL statement
cdb_geocode_street_point
--------------------------
(1 row)
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car');
NOTICE: cdb_dataservices_client._cdb_route_point_to_point(7): [contrib_regression] REMOTE NOTICE: cdb_dataservices_server.cdb_route_point_to_point invoked with params (test_user, <NULL>, 0101000000D53E1D8F19F455C0185B087250F24440, 0101000000465F419AB1F255C0D8B628B341EE4440, car, {}, kilometers)
CONTEXT: SQL statement "SELECT * FROM cdb_dataservices_client._cdb_route_point_to_point(username, orgname, origin, destination, mode, options, units)"
PL/pgSQL function cdb_route_point_to_point(geometry,geometry,text,text[],text) line 16 at SQL statement
cdb_route_point_to_point
--------------------------
(,5.33,100)
(1 row)
-- Check the regular user has no permissions on private functions
SELECT _cdb_geocode_admin0_polygon('evil_user', 'evil_orgname', 'Hell');
ERROR: permission denied for function _cdb_geocode_admin0_polygon
SELECT _cdb_geocode_admin1_polygon('evil_user', 'evil_orgname', 'Hell');
ERROR: permission denied for function _cdb_geocode_admin1_polygon
SELECT _cdb_geocode_admin1_polygon('evil_user', 'evil_orgname', 'Sheol', 'Hell');
ERROR: permission denied for function _cdb_geocode_admin1_polygon
SELECT _cdb_geocode_namedplace_point('evil_user', 'evil_orgname', 'Sheol');
ERROR: permission denied for function _cdb_geocode_namedplace_point
SELECT _cdb_geocode_namedplace_point('evil_user', 'evil_orgname', 'Sheol', 'Hell');
ERROR: permission denied for function _cdb_geocode_namedplace_point
SELECT _cdb_geocode_namedplace_point('evil_user', 'evil_orgname', 'Sheol', 'Hell', 'Ugly world');
ERROR: permission denied for function _cdb_geocode_namedplace_point
SELECT _cdb_geocode_postalcode_polygon('evil_user', 'evil_orgname', '66666', 'Hell');
ERROR: permission denied for function _cdb_geocode_postalcode_polygon
SELECT _cdb_geocode_postalcode_point('evil_user', 'evil_orgname', '66666', 'Hell');
ERROR: permission denied for function _cdb_geocode_postalcode_point
SELECT _cdb_geocode_ipaddress_point('evil_user', 'evil_orgname', '8.8.8.8');
ERROR: permission denied for function _cdb_geocode_ipaddress_point
SELECT _cdb_geocode_street_point('evil_user', 'evil_orgname', 'one street, 1');
ERROR: permission denied for function _cdb_geocode_street_point
SELECT _cdb_route_point_to_point('evil_user', 'evil_orgname', 'POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car');
ERROR: permission denied for function _cdb_route_point_to_point

View File

@@ -0,0 +1 @@
../../0.2.0/sql/00_installation_test.sql

View File

@@ -0,0 +1 @@
../../0.2.0/sql/10_admin0_test.sql

View File

@@ -0,0 +1 @@
../../0.2.0/sql/20_admin1_test.sql

View File

@@ -0,0 +1 @@
../../0.2.0/sql/30_namedplaces_test.sql

View File

@@ -0,0 +1 @@
../../0.2.0/sql/40_postalcodes_test.sql

View File

@@ -0,0 +1 @@
../../0.2.0/sql/50_ipaddresses_test.sql

View File

@@ -0,0 +1 @@
../../0.2.0/sql/60_street_test.sql

View File

@@ -0,0 +1,21 @@
-- Add to the search path the schema
SET search_path TO public,cartodb,cdb_dataservices_client;
-- Mock the server functions
CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_route_point_to_point (username text, orgname text, origin geometry(Point, 4326), destination geometry(Point, 4326), mode TEXT, options text[] DEFAULT ARRAY[]::text[], units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_client.simple_route AS $$
DECLARE
ret cdb_dataservices_client.simple_route;
BEGIN
RAISE NOTICE 'cdb_dataservices_server.cdb_route_point_to_point invoked with params (%, %, %, %, %, %, %)', username, orgname, origin, destination, mode, options, units;
SELECT NULL, 5.33, 100 INTO ret;
RETURN ret;
END;
$$ LANGUAGE 'plpgsql';
-- Exercise the public and the proxied function
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car');
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car', ARRAY['mode_type=shortest']::text[]);
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car', ARRAY[]::text[], 'miles');

View File

@@ -0,0 +1,32 @@
-- Use regular user role
SET ROLE test_regular_user;
-- Add to the search path the schema
SET search_path TO public,cartodb,cdb_dataservices_client;
-- Exercise the public function
-- it is public, it shall work
SELECT cdb_geocode_admin0_polygon('Spain');
SELECT cdb_geocode_admin1_polygon('California');
SELECT cdb_geocode_admin1_polygon('California', 'United States');
SELECT cdb_geocode_namedplace_point('Elx');
SELECT cdb_geocode_namedplace_point('Elx', 'Valencia');
SELECT cdb_geocode_namedplace_point('Elx', 'Valencia', 'Spain');
SELECT cdb_geocode_postalcode_polygon('03204', 'Spain');
SELECT cdb_geocode_postalcode_point('03204', 'Spain');
SELECT cdb_geocode_ipaddress_point('8.8.8.8');
SELECT cdb_geocode_street_point('one street, 1');
SELECT cdb_route_point_to_point('POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car');
-- Check the regular user has no permissions on private functions
SELECT _cdb_geocode_admin0_polygon('evil_user', 'evil_orgname', 'Hell');
SELECT _cdb_geocode_admin1_polygon('evil_user', 'evil_orgname', 'Hell');
SELECT _cdb_geocode_admin1_polygon('evil_user', 'evil_orgname', 'Sheol', 'Hell');
SELECT _cdb_geocode_namedplace_point('evil_user', 'evil_orgname', 'Sheol');
SELECT _cdb_geocode_namedplace_point('evil_user', 'evil_orgname', 'Sheol', 'Hell');
SELECT _cdb_geocode_namedplace_point('evil_user', 'evil_orgname', 'Sheol', 'Hell', 'Ugly world');
SELECT _cdb_geocode_postalcode_polygon('evil_user', 'evil_orgname', '66666', 'Hell');
SELECT _cdb_geocode_postalcode_point('evil_user', 'evil_orgname', '66666', 'Hell');
SELECT _cdb_geocode_ipaddress_point('evil_user', 'evil_orgname', '8.8.8.8');
SELECT _cdb_geocode_street_point('evil_user', 'evil_orgname', 'one street, 1');
SELECT _cdb_route_point_to_point('evil_user', 'evil_orgname', 'POINT(-87.81406 41.89308)'::geometry,'POINT(-87.79209 41.86138)'::geometry, 'car');

View File

@@ -269,7 +269,7 @@ UPDATE {tablename} SET the_geom = cdb_geocode_ipaddress_point('102.23.34.1')
``` ```
## Street-level geocoder ## Street-level geocoder
This function provides a street-level geocoding service. This service uses the street level geocoder defined for the user (currently, only the Here geocoder is available). This function provides a street-level geocoding service. This service uses the street level geocoder defined for the user.
**This service is subject to quota limitations, and extra fees may apply.** Please view our [terms and conditions](https://cartodb.com/terms/) **This service is subject to quota limitations, and extra fees may apply.** Please view our [terms and conditions](https://cartodb.com/terms/)

View File

@@ -0,0 +1,36 @@
#!/bin/bash
TARGET_DB=$1
ADMIN_PGUSER="postgres"
PGUSER=${2:-${ADMIN_PGUSER}}
PYTHON_LIBRARY_NAME='cartodb_services'
function usage {
echo "Usage: ${0} <dbname> [dbuser]"
}
[[ -z $TARGET_DB ]] && echo "Missing DB parameter" && usage && exit 1
python -c "import ${PYTHON_LIBRARY_NAME}"
if [[ $? != 0 ]]
then
echo "Missing ${PYTHON_LIBRARY_NAME} python library"
echo "Trying to install.."
cd server/lib/python/cartodb_services && sudo python setup.py install
python -c "import ${PYTHON_LIBRARY_NAME}" 2> /dev/null
if [[ $? != 0 ]]
then
echo "There are some problems with python library. Debug manually"
exit 1
fi
fi
CREATE_EXTENSION_COMMAND="CREATE EXTENSION IF NOT EXISTS"
CDB_GEOCODER_EXTENSION_CREATE="${CREATE_EXTENSION_COMMAND} cdb_geocoder"
CDB_DATASERVICES_SERVER_CREATE="${CREATE_EXTENSION_COMMAND} cdb_dataservices_server"
echo "* Creating extension cdb_geocoder"
psql -U ${PGUSER} -d ${TARGET_DB} -c "${CDB_GEOCODER_EXTENSION_CREATE}"
echo "* Creating extension cdb_dataservices_server"
psql -U ${ADMIN_PGUSER} -d ${TARGET_DB} -c "${CDB_DATASERVICES_SERVER_CREATE}"

View File

@@ -15,14 +15,17 @@ DATA = $(NEW_EXTENSION_ARTIFACT) \
cdb_dataservices_server--0.1.0.sql \ cdb_dataservices_server--0.1.0.sql \
cdb_dataservices_server--0.2.0.sql \ cdb_dataservices_server--0.2.0.sql \
cdb_dataservices_server--0.3.0.sql \ cdb_dataservices_server--0.3.0.sql \
cdb_dataservices_server--0.4.0.sql \
cdb_dataservices_server--0.1.0--0.0.1.sql \ cdb_dataservices_server--0.1.0--0.0.1.sql \
cdb_dataservices_server--0.0.1--0.1.0.sql \ cdb_dataservices_server--0.0.1--0.1.0.sql \
cdb_dataservices_server--0.2.0--0.1.0.sql \ cdb_dataservices_server--0.2.0--0.1.0.sql \
cdb_dataservices_server--0.1.0--0.2.0.sql \ cdb_dataservices_server--0.1.0--0.2.0.sql \
cdb_dataservices_server--0.2.0--0.3.0.sql \ cdb_dataservices_server--0.2.0--0.3.0.sql \
cdb_dataservices_server--0.3.0--0.2.0.sql \ cdb_dataservices_server--0.3.0--0.2.0.sql \
cdb_dataservices_server--0.3.0--0.4.0.sql \ cdb_dataservices_server--0.3.0--0.4.0.sql \
cdb_dataservices_server--0.4.0--0.3.0.sql cdb_dataservices_server--0.4.0--0.3.0.sql \
cdb_dataservices_server--0.5.0--0.4.0.sql \
cdb_dataservices_server--0.4.0--0.5.0.sql
REGRESS = $(notdir $(basename $(wildcard test/$(EXTVERSION)/sql/*test.sql))) REGRESS = $(notdir $(basename $(wildcard test/$(EXTVERSION)/sql/*test.sql)))
TEST_DIR = test/$(EXTVERSION) TEST_DIR = test/$(EXTVERSION)

View File

@@ -0,0 +1,157 @@
ALTER TYPE cdb_dataservices_server._redis_conf_params ADD ATTRIBUTE redis_host text;
ALTER TYPE cdb_dataservices_server._redis_conf_params ADD ATTRIBUTE redis_port int;
ALTER TYPE cdb_dataservices_server._redis_conf_params DROP ATTRIBUTE IF EXISTS sentinel_host;
ALTER TYPE cdb_dataservices_server._redis_conf_params DROP ATTRIBUTE IF EXISTS sentinel_port;
-- Get the Redis configuration from the _conf table --
CREATE OR REPLACE FUNCTION cdb_dataservices_server._get_redis_conf_v2(config_key text)
RETURNS cdb_dataservices_server._redis_conf_params AS $$
conf_query = "SELECT cartodb.CDB_Conf_GetConf('{0}') as conf".format(config_key)
conf = plpy.execute(conf_query)[0]['conf']
if conf is None:
plpy.error("There is no redis configuration defined")
else:
import json
params = json.loads(conf)
redis_conf_params = {
"redis_host": params['redis_host'],
"redis_port": params['redis_port'],
"timeout": params['timeout'],
"redis_db": params['redis_db']
}
if "sentinel_master_id" in params:
redis_conf_params["sentinel_master_id"] = params["sentinel_master_id"]
else:
redis_conf_params["sentinel_master_id"] = None
return redis_conf_params
$$ LANGUAGE plpythonu;
-- Get the connection to redis from cache or create a new one
CREATE OR REPLACE FUNCTION cdb_dataservices_server._connect_to_redis(user_id text)
RETURNS boolean AS $$
cache_key = "redis_connection_{0}".format(user_id)
if cache_key in GD:
return False
else:
from cartodb_services.tools import RedisConnection
metadata_config_params = plpy.execute("""select c.sentinel_master_id, c.redis_host,
c.redis_port, c.timeout, c.redis_db
from cdb_dataservices_server._get_redis_conf_v2('redis_metadata_config') c;""")[0]
metrics_config_params = plpy.execute("""select c.sentinel_master_id, c.redis_host,
c.redis_port, c.timeout, c.redis_db
from cdb_dataservices_server._get_redis_conf_v2('redis_metrics_config') c;""")[0]
redis_metadata_connection = RedisConnection(metadata_config_params['sentinel_master_id'],
metadata_config_params['redis_host'],
metadata_config_params['redis_port'],
timeout=metadata_config_params['timeout'],
redis_db=metadata_config_params['redis_db']).redis_connection()
redis_metrics_connection = RedisConnection(metrics_config_params['sentinel_master_id'],
metrics_config_params['redis_host'],
metrics_config_params['redis_port'],
timeout=metrics_config_params['timeout'],
redis_db=metrics_config_params['redis_db']).redis_connection()
GD[cache_key] = {
'redis_metadata_connection': redis_metadata_connection,
'redis_metrics_connection': redis_metrics_connection,
}
return True
$$ LANGUAGE plpythonu SECURITY DEFINER;
-- Mapzen routing integration
CREATE TYPE cdb_dataservices_server.simple_route AS (
shape geometry(LineString,4326),
length real,
duration integer
);
-- Get the Redis configuration from the _conf table --
CREATE OR REPLACE FUNCTION cdb_dataservices_server._get_routing_config(username text, orgname text)
RETURNS boolean AS $$
cache_key = "user_routing_config_{0}".format(username)
if cache_key in GD:
return False
else:
import json
from cartodb_services.metrics import RoutingConfig
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metadata_connection']
mapzen_conf_json = plpy.execute("SELECT cartodb.CDB_Conf_GetConf('mapzen_conf') as mapzen_conf", 1)[0]['mapzen_conf']
if not mapzen_conf_json:
mapzen_app_key = None
else:
mapzen_conf = json.loads(mapzen_conf_json)
mapzen_app_key = mapzen_conf['routing_app_key']
routing_config = RoutingConfig(redis_conn, username, orgname, mapzen_app_key)
GD[cache_key] = routing_config
return True
$$ LANGUAGE plpythonu SECURITY DEFINER;
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_route_point_to_point(
username TEXT,
orgname TEXT,
origin geometry(Point, 4326),
destination geometry(Point, 4326),
mode TEXT,
options text[] DEFAULT ARRAY[]::text[],
units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_server.simple_route AS $$
import json
from cartodb_services.mapzen import MapzenRouting, MapzenRoutingResponse
from cartodb_services.mapzen.types import polyline_to_linestring
from cartodb_services.metrics import QuotaService
from cartodb_services.tools import Coordinate
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
user_routing_config = GD["user_routing_config_{0}".format(username)]
quota_service = QuotaService(user_routing_config, redis_conn)
try:
client = MapzenRouting(user_routing_config.mapzen_app_key)
orig_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % origin)[0]['lat']
orig_lon = plpy.execute("SELECT ST_X('%s') AS lon" % origin)[0]['lon']
origin_coordinates = Coordinate(orig_lon, orig_lat)
dest_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % destination)[0]['lat']
dest_lon = plpy.execute("SELECT ST_X('%s') AS lon" % destination)[0]['lon']
dest_coordinates = Coordinate(dest_lon, dest_lat)
resp = client.calculate_route_point_to_point(origin_coordinates, dest_coordinates, mode, options, units)
if resp:
shape_linestring = polyline_to_linestring(resp.shape)
quota_service.increment_success_geocoder_use()
return [shape_linestring, resp.length, resp.duration]
else:
quota_service.increment_empty_geocoder_use()
except BaseException as e:
import sys, traceback
type_, value_, traceback_ = sys.exc_info()
quota_service.increment_failed_geocoder_use()
error_msg = 'There was an error trying to obtain route using mapzen provider: {0}'.format(e)
plpy.notice(traceback.format_tb(traceback_))
plpy.error(error_msg)
finally:
quota_service.increment_total_geocoder_use()
$$ LANGUAGE plpythonu SECURITY DEFINER;
CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_route_point_to_point(
username TEXT,
orgname TEXT,
origin geometry(Point, 4326),
destination geometry(Point, 4326),
mode TEXT,
options text[] DEFAULT ARRAY[]::text[],
units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_server.simple_route AS $$
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
plpy.execute("SELECT cdb_dataservices_server._get_routing_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname)))
user_routing_config = GD["user_routing_config_{0}".format(username)]
mapzen_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapzen_route_point_to_point($1, $2, $3, $4, $5, $6, $7) as route;", ["text", "text", "geometry(Point, 4326)", "geometry(Point, 4326)", "text", "text[]", "text"])
result = plpy.execute(mapzen_plan, [username, orgname, origin, destination, mode, options, units])
return [result[0]['shape'],result[0]['length'], result[0]['duration']]
$$ LANGUAGE plpythonu;

View File

@@ -0,0 +1,61 @@
ALTER TYPE cdb_dataservices_server._redis_conf_params DROP ATTRIBUTE IF EXISTS redis_host,
DROP ATTRIBUTE IF EXISTS redis_port,
ADD ATTRIBUTE sentinel_host text,
ADD ATTRIBUTE sentinel_port int;
-- Get the Redis configuration from the _conf table --
CREATE OR REPLACE FUNCTION cdb_dataservices_server._get_redis_conf_v2(config_key text)
RETURNS cdb_dataservices_server._redis_conf_params AS $$
conf_query = "SELECT cartodb.CDB_Conf_GetConf('{0}') as conf".format(config_key)
conf = plpy.execute(conf_query)[0]['conf']
if conf is None:
plpy.error("There is no redis configuration defined")
else:
import json
params = json.loads(conf)
return {
"sentinel_host": params['sentinel_host'],
"sentinel_port": params['sentinel_port'],
"sentinel_master_id": params['sentinel_master_id'],
"timeout": params['timeout'],
"redis_db": params['redis_db']
}
$$ LANGUAGE plpythonu;
-- Get the connection to redis from cache or create a new one
CREATE OR REPLACE FUNCTION cdb_dataservices_server._connect_to_redis(user_id text)
RETURNS boolean AS $$
cache_key = "redis_connection_{0}".format(user_id)
if cache_key in GD:
return False
else:
from cartodb_services.tools import RedisConnection
metadata_config_params = plpy.execute("""select c.sentinel_host, c.sentinel_port,
c.sentinel_master_id, c.timeout, c.redis_db
from cdb_dataservices_server._get_redis_conf_v2('redis_metadata_config') c;""")[0]
metrics_config_params = plpy.execute("""select c.sentinel_host, c.sentinel_port,
c.sentinel_master_id, c.timeout, c.redis_db
from cdb_dataservices_server._get_redis_conf_v2('redis_metrics_config') c;""")[0]
redis_metadata_connection = RedisConnection(metadata_config_params['sentinel_host'],
metadata_config_params['sentinel_port'],
metadata_config_params['sentinel_master_id'],
timeout=metadata_config_params['timeout'],
redis_db=metadata_config_params['redis_db']).redis_connection()
redis_metrics_connection = RedisConnection(metrics_config_params['sentinel_host'],
metrics_config_params['sentinel_port'],
metrics_config_params['sentinel_master_id'],
timeout=metrics_config_params['timeout'],
redis_db=metrics_config_params['redis_db']).redis_connection()
GD[cache_key] = {
'redis_metadata_connection': redis_metadata_connection,
'redis_metrics_connection': redis_metrics_connection,
}
return True
$$ LANGUAGE plpythonu SECURITY DEFINER;
-- Mapzen integration
DROP FUNCTION IF EXISTS cdb_dataservices_server.cdb_route_point_to_point(TEXT, TEXT, geometry(Point, 4326), geometry(Point, 4326), TEXT, text[], text);
DROP FUNCTION IF EXISTS cdb_dataservices_server._cdb_mapzen_route_point_to_point(TEXT, TEXT, geometry(Point, 4326), geometry(Point, 4326), TEXT, text[], text);
DROP FUNCTION IF EXISTS cdb_dataservices_server._get_routing_config(text, text);
DROP TYPE IF EXISTS cdb_dataservices_server.simple_route;

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
comment = 'CartoDB dataservices server extension' comment = 'CartoDB dataservices server extension'
default_version = '0.4.0' default_version = '0.5.0'
requires = 'plpythonu, postgis, cdb_geocoder' requires = 'plpythonu, postgis, cdb_geocoder'
superuser = true superuser = true
schema = cdb_dataservices_server schema = cdb_dataservices_server

View File

@@ -0,0 +1 @@
../0.4.0/00_header.sql

View File

@@ -0,0 +1,55 @@
CREATE TYPE cdb_dataservices_server.simple_route AS (
shape geometry(LineString,4326),
length real,
duration integer
);
CREATE OR REPLACE FUNCTION cdb_dataservices_server._cdb_mapzen_route_point_to_point(
username TEXT,
orgname TEXT,
origin geometry(Point, 4326),
destination geometry(Point, 4326),
mode TEXT,
options text[] DEFAULT ARRAY[]::text[],
units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_server.simple_route AS $$
import json
from cartodb_services.mapzen import MapzenRouting, MapzenRoutingResponse
from cartodb_services.mapzen.types import polyline_to_linestring
from cartodb_services.metrics import QuotaService
from cartodb_services.tools import Coordinate
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
user_routing_config = GD["user_routing_config_{0}".format(username)]
quota_service = QuotaService(user_routing_config, redis_conn)
try:
client = MapzenRouting(user_routing_config.mapzen_app_key)
orig_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % origin)[0]['lat']
orig_lon = plpy.execute("SELECT ST_X('%s') AS lon" % origin)[0]['lon']
origin_coordinates = Coordinate(orig_lon, orig_lat)
dest_lat = plpy.execute("SELECT ST_Y('%s') AS lat" % destination)[0]['lat']
dest_lon = plpy.execute("SELECT ST_X('%s') AS lon" % destination)[0]['lon']
dest_coordinates = Coordinate(dest_lon, dest_lat)
resp = client.calculate_route_point_to_point(origin_coordinates, dest_coordinates, mode, options, units)
if resp:
shape_linestring = polyline_to_linestring(resp.shape)
quota_service.increment_success_geocoder_use()
return [shape_linestring, resp.length, resp.duration]
else:
quota_service.increment_empty_geocoder_use()
except BaseException as e:
import sys, traceback
type_, value_, traceback_ = sys.exc_info()
quota_service.increment_failed_geocoder_use()
error_msg = 'There was an error trying to obtain route using mapzen provider: {0}'.format(e)
plpy.notice(traceback.format_tb(traceback_))
plpy.error(error_msg)
finally:
quota_service.increment_total_geocoder_use()
$$ LANGUAGE plpythonu SECURITY DEFINER;

View File

@@ -0,0 +1,18 @@
CREATE OR REPLACE FUNCTION cdb_dataservices_server.cdb_route_point_to_point(
username TEXT,
orgname TEXT,
origin geometry(Point, 4326),
destination geometry(Point, 4326),
mode TEXT,
options text[] DEFAULT ARRAY[]::text[],
units text DEFAULT 'kilometers')
RETURNS cdb_dataservices_server.simple_route AS $$
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metrics_connection']
plpy.execute("SELECT cdb_dataservices_server._get_routing_config({0}, {1})".format(plpy.quote_nullable(username), plpy.quote_nullable(orgname)))
user_routing_config = GD["user_routing_config_{0}".format(username)]
mapzen_plan = plpy.prepare("SELECT * FROM cdb_dataservices_server._cdb_mapzen_route_point_to_point($1, $2, $3, $4, $5, $6, $7) as route;", ["text", "text", "geometry(Point, 4326)", "geometry(Point, 4326)", "text", "text[]", "text"])
result = plpy.execute(mapzen_plan, [username, orgname, origin, destination, mode, options, units])
return [result[0]['shape'],result[0]['length'], result[0]['duration']]
$$ LANGUAGE plpythonu;

View File

@@ -0,0 +1,62 @@
CREATE TYPE cdb_dataservices_server._redis_conf_params AS (
sentinel_master_id text,
redis_host text,
redis_port int,
redis_db text,
timeout float
);
-- Get the Redis configuration from the _conf table --
CREATE OR REPLACE FUNCTION cdb_dataservices_server._get_redis_conf_v2(config_key text)
RETURNS cdb_dataservices_server._redis_conf_params AS $$
conf_query = "SELECT cartodb.CDB_Conf_GetConf('{0}') as conf".format(config_key)
conf = plpy.execute(conf_query)[0]['conf']
if conf is None:
plpy.error("There is no redis configuration defined")
else:
import json
params = json.loads(conf)
redis_conf_params = {
"redis_host": params['redis_host'],
"redis_port": params['redis_port'],
"timeout": params['timeout'],
"redis_db": params['redis_db']
}
if "sentinel_master_id" in params:
redis_conf_params["sentinel_master_id"] = params["sentinel_master_id"]
else:
redis_conf_params["sentinel_master_id"] = None
return redis_conf_params
$$ LANGUAGE plpythonu;
-- Get the connection to redis from cache or create a new one
CREATE OR REPLACE FUNCTION cdb_dataservices_server._connect_to_redis(user_id text)
RETURNS boolean AS $$
cache_key = "redis_connection_{0}".format(user_id)
if cache_key in GD:
return False
else:
from cartodb_services.tools import RedisConnection
metadata_config_params = plpy.execute("""select c.sentinel_master_id, c.redis_host,
c.redis_port, c.timeout, c.redis_db
from cdb_dataservices_server._get_redis_conf_v2('redis_metadata_config') c;""")[0]
metrics_config_params = plpy.execute("""select c.sentinel_master_id, c.redis_host,
c.redis_port, c.timeout, c.redis_db
from cdb_dataservices_server._get_redis_conf_v2('redis_metrics_config') c;""")[0]
redis_metadata_connection = RedisConnection(metadata_config_params['sentinel_master_id'],
metadata_config_params['redis_host'],
metadata_config_params['redis_port'],
timeout=metadata_config_params['timeout'],
redis_db=metadata_config_params['redis_db']).redis_connection()
redis_metrics_connection = RedisConnection(metrics_config_params['sentinel_master_id'],
metrics_config_params['redis_host'],
metrics_config_params['redis_port'],
timeout=metrics_config_params['timeout'],
redis_db=metrics_config_params['redis_db']).redis_connection()
GD[cache_key] = {
'redis_metadata_connection': redis_metadata_connection,
'redis_metrics_connection': redis_metrics_connection,
}
return True
$$ LANGUAGE plpythonu SECURITY DEFINER;

View File

@@ -0,0 +1,75 @@
-- Get the Redis configuration from the _conf table --
CREATE OR REPLACE FUNCTION cdb_dataservices_server._get_geocoder_config(username text, orgname text)
RETURNS boolean AS $$
cache_key = "user_geocoder_config_{0}".format(username)
if cache_key in GD:
return False
else:
import json
from cartodb_services.metrics import GeocoderConfig
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metadata_connection']
heremaps_conf_json = plpy.execute("SELECT cartodb.CDB_Conf_GetConf('heremaps_conf') as heremaps_conf", 1)[0]['heremaps_conf']
if not heremaps_conf_json:
heremaps_app_id = None
heremaps_app_code = None
else:
heremaps_conf = json.loads(heremaps_conf_json)
heremaps_app_id = heremaps_conf['app_id']
heremaps_app_code = heremaps_conf['app_code']
geocoder_config = GeocoderConfig(redis_conn, username, orgname, heremaps_app_id, heremaps_app_code)
# --Think about the security concerns with this kind of global cache, it should be only available
# --for this user session but...
GD[cache_key] = geocoder_config
return True
$$ LANGUAGE plpythonu SECURITY DEFINER;
-- Get the Redis configuration from the _conf table --
CREATE OR REPLACE FUNCTION cdb_dataservices_server._get_isolines_routing_config(username text, orgname text)
RETURNS boolean AS $$
cache_key = "user_isolines_routing_config_{0}".format(username)
if cache_key in GD:
return False
else:
import json
from cartodb_services.metrics import IsolinesRoutingConfig
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metadata_connection']
heremaps_conf_json = plpy.execute("SELECT cartodb.CDB_Conf_GetConf('heremaps_conf') as heremaps_conf", 1)[0]['heremaps_conf']
if not heremaps_conf_json:
heremaps_app_id = None
heremaps_app_code = None
else:
heremaps_conf = json.loads(heremaps_conf_json)
heremaps_app_id = heremaps_conf['app_id']
heremaps_app_code = heremaps_conf['app_code']
isolines_routing_config = IsolinesRoutingConfig(redis_conn, username, orgname, heremaps_app_id, heremaps_app_code)
# --Think about the security concerns with this kind of global cache, it should be only available
# --for this user session but...
GD[cache_key] = isolines_routing_config
return True
$$ LANGUAGE plpythonu SECURITY DEFINER;
-- Get the Redis configuration from the _conf table --
CREATE OR REPLACE FUNCTION cdb_dataservices_server._get_routing_config(username text, orgname text)
RETURNS boolean AS $$
cache_key = "user_routing_config_{0}".format(username)
if cache_key in GD:
return False
else:
import json
from cartodb_services.metrics import RoutingConfig
plpy.execute("SELECT cdb_dataservices_server._connect_to_redis('{0}')".format(username))
redis_conn = GD["redis_connection_{0}".format(username)]['redis_metadata_connection']
mapzen_conf_json = plpy.execute("SELECT cartodb.CDB_Conf_GetConf('mapzen_conf') as mapzen_conf", 1)[0]['mapzen_conf']
if not mapzen_conf_json:
mapzen_app_key = None
else:
mapzen_conf = json.loads(mapzen_conf_json)
mapzen_app_key = mapzen_conf['routing_app_key']
routing_config = RoutingConfig(redis_conn, username, orgname, mapzen_app_key)
# --Think about the security concerns with this kind of global cache, it should be only available
# --for this user session but...
GD[cache_key] = routing_config
return True
$$ LANGUAGE plpythonu SECURITY DEFINER;

View File

@@ -0,0 +1 @@
../0.4.0/20_geocode_street.sql

View File

@@ -0,0 +1 @@
../0.4.0/30_admin0.sql

View File

@@ -0,0 +1 @@
../0.4.0/40_admin1.sql

View File

@@ -0,0 +1 @@
../0.4.0/50_namedplaces.sql

View File

@@ -0,0 +1 @@
../0.4.0/60_postalcodes.sql

View File

@@ -0,0 +1 @@
../0.4.0/70_ips.sql

View File

@@ -0,0 +1 @@
../0.4.0/80_isolines_helper.sql

View File

@@ -0,0 +1 @@
../0.4.0/85_isodistance.sql

View File

@@ -0,0 +1 @@
../0.4.0/90_isochrone.sql

View File

@@ -0,0 +1,15 @@
DO $$
BEGIN
IF NOT EXISTS (
SELECT *
FROM pg_catalog.pg_user
WHERE usename = 'geocoder_api') THEN
CREATE USER geocoder_api;
END IF;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_dataservices_server TO geocoder_api;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO geocoder_api;
GRANT USAGE ON SCHEMA cdb_dataservices_server TO geocoder_api;
GRANT USAGE ON SCHEMA public TO geocoder_api;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO geocoder_api;
END$$;

View File

@@ -0,0 +1,42 @@
-- Install dependencies
CREATE EXTENSION postgis;
CREATE EXTENSION schema_triggers;
CREATE EXTENSION plpythonu;
CREATE EXTENSION cartodb;
CREATE EXTENSION cdb_geocoder;
-- Install the extension
CREATE EXTENSION cdb_dataservices_server;
-- Mock the redis server connection to point to this very test db
SELECT cartodb.cdb_conf_setconf('redis_metrics_config', '{"redis_host": "localhost", "redis_port": 26379, "sentinel_master_id": "mymaster", "timeout": 0.1, "redis_db": 5}');
cdb_conf_setconf
------------------
(1 row)
SELECT cartodb.cdb_conf_setconf('redis_metadata_config', '{"redis_host": "localhost", "redis_port": 26379, "sentinel_master_id": "mymaster", "timeout": 0.1, "redis_db": 5}');
cdb_conf_setconf
------------------
(1 row)
SELECT cartodb.cdb_conf_setconf('mapzen_conf', '{"app_key": "dummy_key"}');
cdb_conf_setconf
------------------
(1 row)
-- Mock the varnish invalidation function
-- (used by cdb_geocoder tests)
CREATE OR REPLACE FUNCTION public.cdb_invalidate_varnish(table_name text) RETURNS void AS $$
BEGIN
RETURN;
END
$$
LANGUAGE plpgsql;
-- Set user quota
SELECT cartodb.CDB_SetUserQuotaInBytes(0);
cdb_setuserquotainbytes
-------------------------
0
(1 row)

View File

@@ -0,0 +1 @@
../../0.4.0/expected/20_street_test.out

View File

@@ -0,0 +1 @@
../../0.4.0/expected/30_admin0_test.out

View File

@@ -0,0 +1 @@
../../0.4.0/expected/40_admin1_test.out

View File

@@ -0,0 +1 @@
../../0.4.0/expected/50_namedplaces_test.out

View File

@@ -0,0 +1 @@
../../0.4.0/expected/60_postalcodes_test.out

View File

@@ -0,0 +1 @@
../../0.4.0/expected/70_ips_test.out

View File

@@ -0,0 +1 @@
../../0.4.0/expected/85_isodistance_test.out

View File

@@ -0,0 +1 @@
../../0.4.0/expected/90_isochrone_test.out

View File

@@ -0,0 +1,12 @@
-- Check for routing point to point signatures
SELECT exists(SELECT *
FROM pg_proc p
INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid)
WHERE ns.nspname = 'cdb_dataservices_server'
AND proname = 'cdb_route_point_to_point'
AND oidvectortypes(p.proargtypes) = 'text, text, geometry, geometry, text, text[], text');
exists
--------
t
(1 row)

View File

@@ -0,0 +1,5 @@
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_dataservices_server FROM geocoder_api;
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM geocoder_api;
REVOKE USAGE ON SCHEMA cdb_dataservices_server FROM geocoder_api;
REVOKE USAGE ON SCHEMA public FROM geocoder_api;
REVOKE SELECT ON ALL TABLES IN SCHEMA public FROM geocoder_api;

View File

@@ -0,0 +1,26 @@
-- Install dependencies
CREATE EXTENSION postgis;
CREATE EXTENSION schema_triggers;
CREATE EXTENSION plpythonu;
CREATE EXTENSION cartodb;
CREATE EXTENSION cdb_geocoder;
-- Install the extension
CREATE EXTENSION cdb_dataservices_server;
-- Mock the redis server connection to point to this very test db
SELECT cartodb.cdb_conf_setconf('redis_metrics_config', '{"redis_host": "localhost", "redis_port": 26379, "sentinel_master_id": "mymaster", "timeout": 0.1, "redis_db": 5}');
SELECT cartodb.cdb_conf_setconf('redis_metadata_config', '{"redis_host": "localhost", "redis_port": 26379, "sentinel_master_id": "mymaster", "timeout": 0.1, "redis_db": 5}');
SELECT cartodb.cdb_conf_setconf('mapzen_conf', '{"app_key": "dummy_key"}');
-- Mock the varnish invalidation function
-- (used by cdb_geocoder tests)
CREATE OR REPLACE FUNCTION public.cdb_invalidate_varnish(table_name text) RETURNS void AS $$
BEGIN
RETURN;
END
$$
LANGUAGE plpgsql;
-- Set user quota
SELECT cartodb.CDB_SetUserQuotaInBytes(0);

View File

@@ -0,0 +1 @@
../../0.4.0/sql/20_street_test.sql

View File

@@ -0,0 +1 @@
../../0.4.0/sql/30_admin0_test.sql

View File

@@ -0,0 +1 @@
../../0.4.0/sql/40_admin1_test.sql

View File

@@ -0,0 +1 @@
../../0.4.0/sql/50_namedplaces_test.sql

View File

@@ -0,0 +1 @@
../../0.4.0/sql/60_postalcodes_test.sql

View File

@@ -0,0 +1 @@
../../0.4.0/sql/70_ips_test.sql

View File

@@ -0,0 +1 @@
../../0.4.0/sql/85_isodistance_test.sql

View File

@@ -0,0 +1 @@
../../0.4.0/sql/90_isochrone_test.sql

View File

@@ -0,0 +1,7 @@
-- Check for routing point to point signatures
SELECT exists(SELECT *
FROM pg_proc p
INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid)
WHERE ns.nspname = 'cdb_dataservices_server'
AND proname = 'cdb_route_point_to_point'
AND oidvectortypes(p.proargtypes) = 'text, text, geometry, geometry, text, text[], text');

View File

@@ -0,0 +1,5 @@
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA cdb_dataservices_server FROM geocoder_api;
REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM geocoder_api;
REVOKE USAGE ON SCHEMA cdb_dataservices_server FROM geocoder_api;
REVOKE USAGE ON SCHEMA public FROM geocoder_api;
REVOKE SELECT ON ALL TABLES IN SCHEMA public FROM geocoder_api;

View File

@@ -0,0 +1 @@
from routing import MapzenRouting, MapzenRoutingResponse

View File

@@ -0,0 +1,21 @@
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import json
class WrongParams(Exception):
def __init__(self, value):
self.value = value
def __str__(self):
return repr('Wrong parameters passed: ' + json.dumps(self.value))
class MalformedResult(Exception):
def __str__(self):
return repr('Result structure is malformed')
class TimeoutException(Exception):
def __str__(self):
return repr('Timeout requesting to mapzen server')

View File

@@ -0,0 +1,47 @@
import time
from datetime import datetime
from exceptions import TimeoutException
DEFAULT_RETRY_TIMEOUT = 60
def qps_retry(f):
def wrapped_f(*args, **kw):
return QPSService().call(f, *args, **kw)
return wrapped_f
class QPSService:
def __init__(self, queries_per_second=10,
retry_timeout=DEFAULT_RETRY_TIMEOUT):
self._queries_per_second = queries_per_second
self._retry_timeout = retry_timeout
def call(self, fn, *args, **kwargs):
start_time = datetime.now()
attempt_number = 1
while True:
try:
return fn(*args, **kwargs)
except Exception as e:
if hasattr(e, 'response') and e.response.status_code == 429:
self.retry(start_time, attempt_number)
else:
raise e
attempt_number += 1
def retry(self, first_request_time, retry_count):
elapsed = datetime.now() - first_request_time
if elapsed.seconds > self._retry_timeout:
raise TimeoutException()
# inverse qps * (1.5 ^ i) is an increased sleep time of 1.5x per
# iteration.
delay = (1.0/self._queries_per_second) * 1.5 ** retry_count
# https://www.awsarchitectureblog.com/2015/03/backoff.html
# https://github.com/googlemaps/google-maps-services-python/blob/master/googlemaps/client.py#L193
sleep_time = delay * (random.random() + 0.5)
time.sleep(sleep_time)

View File

@@ -0,0 +1,119 @@
import requests
import json
import re
from exceptions import WrongParams, MalformedResult
from qps import qps_retry
from cartodb_services.tools import Coordinate, PolyLine
class MapzenRouting:
'A Mapzen Routing wrapper for python'
PRODUCTION_ROUTING_BASE_URL = 'https://valhalla.mapzen.com/route'
ACCEPTED_MODES = {
"walk": "pedestrian",
"car": "auto",
"public_transport": "bus",
"bicycle": "bicycle"
}
AUTO_SHORTEST = 'auto_shortest'
OPTIONAL_PARAMS = [
'mode_type',
]
METRICS_UNITS = 'kilometers'
IMPERIAL_UNITS = 'miles'
def __init__(self, app_key, base_url=PRODUCTION_ROUTING_BASE_URL):
self._app_key = app_key
self._url = base_url
@qps_retry
def calculate_route_point_to_point(self, origin, destination, mode,
options=[], units=METRICS_UNITS):
parsed_options = self.__parse_options(options)
mode_param = self.__parse_mode_param(mode, parsed_options)
directions = self.__parse_directions(origin, destination)
json_request_params = self.__parse_json_parameters(directions,
mode_param,
units)
request_params = self.__parse_request_parameters(json_request_params)
response = requests.get(self._url, params=request_params)
if response.status_code == requests.codes.ok:
return self.__parse_routing_response(response.text)
else:
response.raise_for_status()
def __parse_options(self, options):
return dict(option.split('=') for option in options)
def __parse_request_parameters(self, json_request):
request_options = {"json": json_request}
request_options.update({'api_key': self._app_key})
return request_options
def __parse_json_parameters(self, directions, mode, units):
json_options = directions
json_options.update({'costing': self.ACCEPTED_MODES[mode]})
json_options.update({"directions_options": {'units': units,
'narrative': False}})
return json.dumps(json_options)
def __parse_directions(self, origin, destination):
return {"locations": [
{"lon": origin.longitude, "lat": origin.latitude},
{"lon": destination.longitude, "lat": destination.latitude}
]}
def __parse_routing_response(self, response):
try:
parsed_json_response = json.loads(response)
legs = parsed_json_response['trip']['legs'][0]
shape = PolyLine().decode(legs['shape'])
length = legs['summary']['length']
duration = legs['summary']['time']
routing_response = MapzenRoutingResponse(shape, length, duration)
return routing_response
except IndexError:
return []
except KeyError:
raise MalformedResult()
def __parse_mode_param(self, mode, options):
if mode in self.ACCEPTED_MODES:
mode_source = self.ACCEPTED_MODES[mode]
else:
raise WrongParams("{0} is not an accepted mode type".format(mode))
if mode == self.ACCEPTED_MODES['car'] and 'mode_type' in options and \
options['mode_type'] == 'shortest':
mode = self.AUTO_SHORTEST
return mode
class MapzenRoutingResponse:
def __init__(self, shape, length, duration):
self._shape = shape
self._length = length
self._duration = duration
@property
def shape(self):
return self._shape
@property
def length(self):
return self._length
@property
def duration(self):
return self._duration

View File

@@ -0,0 +1,16 @@
import plpy
def polyline_to_linestring(polyline):
"""Convert a Mapzen polyline shape to a PostGIS multipolygon"""
coordinates = []
for point in polyline:
# Divide by 10 because mapzen uses one more decimal than the
# google standard (https://mapzen.com/documentation/turn-by-turn/decoding/)
coordinates.append("%s %s" % (point[1]/10, point[0]/10))
wkt_coordinates = ','.join(coordinates)
sql = "SELECT ST_GeomFromText('LINESTRING({0})', 4326) as geom".format(wkt_coordinates)
geometry = plpy.execute(sql, 1)[0]['geom']
return geometry

View File

@@ -1,3 +1,3 @@
from config import GeocoderConfig, IsolinesRoutingConfig, InternalGeocoderConfig, ConfigException from config import GeocoderConfig, IsolinesRoutingConfig, InternalGeocoderConfig, RoutingConfig, ConfigException
from quota import QuotaService from quota import QuotaService
from user import UserMetricsService from user import UserMetricsService

View File

@@ -27,6 +27,27 @@ class ServiceConfig(object):
def organization(self): def organization(self):
return self._orgname return self._orgname
class RoutingConfig(ServiceConfig):
ROUTING_CONFIG_KEYS = ['username', 'orgname', 'mapzen_app_key']
MAPZEN_APP_KEY = 'mapzen_app_key'
USERNAME_KEY = 'username'
ORGNAME_KEY = 'orgname'
def __init__(self, redis_connection, username, orgname=None,
mapzen_app_key=None):
super(RoutingConfig, self).__init__(redis_connection, username,
orgname)
self._mapzen_app_key = mapzen_app_key
@property
def service_type(self):
return 'routing_mapzen'
@property
def mapzen_app_key(self):
return self._mapzen_app_key
class IsolinesRoutingConfig(ServiceConfig): class IsolinesRoutingConfig(ServiceConfig):

View File

@@ -1 +1,3 @@
from redis_tools import RedisConnection from redis_tools import RedisConnection
from coordinates import Coordinate
from polyline import PolyLine

View File

@@ -0,0 +1,19 @@
class Coordinate:
"""Class that represents a generic form of coordinates to be used
by the services """
def __init__(self, longitude, latitude):
self._longitude = longitude
self._latitude = latitude
@property
def latitude(self):
return self._latitude
@property
def longitude(self):
return self._longitude
def to_json(self):
return "{{\"lon\": {0},\"lat\": {1}}}".format(self._longitude,
self._latitude)

View File

@@ -0,0 +1,51 @@
from itertools import tee, izip
from math import trunc
class PolyLine:
""" Polyline decoder https://developers.google.com/maps/documentation/utilities/polylinealgorithm?csw=1 """
def decode(self, data):
coordinates = []
chunks = self._extract_chunks(data)
for chunk in chunks:
coordinate = self._process_chunk(chunk)
coordinate /= 1e5
print coordinate
if len(coordinates) > 1:
# We have to sum the previous with the offset in this chunk
coordinate += coordinates[-2]
coordinates.append(round(coordinate, 5))
print coordinates
return zip(coordinates, coordinates[1:])[::2]
def _extract_chunks(self, data):
chunks, chunk = [], []
for character in data:
byte = ord(character) - 63
if byte & 0x20 > 0:
byte &= 0x1F
chunk.append(byte)
else:
chunk.append(byte)
chunks.append(chunk)
chunk = []
return chunks
def _process_chunk(self, chunk):
coordinate = self._get_coordinate(chunk)
# Check if the coordinate is negative
if coordinate & 0x1:
return ~(coordinate >> 1)
else:
return coordinate >> 1
def _get_coordinate(self, chunk):
coordinate = 0
for i, c in enumerate(chunk):
coordinate |= c << (i * 5)
return coordinate

View File

@@ -1,16 +1,16 @@
from redis.sentinel import Sentinel from redis.sentinel import Sentinel
from redis import StrictRedis
class RedisConnection: class RedisConnection:
REDIS_DEFAULT_USER_DB = 5 REDIS_DEFAULT_USER_DB = 5
REDIS_DEFAULT_TIMEOUT = 2 #seconds REDIS_DEFAULT_TIMEOUT = 2 #seconds
REDIS_SENTINEL_DEFAULT_PORT = 26379
def __init__(self, sentinel_host, sentinel_port, sentinel_master_id, def __init__(self, sentinel_master_id, redis_host, redis_port,
redis_db=REDIS_DEFAULT_USER_DB, **kwargs): redis_db=REDIS_DEFAULT_USER_DB, **kwargs):
self.sentinel_host = sentinel_host self.redis_host = redis_host
self.sentinel_port = sentinel_port self.redis_port = redis_port
self.sentinel_master_id = sentinel_master_id self.sentinel_master_id = sentinel_master_id
self.timeout = kwargs['timeout'] if 'timeout' in kwargs else self.REDIS_DEFAULT_TIMEOUT self.timeout = kwargs['timeout'] if 'timeout' in kwargs else self.REDIS_DEFAULT_TIMEOUT
self.redis_db = redis_db self.redis_db = redis_db
@@ -19,11 +19,14 @@ class RedisConnection:
return self.__create_redis_connection() return self.__create_redis_connection()
def __create_redis_connection(self): def __create_redis_connection(self):
sentinel = Sentinel([(self.sentinel_host, if self.sentinel_master_id == None:
self.REDIS_SENTINEL_DEFAULT_PORT)], return StrictRedis(host=self.redis_host, port=self.redis_port, db=self.redis_db)
socket_timeout=self.timeout) else:
return sentinel.master_for( sentinel = Sentinel([(self.redis_host,
self.sentinel_master_id, self.redis_port)],
socket_timeout=self.timeout, socket_timeout=self.timeout)
db=self.redis_db return sentinel.master_for(
) self.sentinel_master_id,
socket_timeout=self.timeout,
db=self.redis_db
)

View File

@@ -10,7 +10,7 @@ from setuptools import setup, find_packages
setup( setup(
name='cartodb_services', name='cartodb_services',
version='0.2.0', version='0.3.1',
description='CartoDB Services API Python Library', description='CartoDB Services API Python Library',

View File

@@ -148,7 +148,6 @@ class HereMapsRoutingIsolineTestCase(unittest.TestCase):
u'32.9699707,0.9462833']) u'32.9699707,0.9462833'])
def test_calculate_isochrone_with_valid_params(self, req_mock): def test_calculate_isochrone_with_valid_params(self, req_mock):
print self.isoline_url
url = "{0}?start=geo%2133.0%2C1.0&mode=shortest%3Bcar".format(self.isoline_url) url = "{0}?start=geo%2133.0%2C1.0&mode=shortest%3Bcar".format(self.isoline_url)
req_mock.register_uri('GET', url, text=self.GOOD_RESPONSE) req_mock.register_uri('GET', url, text=self.GOOD_RESPONSE)
response = self.routing.calculate_isochrone('geo!33.0,1.0', 'car', response = self.routing.calculate_isochrone('geo!33.0,1.0', 'car',

View File

@@ -0,0 +1,83 @@
#!/usr/local/bin/python
# -*- coding: utf-8 -*-
import unittest
import requests_mock
import re
from nose.tools import assert_raises
from urlparse import urlparse, parse_qs
from cartodb_services.mapzen import MapzenRouting, MapzenRoutingResponse
from cartodb_services.mapzen.exceptions import WrongParams
from cartodb_services.tools import Coordinate
requests_mock.Mocker.TEST_PREFIX = 'test_'
@requests_mock.Mocker()
class MapzenRoutingTestCase(unittest.TestCase):
GOOD_SHAPE = [(38.5, -120.2), (43.2, -126.4)]
GOOD_RESPONSE = """{{
"id": "ethervoid-route",
"trip": {{
"status": 0,
"status_message": "Found route between points",
"legs": [
{{
"shape": "_p~iF~ps|U_~t[~|yd@",
"summary": {{
"length": 444.59,
"time": 16969
}}
}}
],
"units": "kilometers",
"summary": {{
"length": 444.59,
"time": 16969
}},
"locations": [
{{
"lon": -120.2,
"lat": 38.5,
"type": "break"
}},
{{
"lon": -126.4,
"lat": 43.2,
"type": "break"
}}
]
}}
}}""".format(GOOD_SHAPE)
MALFORMED_RESPONSE = """{"manolo": "escobar"}"""
def setUp(self):
self.routing = MapzenRouting('api_key')
self.url = MapzenRouting.PRODUCTION_ROUTING_BASE_URL
def test_calculate_simple_routing_with_valid_params(self, req_mock):
req_mock.register_uri('GET', requests_mock.ANY,
text=self.GOOD_RESPONSE)
origin = Coordinate('-120.2', '38.5')
destination = Coordinate('-126.4', '43.2')
response = self.routing.calculate_route_point_to_point(origin,
destination,
'car')
self.assertEqual(response.shape, self.GOOD_SHAPE)
self.assertEqual(response.length, 444.59)
self.assertEqual(response.duration, 16969)
def test_uknown_mode_raise_exception(self, req_mock):
req_mock.register_uri('GET', requests_mock.ANY,
text=self.GOOD_RESPONSE)
origin = Coordinate('-120.2', '38.5')
destination = Coordinate('-126.4', '43.2')
assert_raises(WrongParams,
self.routing.calculate_route_point_to_point,
origin, destination, 'unknown')

View File

@@ -0,0 +1,39 @@
from cartodb_services.tools import PolyLine
from unittest import TestCase
class TestPolyline(TestCase):
def setUp(self):
self.polyline = PolyLine()
def test_should_decode_a_chunk_correctly(self):
decoded_polyline = self.polyline.decode('`~oia@`~oia@')
original_value = [(-179.98321, -179.98321)]
assert decoded_polyline == original_value
def test_should_decode_polyline_correctly(self):
original_polyline_1 = [(38.5, -120.2),
(40.7, -120.95),
(43.252, -126.453)]
decoded_polyline_1 = self.polyline.decode('_p~iF~ps|U_ulLnnqC_mqNvxq`@')
assert decoded_polyline_1 == original_polyline_1
original_polyline_2 = [(17.95783,-5.58105),
(15.79225,2.90039),
(7.60211,-10.76660)]
decoded_polyline_2 = self.polyline.decode('mkrlBp`aa@z}eL_pwr@js~p@tilrA')
assert decoded_polyline_2 == original_polyline_2
original_polyline_3 = [(62.75473,-157.14844),
(65.07213,169.80469) ,
(48.92250,158.55469),
(44.33957,-150.46875)]
decoded_polyline_3 = self.polyline.decode('ax_~Jv`d~\wrcMa`qj}@dfqaBngtcAhb~Zncc}y@')
assert decoded_polyline_3 == original_polyline_3

View File

@@ -0,0 +1,34 @@
from unittest import TestCase
from nose.tools import assert_raises
from nose.tools import assert_not_equal, assert_equal
from ..helpers.integration_test_helper import IntegrationTestHelper
class TestRoutingFunctions(TestCase):
def setUp(self):
self.env_variables = IntegrationTestHelper.get_environment_variables()
self.sql_api_url = "https://{0}.{1}/api/v2/sql".format(
self.env_variables['username'],
self.env_variables['host'],
self.env_variables['api_key']
)
def test_if_select_with_routing_point_to_point_is_ok(self):
query = "SELECT duration, length, shape as the_geom " \
"FROM cdb_route_point_to_point('POINT(-3.70237112 40.41706163)'::geometry, " \
"'POINT(-3.69909883 40.41236875)'::geometry, 'car', " \
"ARRAY['mode_type=shortest']::text[])&api_key={0}".format(
self.env_variables['api_key'])
routing = IntegrationTestHelper.execute_query(self.sql_api_url, query)
assert_not_equal(routing['the_geom'], None)
def test_if_select_with_routing_point_to_point_without_api_key_raise_error(self):
query = "SELECT duration, length, shape as the_geom " \
"FROM cdb_route_point_to_point('POINT(-3.70237112 40.41706163)'::geometry, " \
"'POINT(-3.69909883 40.41236875)'::geometry, 'car', " \
"ARRAY['mode_type=shortest']::text[])"
try:
IntegrationTestHelper.execute_query(self.sql_api_url, query)
except Exception as e:
assert_equal(e.message[0], "The api_key must be provided")