Skip to content

Commit

Permalink
Breaking migration into 2 parts
Browse files Browse the repository at this point in the history
  • Loading branch information
underbluewaters committed Nov 13, 2024
1 parent 030acfa commit 98d3e04
Showing 1 changed file with 0 additions and 271 deletions.
271 changes: 0 additions & 271 deletions packages/api/migrations/current.sql
Original file line number Diff line number Diff line change
Expand Up @@ -4,274 +4,3 @@ alter table sketch_classes add column if not exists filter_api_server_location t

alter type sketch_geometry_type add value if not exists 'FILTERED_PLANNING_UNITS';

set role seasketch_superuser;
delete from sketch_classes where project_id = (select id from projects where slug = 'superuser') and name = 'Filtered Planning Units';

insert into sketch_classes(
project_id,
name,
geometry_type,
mapbox_gl_style,
is_template,
template_description
) values (
(select id from projects where slug = 'superuser'),
'Filtered Planning Units',
'FILTERED_PLANNING_UNITS'::sketch_geometry_type,
'{}'::jsonb,
true,
'Filter polygons by criteria. Requires an API server.'
) on conflict do nothing;

select initialize_sketch_class_form_from_template((select id from sketch_classes where name = 'Filtered Planning Units' and is_template = true), (select id from forms where is_template = true and template_type = 'SKETCHES' and template_name = 'Basic Template'));
set role postgres;

GRANT update (filter_api_server_location) on sketch_classes to seasketch_user;
GRANT update (filter_api_version) on sketch_classes to seasketch_user;

delete from form_element_types where component_name = 'FilterInput';
insert into form_element_types (
component_name,
label,
is_input,
is_surveys_only
) values (
'FilterInput',
'Filter Input',
true,
false
);

CREATE OR REPLACE FUNCTION public.before_sketch_insert_or_update() RETURNS trigger
LANGUAGE plpgsql SECURITY DEFINER
AS $$
declare
class_geometry_type sketch_geometry_type;
allow_multi boolean;
incoming_geometry_type text;
new_geometry_type text;
parent_collection_id int;
begin
select
geometry_type,
sketch_classes.allow_multi
into
class_geometry_type,
allow_multi,
incoming_geometry_type
from
sketch_classes
where
id = NEW.sketch_class_id;
if NEW.folder_id is not null and NEW.collection_id is not null then
raise exception 'Parent cannot be to both folder and collection';
end if;
if class_geometry_type = 'COLLECTION' then
-- Also check for parent collection of parent folder (recursively)
if NEW.collection_id is not null then
if (select get_parent_collection_id('sketch', NEW.collection_id)) is not null then
raise exception 'Nested collections are not allowed';
end if;
elsif NEW.folder_id is not null then
if (select get_parent_collection_id('sketch_folder', NEW.folder_id)) is not null then
raise exception 'Nested collections are not allowed';
end if;
end if;
-- geom must be present unless a collection
if NEW.geom is not null or NEW.user_geom is not null then
raise exception 'Collections should not have geometry';
else
-- no nested collections
if NEW.collection_id is not null then
raise exception 'Nested collections are not allowed';
else
return NEW;
end if;
end if;
elsif class_geometry_type = 'FILTERED_PLANNING_UNITS' then
-- Also check for parent collection of parent folder (recursively)
if NEW.collection_id is not null then
raise exception 'Filtered planning units cannot be part of a collection';
elsif NEW.folder_id is not null then
if (select get_parent_collection_id('sketch_folder', NEW.folder_id)) is not null then
raise exception 'Filtered planning units cannot be part of a collection';
end if;
end if;
-- geom must be present unless a collection
if NEW.geom is not null or NEW.user_geom is not null then
raise exception 'Filtered planning units should not have geometry';
else
-- no nested collections
if NEW.collection_id is not null then
raise exception 'Filtered planning units cannot be part of a collection';
else
return NEW;
end if;
end if;
else
select geometrytype(NEW.user_geom) into new_geometry_type;
-- geometry type must match sketch_class.geometry_type and sketch_class.allow_multi
if (new_geometry_type = class_geometry_type::text) or (allow_multi and new_geometry_type like '%' || class_geometry_type::text) then
-- if specifying a collection_id, must be in it's valid_children
if NEW.collection_id is null or not exists(select 1 from sketch_classes_valid_children where parent_id in (select sketch_class_id from sketches where id = NEW.collection_id)) or exists(select 1 from sketch_classes_valid_children where parent_id in (select sketch_class_id from sketches where id = NEW.collection_id) and child_id = NEW.sketch_class_id) then
return NEW;
else
raise exception 'Sketch is not a valid child of collection';
end if;
else
raise exception 'Geometry type does not match sketch class';
end if;
end if;
end;
$$;


CREATE OR REPLACE FUNCTION url_encode(input text)
RETURNS text AS $$
DECLARE
cleaned_input text;
output text := '';
ch text;
ch_code int;
BEGIN
-- Remove all extraneous whitespace from the input JSON text
cleaned_input := regexp_replace(input, '\s+', '', 'g');

-- Perform URL encoding on the cleaned input
FOR i IN 1..length(cleaned_input) LOOP
ch := substr(cleaned_input, i, 1);
ch_code := ascii(ch);
-- Allow only URL-safe characters (alphanumeric and unreserved characters)
IF ch ~ '[a-zA-Z0-9_.~-]' THEN
output := output || ch;
ELSE
-- Use lpad and upper to ensure two-character hexadecimal representation
output := output || '%' || lpad(upper(to_hex(ch_code)), 2, '0');
END IF;
END LOOP;

RETURN output;
END;
$$ LANGUAGE plpgsql;

grant execute on function url_encode to anon;
comment on function url_encode is '@omit';

drop function if exists filter_state_to_search_string(jsonb, int);
drop function if exists filter_state_to_search_string(jsonb);

CREATE OR REPLACE FUNCTION filter_state_to_search_string(filters jsonb, sketch_class_id int)
RETURNS text AS $$
DECLARE
state jsonb := '{}';
filter jsonb;
attr text;
result text;
attribute_name text;
filter_server_location text;
filter_version int;
final_url text;
BEGIN
-- Retrieve the filter_api_server_location and filter_api_version from sketch_classes table
SELECT sketch_classes.filter_api_server_location, sketch_classes.filter_api_version
INTO filter_server_location, filter_version
FROM sketch_classes
WHERE id = sketch_class_id;

-- If filter_api_server_location is NULL, return NULL
IF filter_server_location IS NULL THEN
RETURN NULL;
END IF;

-- Loop through each attribute in the input JSONB object
FOR attr, filter IN
SELECT key, value FROM jsonb_each(filters)
LOOP
-- Only process if "selected" is true
IF filter->>'selected' = 'true' THEN
if filter->>'attribute' is not null then
attribute_name := filter->>'attribute';
else
-- Look up the attribute name from form_elements for the current attr ID
SELECT component_settings->>'attribute'
INTO attribute_name
FROM form_elements
WHERE id = attr::int; -- Cast attr to integer to match form_elements ID
end if;


-- Default to the original key if no match is found
IF attribute_name IS NULL THEN
attribute_name := attr;
END IF;

-- Check for numberState
IF filter ? 'numberState' THEN
state := state || jsonb_build_object(attribute_name, jsonb_build_object(
'min', filter->'numberState'->'min',
'max', filter->'numberState'->'max'
));

-- Check for stringState
ELSIF filter ? 'stringState' THEN
state := state || jsonb_build_object(attribute_name, jsonb_build_object(
'choices', filter->'stringState'
));

-- Check for booleanState
ELSIF filter ? 'booleanState' THEN
state := state || jsonb_build_object(attribute_name, jsonb_build_object(
'bool', COALESCE(filter->'booleanState', 'false')::boolean
));
END IF;
END IF;
END LOOP;

-- If no keys were added, return an empty string; otherwise, encode the JSON
IF state = '{}'::jsonb THEN
RETURN '';
ELSE
-- Convert the JSONB object to a text string without formatting
result := state::text;

-- URL encode the resulting JSON text using the updated url_encode function
result := url_encode(result);

-- Construct the final URL
final_url := filter_server_location || '/v' || filter_version || '/mvt/{z}/{x}/{y}.pbf?filter=' || result;
RETURN final_url;
END IF;
END;
$$ LANGUAGE plpgsql;

grant execute on function filter_state_to_search_string to anon;
comment on function filter_state_to_search_string is '@omit';

alter table sketches drop column if exists filter_mvt_url;
create or replace function sketches_filter_mvt_url(s sketches)
returns text
language sql
security definer
stable
as $$
select filter_state_to_search_string(s.properties, s.sketch_class_id);
$$;


grant execute on function sketches_filter_mvt_url to anon;
-- alter table sketches add column if not exists filter_mvt_url text generated always as (filter_state_to_search_string(properties, sketch_class_id)) stored;

-- grant select(filter_mvt_url) on sketches to anon;

delete from form_element_types where component_name = 'CollapsibleGroup';
insert into form_element_types (
component_name,
label,
is_input,
is_surveys_only
) values (
'CollapsibleGroup',
'Collapsible Group',
false,
false
);

0 comments on commit 98d3e04

Please sign in to comment.