Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions database/migrations/functions/001_load_functions.sql
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
{{ template "common/jsonb_geography_point.sql" }} -- Dependency for payload location mappings and distance search filters
{{ template "common/jsonb_text_array.sql" }} -- Dependency for payload text-array mappings

{{ template "auth/get_user_by_id.sql" }} -- Do not sort alphabetically, has dependency
{{ template "auth/resolve_unique_username.sql" }} -- Dependency for signup and pre-registration activation
{{ template "auth/activate_pre_registered_user_email_password.sql" }}
{{ template "auth/activate_pre_registered_user_external_provider.sql" }}
{{ template "auth/get_user_by_email.sql" }}
Expand Down Expand Up @@ -27,6 +31,8 @@
{{ template "common/stats_label_count_series_by_name.sql" }}
{{ template "common/stats_running_total_series.sql" }}
{{ template "common/stats_running_total_series_by_name.sql" }}
{{ template "common/validate_cfs_submission_label_ids.sql" }} -- Dependency for CFS submission label sync
{{ template "common/sync_cfs_submission_labels.sql" }} -- Dependency for add/update_cfs_submission
{{ template "common/validate_questionnaire_questions_payload.sql" }} -- Do not sort alphabetically, dependency for add/update_event and validate_questionnaire_answers_payload
{{ template "common/validate_questionnaire_answers_payload.sql" }} -- Do not sort alphabetically, dependency for attend_event, submit_event_registration_answers and prepare_event_checkout_purchase
{{ template "common/get_event_full.sql" }}
Expand Down Expand Up @@ -87,9 +93,15 @@
{{ template "dashboard-group/validate_event_series_action_event_ids.sql" }} -- Dependency for series actions
{{ template "dashboard-group/validate_event_ticket_types_payload.sql" }} -- Dependency for validate_event_ticketing_payload
{{ template "dashboard-group/validate_event_ticketing_payload.sql" }} -- Dependency for add/update_event
{{ template "dashboard-group/validate_add_event_dates.sql" }} -- Dependency for add_event
{{ template "event/promote_event_waitlist.sql" }} -- Dependency for update_event and leave_event
{{ template "dashboard-group/sync_event_discount_codes.sql" }} -- Dependency for add/update_event
{{ template "dashboard-group/sync_event_ticket_types.sql" }} -- Dependency for add/update_event
{{ template "dashboard-group/is_event_meeting_in_sync.sql" }} -- Dependency for update_event
{{ template "dashboard-group/is_session_meeting_in_sync.sql" }} -- Dependency for sync_event_sessions
{{ template "dashboard-group/sync_event_cfs_labels.sql" }} -- Dependency for add/update_event
{{ template "dashboard-group/sync_event_hosts_speakers_sponsors.sql" }} -- Dependency for add/update_event
{{ template "dashboard-group/sync_event_sessions.sql" }} -- Dependency for add/update_event
{{ template "dashboard-group/accept_event_invitation_request.sql" }}
{{ template "dashboard-group/add_event.sql" }}
{{ template "dashboard-group/add_event_series.sql" }}
Expand All @@ -107,8 +119,6 @@
{{ template "dashboard-group/get_event_summary_dashboard.sql" }} -- Dependency for list_group_events
{{ template "dashboard-group/get_group_sponsor.sql" }}
{{ template "dashboard-group/get_group_stats.sql" }}
{{ template "dashboard-group/is_event_meeting_in_sync.sql" }}
{{ template "dashboard-group/is_session_meeting_in_sync.sql" }}
{{ template "dashboard-group/invite_event_attendee.sql" }}
{{ template "dashboard-group/list_cfs_submission_statuses_for_review.sql" }}
{{ template "dashboard-group/list_event_approved_cfs_submissions.sql" }}
Expand Down Expand Up @@ -136,8 +146,6 @@
{{ template "dashboard-group/search_event_attendees.sql" }}
{{ template "dashboard-group/search_event_invitation_requests.sql" }}
{{ template "dashboard-group/search_event_waitlist.sql" }}
{{ template "dashboard-group/sync_event_cfs_labels.sql" }} -- Dependency for update_event
{{ template "dashboard-group/sync_event_sessions.sql" }} -- Dependency for update_event
{{ template "dashboard-group/unpublish_event.sql" }}
{{ template "dashboard-group/unpublish_event_series_events.sql" }}
{{ template "dashboard-group/update_cfs_submission.sql" }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,8 @@ create or replace function activate_pre_registered_user_email_password(
)
returns table("user" json, verification_code uuid) as $$
declare
v_base_username text;
v_suffix int;
v_username text;
v_username_exists boolean;
v_user_id uuid;
v_username text;
v_verification_code uuid;
begin
-- Lock the placeholder user if this email was pre-registered
Expand All @@ -23,35 +20,8 @@ begin
return;
end if;

-- Generate a unique username using the user-provided username
v_base_username := p_user->>'username';
v_username := v_base_username;

select exists(
select 1
from "user"
where username = v_username
and user_id <> v_user_id
) into v_username_exists;

if v_username_exists then
for v_suffix in 2..99 loop
v_username := v_base_username || v_suffix;

select exists(
select 1
from "user"
where username = v_username
and user_id <> v_user_id
) into v_username_exists;

exit when not v_username_exists;
end loop;

if v_username_exists then
raise exception 'unable to generate unique username: all variants from % to %99 are taken', v_base_username, v_base_username;
end if;
end if;
-- Resolve the requested username while ignoring the placeholder row
v_username := resolve_unique_username(p_user->>'username', v_user_id);

-- Promote the placeholder row while keeping email verification pending
update "user"
Expand All @@ -70,6 +40,7 @@ begin
raise exception 'pre-registered user not found';
end if;

-- Create/refresh email verification code for the activated user
insert into email_verification_code (user_id)
values (v_user_id)
on conflict (user_id) do update
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,10 @@ create or replace function activate_pre_registered_user_external_provider(
)
returns json as $$
declare
v_base_username text;
v_suffix int;
v_username text;
v_username_exists boolean;
begin
-- Generate a unique username using the provider-provided username
v_base_username := p_user->>'username';
v_username := v_base_username;

select exists(
select 1
from "user"
where username = v_username
and user_id <> p_user_id
) into v_username_exists;

if v_username_exists then
for v_suffix in 2..99 loop
v_username := v_base_username || v_suffix;

select exists(
select 1
from "user"
where username = v_username
and user_id <> p_user_id
) into v_username_exists;

exit when not v_username_exists;
end loop;

if v_username_exists then
raise exception 'unable to generate unique username: all variants from % to %99 are taken', v_base_username, v_base_username;
end if;
end if;
-- Resolve the requested username while ignoring the placeholder row
v_username := resolve_unique_username(p_user->>'username', p_user_id);

-- Promote the placeholder record into a regular verified user
update "user"
Expand Down
50 changes: 50 additions & 0 deletions database/migrations/functions/auth/resolve_unique_username.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-- Resolves a unique username by appending a numeric suffix when needed.
create or replace function resolve_unique_username(
p_base_username text,
p_excluded_user_id uuid default null
)
returns text as $$
declare
v_suffix int;
v_username text := p_base_username;
v_username_exists boolean;
begin
-- Check whether the base username is available
select exists(
select 1
from "user"
where username = v_username
and (
p_excluded_user_id is null
or user_id <> p_excluded_user_id
)
) into v_username_exists;

-- If username exists, try with numeric suffixes from 2 to 99
if v_username_exists then
for v_suffix in 2..99 loop
v_username := p_base_username || v_suffix;

select exists(
select 1
from "user"
where username = v_username
and (
p_excluded_user_id is null
or user_id <> p_excluded_user_id
)
) into v_username_exists;

exit when not v_username_exists;
end loop;

if v_username_exists then
raise exception 'unable to generate unique username: all variants from % to %99 are taken',
p_base_username,
p_base_username;
end if;
end if;

return v_username;
end;
$$ language plpgsql;
34 changes: 3 additions & 31 deletions database/migrations/functions/auth/sign_up_user.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,40 +5,12 @@ create or replace function sign_up_user(
)
returns table("user" json, verification_code uuid) as $$
declare
v_username text;
v_base_username text;
v_suffix int;
v_username_exists boolean;
v_user_id uuid;
v_username text;
v_verification_code uuid;
begin
-- Get the base username
v_base_username := p_user->>'username';
v_username := v_base_username;

-- Check if username exists
select exists(
select 1 from "user"
where username = v_username
) into v_username_exists;

-- If username exists, try with numeric suffixes from 2 to 99
if v_username_exists then
for v_suffix in 2..99 loop
v_username := v_base_username || v_suffix;
select exists(
select 1 from "user"
where username = v_username
) into v_username_exists;

exit when not v_username_exists;
end loop;

-- If still exists after trying all suffixes, raise error
if v_username_exists then
raise exception 'unable to generate unique username: all variants from % to %99 are taken', v_base_username, v_base_username;
end if;
end if;
-- Resolve the requested username before inserting the user
v_username := resolve_unique_username(p_user->>'username');

-- Insert the user with the available username
insert into "user" (
Expand Down
6 changes: 1 addition & 5 deletions database/migrations/functions/auth/update_user_details.sql
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ begin
country = nullif(p_user->>'country', ''),
facebook_url = nullif(p_user->>'facebook_url', ''),
github_url = nullif(p_user->>'github_url', ''),
interests = case
when p_user ? 'interests' and jsonb_typeof(p_user->'interests') != 'null' then
array(select jsonb_array_elements_text(p_user->'interests'))
else null
end,
interests = jsonb_text_array(p_user->'interests'),
linkedin_url = nullif(p_user->>'linkedin_url', ''),
optional_notifications_enabled = coalesce(
(p_user->>'optional_notifications_enabled')::boolean,
Expand Down
17 changes: 17 additions & 0 deletions database/migrations/functions/common/jsonb_geography_point.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
-- Converts JSONB latitude and longitude fields into a geography point.
create or replace function jsonb_geography_point(p_value jsonb)
returns geography as $$
select case
when p_value is null
or jsonb_typeof(p_value) = 'null'
or p_value->>'latitude' is null
or p_value->>'longitude' is null then null
else ST_SetSRID(
ST_MakePoint(
(p_value->>'longitude')::float,
(p_value->>'latitude')::float
),
4326
)::geography
end;
$$ language sql immutable;
8 changes: 8 additions & 0 deletions database/migrations/functions/common/jsonb_text_array.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Converts a JSONB text array into a SQL text array.
create or replace function jsonb_text_array(p_value jsonb)
returns text[] as $$
select case
when p_value is null or jsonb_typeof(p_value) = 'null' then null
else array(select jsonb_array_elements_text(p_value))
end;
$$ language sql immutable;
2 changes: 1 addition & 1 deletion database/migrations/functions/common/search_events.sql
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ begin
from jsonb_array_elements_text(p_filters->'kind') e;
end if;
if p_filters ? 'latitude' and p_filters ? 'longitude' then
v_user_location := st_setsrid(st_makepoint((p_filters->>'longitude')::real, (p_filters->>'latitude')::real), 4326);
v_user_location := jsonb_geography_point(p_filters);
if p_filters ? 'distance' then
v_max_distance := (p_filters->>'distance')::real;
end if;
Expand Down
2 changes: 1 addition & 1 deletion database/migrations/functions/common/search_groups.sql
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ begin
from jsonb_array_elements_text(p_filters->'group_category') e;
end if;
if p_filters ? 'latitude' and p_filters ? 'longitude' then
v_user_location := st_setsrid(st_makepoint((p_filters->>'longitude')::real, (p_filters->>'latitude')::real), 4326);
v_user_location := jsonb_geography_point(p_filters);
if p_filters ? 'distance' then
v_max_distance := (p_filters->>'distance')::real;
end if;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
-- Replaces the labels linked to a CFS submission.
create or replace function sync_cfs_submission_labels(
p_cfs_submission_id uuid,
p_event_id uuid,
p_label_ids uuid[]
)
returns void as $$
begin
-- Ensure the submission belongs to the event before mutating labels
perform 1
from cfs_submission cs
where cs.cfs_submission_id = p_cfs_submission_id
and cs.event_id = p_event_id;

if not found then
raise exception 'submission not found';
end if;

-- Validate supplied labels before replacing existing links
perform validate_cfs_submission_label_ids(p_event_id, p_label_ids);

-- Remove labels omitted from the payload
delete from cfs_submission_label
where cfs_submission_id = p_cfs_submission_id;

-- Insert supplied labels, deduplicating repeated IDs
if p_label_ids is not null then
insert into cfs_submission_label (cfs_submission_id, event_cfs_label_id)
select p_cfs_submission_id, input_label.event_cfs_label_id
from unnest(p_label_ids) as input_label(event_cfs_label_id)
group by input_label.event_cfs_label_id;
end if;
end;
$$ language plpgsql;
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- Validates CFS submission label IDs for an event.
create or replace function validate_cfs_submission_label_ids(
p_event_id uuid,
p_label_ids uuid[]
)
returns void as $$
begin
-- Enforce the maximum number of labels per submission
if coalesce(array_length(p_label_ids, 1), 0) > 10 then
raise exception 'too many submission labels';
end if;

-- Ensure all supplied labels belong to the event
if p_label_ids is not null then
perform 1
from unnest(p_label_ids) as input_label(event_cfs_label_id)
where not exists (
select 1
from event_cfs_label ecl
where ecl.event_cfs_label_id = input_label.event_cfs_label_id
and ecl.event_id = p_event_id
);

if found then
raise exception 'invalid event CFS labels';
end if;
end if;
end;
$$ language plpgsql;
Loading
Loading