Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow for reporting monthly active users #4445

Merged
merged 27 commits into from
Feb 23, 2024
Merged

Conversation

tmessi
Copy link
Member

@tmessi tmessi commented Feb 23, 2024

This adds support for tracking and reporting monthly active users for
the purpose of billing. It adds a new API endpoint,
/v1/billing:monthly-active-users and new cli command, boundary billing monthly-active-users that can be used to view the monthly
active user counts.

This adds a new accumulating fact table to the data warehouse for auth
tokens. This fact table is used to calculate the number of active users
within a month, where an active user is a user that has at least one
issued auth token within the time range of the month.

tmessi and others added 20 commits February 23, 2024 15:44
Some of the prepared statement identifies used in these tests exceeded
the identifier character limit. This results in postgres truncating the
name, which is fine for a test, but does result in some noise in the
test output. This change shortens the identifies to eliminate noise like
the following:

    psql:/test/credential/vault/credential_vault_library_ssh_private_key_mapping_override.sql:49: NOTICE:  identifier "insert_credential_vault_library_ssh_private_key_mapping_override" will be truncated to "insert_credential_vault_library_ssh_private_key_mapping_overrid"

The tests should no longer create notices like these.
This notice just generated noise, particuarlly in some of the sqltests.
When running the sqltests, sometimes it is helpful to see the full
database container logs, especially when there is an error in a
migration or database setup. However, it also makes it difficult to see
the test results.

This adds a new variable to the makefile to easily toggle the output of
these logs on or off. To run the tests without printing the dtabase logs
use:

    DB_LOGS=0 make test

To enable the logs use:

    DB_LOGS=1 make test

By default the logs are not printed.
Previously the function used to upsert into the user dimension required
both an auth token id, and a user id. However, the user id can be
determined from the auth token, in fact it already was via the
whx_user_dimension_source view. This refactor removes the user id as an
argument, since it is not needed and will not be directly available when
this function is used when populating the new auth token accumulating
fact.
This adds a trigger and function to ensure that an auth token fact is
created when a new auth token is created with the 'token issued' status.
This adds a function and update trigger to auth_token to ensure that:

1. If the auth token is updated to the issued state, a auth token fact
   is added to the warehouse.
2. If the auth token's approximate last access time is updated, the
   corresponding auth token fact is updated with the new time.
This adds a function and after delete trigger to the auth_token table to
ensure that a corresponding auth token fact is updated with the deleted
time and sets the auth token valid time range. The data warehouse will
only be updated if the token that was deleted is in the 'token issued'
status.
With the addition of an after insert trigger to the auth_token table,
entries are added to the wh_user_dimension table when auth token's are
inserted with the 'token issued' status. This caused a number of tests
to fail that assumed the user dimension table would be empty prior to
inserting sessions. This introduces a number of changes to the tests:

- In the test setup, auth tokens are not created with an explicit
  expiration time and status. The expiration time ensures that the
  tokens are 'valid' for the test. The status allows test to make
  assertions on tokens in different statuses, and allow the status to
  change during the test.
- Tests make the correct assertions on the number of entries in the user
  dimension table after test setup.
- User dimension tests modify a user after the standard setup to ensure
  that a new user dimension is added when a session is inserted.
Now that there is an auth token fact table in the data warehouse, it is
not possible to insert an auth_token with an old public_id even after
that auth_token was deleted, since it now causes a constraint violation
on the data warehouse table.
This test started failing now that the data warehouse records a auth
token fact when an auth token is issued and it requires that the auth
token has a user associated with it.

Since this test is testing the interaction with the database directly,
and not via the repository, it by-passes the application checks that
would prevent an auth token without an associated user being inserted.

This test is updated to meet this same pre-condition prior to attempting
to insert into the database.
This adds two sql functions that can be used to access the views to
retrieve monthly active user counts. The functions ensure that any time
calculates are performed in UTC, so that the month bounds align with the
start of the month in UTC.

1. hcp_billing_monthly_active_users_last_2_months() will return monthly
   active users for the current and previous month.
2. hcp_billing_monthly_active_users_all() will return monthly active
   users for all months. This function can also be provided a start time
   and or end time to filter the results to a specific range.
tmessi and others added 6 commits February 23, 2024 15:44
These functions will ensure that UTC time is used for billing queries,
eliminating the need for the repository to set the timezone. This also
means the repository no longern needs a writer.
When converting a timestamp.Timestamp for use in SQL, this now correctly
converts the NegativeInfinity and PositiveInfinity constants into the
corresponding `-infinity` and `infinity` SQL constants.
This updates the billing repository to use timestmap.Timestamp when
constructing the queries to retrieve monthly active users. This allows
for specifying NegativeInfinity to easily retrieve all monthly active
user counts.

This comment has been minimized.

This comment has been minimized.

@tmessi tmessi merged commit 3856573 into main Feb 23, 2024
33 of 34 checks passed
@tmessi tmessi deleted the llb-monthly-active-users branch February 23, 2024 16:07
Copy link

Database schema diff between main and llb-monthly-active-users @ 4b0e7ab

To understand how these diffs are generated and some limitations see the
documentation of the script.

Functions

diff --git a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/_wtt_load_widgets_auth.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/_wtt_load_widgets_auth.sql
index 6cb4709e7..75080f39a 100644
--- a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/_wtt_load_widgets_auth.sql
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/_wtt_load_widgets_auth.sql
@@ -54,13 +54,13 @@ create function public._wtt_load_widgets_auth() returns void
     update auth_account set iam_user_id = 'u_____wilson' where public_id = 'apa___wilson';
 
     insert into auth_token
-      (key_id, auth_account_id, public_id, token)
+      (key_id,          auth_account_id, public_id,      token,                 expiration_time,            status)
     values
-      ('kdkv___widget', 'apa___walter', 'tok___walter', 'tok___walter'::bytea),
-      ('kdkv___widget', 'apa1__walter', 'tok1__walter', 'tok1__walter'::bytea),
-      ('kdkv___widget', 'apa___warren', 'tok___warren', 'tok___warren'::bytea),
-      ('kdkv___widget', 'apa___waylon', 'tok___waylon', 'tok___waylon'::bytea),
-      ('kdkv___widget', 'apa___wilson', 'tok___wilson', 'tok___wilson'::bytea);
+      ('kdkv___widget', 'apa___walter',  'tok___walter', 'tok___walter'::bytea, now() + interval '15 days', 'token issued'),
+      ('kdkv___widget', 'apa1__walter',  'tok1__walter', 'tok1__walter'::bytea, now() + interval '15 days', 'token issued'),
+      ('kdkv___widget', 'apa___warren',  'tok___warren', 'tok___warren'::bytea, now() + interval '15 days', 'token issued'),
+      ('kdkv___widget', 'apa___waylon',  'tok___waylon', 'tok___waylon'::bytea, now() + interval '15 days', 'token issued'),
+      ('kdkv___widget', 'apa___wilson',  'tok___wilson', 'tok___wilson'::bytea, now() + interval '15 days', 'auth token pending');
 
     insert into auth_oidc_method
       (scope_id,       public_id,      client_id,      name,          state,            key_id,          issuer)
@@ -88,7 +88,7 @@ create function public._wtt_load_widgets_auth() returns void
       ('o_____widget', 'alm___widget', 'widget ldap', 'active-private');
     insert into auth_ldap_url
       (ldap_method_id, url,             connection_priority)
-    values 
+    values
       ('alm___widget', 'ldaps://ldap1', 1);
 
     insert into auth_ldap_account
diff --git a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/_wtt_load_widgets_kms.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/_wtt_load_widgets_kms.sql
index 08e25506e..1a5662384 100644
--- a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/_wtt_load_widgets_kms.sql
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/_wtt_load_widgets_kms.sql
@@ -53,7 +53,7 @@ create function public._wtt_load_widgets_kms() returns void
       (key_id, table_name, total_count)
     values
       ('kdkv___widget', 'auth_token', 100);
-  
+
   end;
   $$;
 
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_before_issued.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_before_issued.sql
new file mode 100644
index 000000000..78fa2083c
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_before_issued.sql
@@ -0,0 +1,38 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token_before_issued(); type: function; schema: public; owner: -
+--
+
+create function public.auth_token_before_issued() returns trigger
+    language plpgsql
+    as $$
+  begin
+    if new.status = 'token issued' then
+      new.approximate_last_access_time = now();
+    end if;
+    return new;
+  end;
+  $$;
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_deleted.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_deleted.sql
new file mode 100644
index 000000000..fc690e2a5
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_deleted.sql
@@ -0,0 +1,38 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token_deleted(); type: function; schema: public; owner: -
+--
+
+create function public.auth_token_deleted() returns trigger
+    language plpgsql
+    as $$
+  begin
+    if old.status = 'token issued' then
+      perform wh_auth_token_deleted(old.public_id);
+    end if;
+    return null;
+  end;
+  $$;
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_inserted.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_inserted.sql
new file mode 100644
index 000000000..142bca962
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_inserted.sql
@@ -0,0 +1,38 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token_inserted(); type: function; schema: public; owner: -
+--
+
+create function public.auth_token_inserted() returns trigger
+    language plpgsql
+    as $$
+  begin
+    if new.status = 'token issued' then
+      perform wh_auth_token_issued(new.public_id, new.update_time, new.approximate_last_access_time);
+    end if;
+    return null;
+  end;
+  $$;
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_updated.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_updated.sql
new file mode 100644
index 000000000..10281f879
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/auth_token_updated.sql
@@ -0,0 +1,43 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token_updated(); type: function; schema: public; owner: -
+--
+
+create function public.auth_token_updated() returns trigger
+    language plpgsql
+    as $$
+  begin
+    case
+      when new.status = 'token issued' and new.status is distinct from old.status then
+        perform wh_auth_token_issued(new.public_id, new.update_time, new.approximate_last_access_time);
+      when new.approximate_last_access_time is distinct from old.approximate_last_access_time then
+        perform wh_auth_token_accessed(new.public_id, new.approximate_last_access_time);
+      else
+        return null;
+    end case;
+    return null;
+  end;
+  $$;
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/default_ssh_certificate_credential_type.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/default_ssh_certificate_credential_type.sql
index affd7768c..4f7400e30 100644
--- a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/default_ssh_certificate_credential_type.sql
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/default_ssh_certificate_credential_type.sql
@@ -25,7 +25,6 @@ create function public.default_ssh_certificate_credential_type() returns trigger
     as $$
   begin
     if new.credential_type is distinct from 'ssh_certificate' then
-      raise warning 'credential_vault_ssh_cert_library only supports ssh_certificate credentials';
       new.credential_type = 'ssh_certificate';
     end if;
     return new;
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_all.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_all.sql
new file mode 100644
index 000000000..e2831afb0
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_all.sql
@@ -0,0 +1,61 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: hcp_billing_monthly_active_users_all(timestamp with time zone, timestamp with time zone); type: function; schema: public; owner: -
+--
+
+create function public.hcp_billing_monthly_active_users_all(p_start_time timestamp with time zone default null::timestamp with time zone, p_end_time timestamp with time zone default null::timestamp with time zone) returns setof public.hcp_billing_monthly_active_users_all
+    language plpgsql immutable parallel safe
+    set "timezone" to 'utc'
+    as $$
+  begin
+    case
+    when p_start_time is not null and p_end_time is not null then
+      return query select *
+                     from hcp_billing_monthly_active_users_all
+                    where start_time >= p_start_time
+                      and end_time   <= p_end_time;
+    when p_start_time is not null then
+      return query select *
+                     from hcp_billing_monthly_active_users_all
+                    where start_time >= p_start_time;
+    when p_end_time is not null then
+      return query select *
+                     from hcp_billing_monthly_active_users_all
+                    where end_time <= p_end_time;
+    else
+      return query select *
+                     from hcp_billing_monthly_active_users_all;
+    end case;
+    return;
+  end;
+  $$;
+
+
+--
+-- name: function hcp_billing_monthly_active_users_all(p_start_time timestamp with time zone, p_end_time timestamp with time zone); type: comment; schema: public; owner: -
+--
+
+comment on function public.hcp_billing_monthly_active_users_all(p_start_time timestamp with time zone, p_end_time timestamp with time zone) is 'hcp_billing_monthly_active_users_all is a function that contains the count of active users for the all months.the current month is a sum from the beginning of the current month until the start of the current hour (exclusive).it can be provided with a start time and end time to restrict the results.all timestamps returned are in utc.';
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_last_2_months.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_last_2_months.sql
new file mode 100644
index 000000000..64ec6be7d
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_last_2_months.sql
@@ -0,0 +1,42 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: hcp_billing_monthly_active_users_last_2_months(); type: function; schema: public; owner: -
+--
+
+create function public.hcp_billing_monthly_active_users_last_2_months() returns table(start_time timestamp with time zone, end_time timestamp with time zone, active_user_count bigint)
+    language sql immutable strict parallel safe
+    set "timezone" to 'utc'
+    as $$
+    select *
+      from hcp_billing_monthly_active_users_last_2_months;
+  $$;
+
+
+--
+-- name: function hcp_billing_monthly_active_users_last_2_months(); type: comment; schema: public; owner: -
+--
+
+comment on function public.hcp_billing_monthly_active_users_last_2_months() is 'hcp_billing_monthly_active_users_last_2_months is a function that contains the count of active users for the current month and the previous month. the current month is a sum from the beginning of the current month until the start of the current hour (exclusive).all timestamps returned are in utc.';
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accessed.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accessed.sql
new file mode 100644
index 000000000..5be5fe3b0
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accessed.sql
@@ -0,0 +1,48 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: wh_auth_token_accessed(public.wt_public_id, public.wt_timestamp); type: function; schema: public; owner: -
+--
+
+create function public.wh_auth_token_accessed(p_auth_token_id public.wt_public_id, last_accessed_ts public.wt_timestamp) returns void
+    language plpgsql
+    as $$
+  begin
+    update wh_auth_token_accumulating_fact
+       set auth_token_approximate_last_access_date_key = wh_date_key(last_accessed_ts),
+           auth_token_approximate_last_access_time_key = wh_time_key(last_accessed_ts),
+           auth_token_approximate_last_access_time     = last_accessed_ts,
+           auth_token_approximate_active_time_range    = tstzrange(lower(auth_token_approximate_active_time_range), last_accessed_ts, '[]')
+     where auth_token_id = p_auth_token_id;
+    return;
+  end;
+  $$;
+
+
+--
+-- name: function wh_auth_token_accessed(p_auth_token_id public.wt_public_id, last_accessed_ts public.wt_timestamp); type: comment; schema: public; owner: -
+--
+
+comment on function public.wh_auth_token_accessed(p_auth_token_id public.wt_public_id, last_accessed_ts public.wt_timestamp) is 'wh_auth_token_accessed is a function that updates the wh_auth_token_accumulating_factwhen an auth_token is accessed.';
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_deleted.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_deleted.sql
new file mode 100644
index 000000000..8fdc3b919
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_deleted.sql
@@ -0,0 +1,48 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: wh_auth_token_deleted(public.wt_public_id); type: function; schema: public; owner: -
+--
+
+create function public.wh_auth_token_deleted(p_auth_token_id public.wt_public_id) returns void
+    language plpgsql
+    as $$
+  begin
+    update wh_auth_token_accumulating_fact
+       set auth_token_deleted_date_key = wh_date_key(now()),
+           auth_token_deleted_time_key = wh_time_key(now()),
+           auth_token_deleted_time     = now(),
+           auth_token_valid_time_range = tstzrange(lower(auth_token_valid_time_range), now(), '[]')
+     where auth_token_id = p_auth_token_id;
+    return;
+  end;
+  $$;
+
+
+--
+-- name: function wh_auth_token_deleted(p_auth_token_id public.wt_public_id); type: comment; schema: public; owner: -
+--
+
+comment on function public.wh_auth_token_deleted(p_auth_token_id public.wt_public_id) is 'wh_auth_token_deleted is a function that updates the wh_auth_token_accumulating_factwhen a previously issued auth_token is deleted.';
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_issued.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_issued.sql
new file mode 100644
index 000000000..7196234e5
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_issued.sql
@@ -0,0 +1,67 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: wh_auth_token_issued(public.wt_public_id, public.wt_timestamp, public.wt_timestamp); type: function; schema: public; owner: -
+--
+
+create function public.wh_auth_token_issued(p_auth_token_id public.wt_public_id, issued_ts public.wt_timestamp, last_accessed_ts public.wt_timestamp) returns void
+    language plpgsql
+    as $$
+  declare
+    new_row wh_auth_token_accumulating_fact%rowtype;
+  begin
+    insert into wh_auth_token_accumulating_fact (
+                auth_token_id,
+                user_key,
+                auth_token_issued_date_key,
+                auth_token_issued_time_key,
+                auth_token_issued_time,
+                auth_token_approximate_last_access_date_key,
+                auth_token_approximate_last_access_time_key,
+                auth_token_approximate_last_access_time,
+                auth_token_approximate_active_time_range,
+                auth_token_valid_time_range
+    )
+         select p_auth_token_id,
+                wh_upsert_user(p_auth_token_id),
+                wh_date_key(issued_ts),
+                wh_time_key(issued_ts),
+                issued_ts,
+                wh_date_key(last_accessed_ts),
+                wh_time_key(last_accessed_ts),
+                last_accessed_ts,
+                tstzrange(issued_ts, last_accessed_ts,         '[]'),
+                tstzrange(issued_ts, 'infinity'::wt_timestamp, '[]')
+      returning * into strict new_row;
+    return;
+  end;
+  $$;
+
+
+--
+-- name: function wh_auth_token_issued(p_auth_token_id public.wt_public_id, issued_ts public.wt_timestamp, last_accessed_ts public.wt_timestamp); type: comment; schema: public; owner: -
+--
+
+comment on function public.wh_auth_token_issued(p_auth_token_id public.wt_public_id, issued_ts public.wt_timestamp, last_accessed_ts public.wt_timestamp) is 'wh_auth_token_issued is a function called when an auth token is issued to insert a fact into the auth token accumulating fact table.';
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_insert_auth_token.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_insert_auth_token.sql
new file mode 100644
index 000000000..7ac7d7cc4
--- /dev/null
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_insert_auth_token.sql
@@ -0,0 +1,40 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: wh_insert_auth_token(); type: function; schema: public; owner: -
+--
+
+create function public.wh_insert_auth_token() returns trigger
+    language plpgsql
+    as $$
+  begin
+    select user_id
+      into new.user_id
+      from wh_user_dimension
+     where key = new.user_key;
+
+    return new;
+  end;
+  $$;
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/wh_insert_session.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_insert_session.sql
index 7a2a637cc..c3625a23d 100644
--- a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/wh_insert_session.sql
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_insert_session.sql
@@ -46,7 +46,7 @@ create function public.wh_insert_session() returns trigger
     select new.public_id,
            new.auth_token_id,
            'no host source', -- will be updated by wh_upsert_host
-           wh_upsert_user(new.user_id, new.auth_token_id),
+           wh_upsert_user(new.auth_token_id),
            'no credentials', -- will be updated by wh_upsert_credential_group
            pending_timestamp.date_dim_key,
            pending_timestamp.time_dim_key,
diff --git a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/wh_upsert_user.sql b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_upsert_user.sql
index 49e8b276e..b85dcbe99 100644
--- a/.schema-diff/funcs_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/wh_upsert_user.sql
+++ b/.schema-diff/funcs_a356edf1622d4714672dd2387589c7929c59153a/wh_upsert_user.sql
@@ -17,10 +17,10 @@ set client_min_messages = warning;
 set row_security = off;
 
 --
--- name: wh_upsert_user(public.wt_user_id, public.wt_public_id); type: function; schema: public; owner: -
+-- name: wh_upsert_user(public.wt_public_id); type: function; schema: public; owner: -
 --
 
-create function public.wh_upsert_user(p_user_id public.wt_user_id, p_auth_token_id public.wt_public_id) returns public.wh_dim_key
+create function public.wh_upsert_user(p_auth_token_id public.wt_public_id) returns public.wh_dim_key
     language plpgsql
     as $$
   declare
@@ -35,13 +35,11 @@ create function public.wh_upsert_user(p_user_id public.wt_user_id, p_auth_token_
 
     select * into target
       from whx_user_dimension_target as t
-     where t.user_id               = p_user_id
-       and t.auth_account_id       = acct_id;
+     where t.auth_account_id = acct_id;
 
     select target.key, t.* into src
       from whx_user_dimension_source as t
-     where t.user_id               = p_user_id
-       and t.auth_account_id       = acct_id;
+     where t.auth_account_id = acct_id;
 
     if src is distinct from target then
 
@@ -49,8 +47,7 @@ create function public.wh_upsert_user(p_user_id public.wt_user_id, p_auth_token_
       update wh_user_dimension
          set current_row_indicator = 'expired',
              row_expiration_time   = current_timestamp
-       where user_id               = p_user_id
-         and auth_account_id       = acct_id
+       where auth_account_id       = acct_id
          and current_row_indicator = 'current';
 
       -- insert a new row
@@ -71,8 +68,7 @@ create function public.wh_upsert_user(p_user_id public.wt_user_id, p_auth_token_
              user_organization_id,     user_organization_name, user_organization_description,
              'current',                current_timestamp,      'infinity'::timestamptz
         from whx_user_dimension_source
-       where user_id               = p_user_id
-         and auth_account_id       = acct_id
+       where auth_account_id = acct_id
       returning * into new_row;
 
       return new_row.key;
@@ -83,6 +79,13 @@ create function public.wh_upsert_user(p_user_id public.wt_user_id, p_auth_token_
   $$;
 
 
+--
+-- name: function wh_upsert_user(p_auth_token_id public.wt_public_id); type: comment; schema: public; owner: -
+--
+
+comment on function public.wh_upsert_user(p_auth_token_id public.wt_public_id) is 'wh_upsert_user a function insert or updates the wh_user_dimension tablefor the user that corresponds to the provided auth_token_id.';
+
+
 --
 -- postgresql database dump complete
 --

Tables

diff --git a/.schema-diff/tables_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/census_last_uploaded.sql b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/census_last_uploaded.sql
index 6e823cb1c..5dccd1ffc 100644
--- a/.schema-diff/tables_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/census_last_uploaded.sql
+++ b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/census_last_uploaded.sql
@@ -25,7 +25,8 @@ set default_table_access_method = heap;
 --
 
 create table public.census_last_uploaded (
-    last_uploaded_at public.wt_timestamp not null
+    last_uploaded_at public.wt_timestamp not null,
+    metric text not null
 );
 
 
@@ -33,7 +34,7 @@ create table public.census_last_uploaded (
 -- name: table census_last_uploaded; type: comment; schema: public; owner: -
 --
 
-comment on table public.census_last_uploaded is 'census_last_uploaded is a table with 1 row which contains the timestamp of the last time census data was uploaded.';
+comment on table public.census_last_uploaded is 'census_last_uploaded is a table which contains the timestamp of the last time census data was uploaded, per census metric.';
 
 
 --
diff --git a/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm.sql b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm.sql
new file mode 100644
index 000000000..9470f8455
--- /dev/null
+++ b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm.sql
@@ -0,0 +1,43 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+set default_tablespace = '';
+
+set default_table_access_method = heap;
+
+--
+-- name: census_metric_enm; type: table; schema: public; owner: -
+--
+
+create table public.census_metric_enm (
+    name text not null,
+    constraint only_predefined_census_metrics_allowed check ((name = any (array['sessions'::text, 'active_users'::text])))
+);
+
+
+--
+-- name: table census_metric_enm; type: comment; schema: public; owner: -
+--
+
+comment on table public.census_metric_enm is 'census_metric_enm is an enumeration table for census metric types.';
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/public census_metric_enm.sql b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/public census_metric_enm.sql
new file mode 100644
index 000000000..a0b2f694a
--- /dev/null
+++ b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/public census_metric_enm.sql	
@@ -0,0 +1,22 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/public wh_auth_token_accumulating_fact.sql b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/public wh_auth_token_accumulating_fact.sql
new file mode 100644
index 000000000..a0b2f694a
--- /dev/null
+++ b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/public wh_auth_token_accumulating_fact.sql	
@@ -0,0 +1,22 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact.sql b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact.sql
new file mode 100644
index 000000000..aea87f89f
--- /dev/null
+++ b/.schema-diff/tables_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact.sql
@@ -0,0 +1,63 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+set default_tablespace = '';
+
+set default_table_access_method = heap;
+
+--
+-- name: wh_auth_token_accumulating_fact; type: table; schema: public; owner: -
+--
+
+create table public.wh_auth_token_accumulating_fact (
+    auth_token_id public.wt_public_id not null,
+    user_id public.wh_user_id not null,
+    user_key public.wh_dim_key not null,
+    auth_token_issued_date_key integer not null,
+    auth_token_issued_time_key integer not null,
+    auth_token_issued_time public.wh_timestamp,
+    auth_token_deleted_date_key integer default '-1'::integer not null,
+    auth_token_deleted_time_key integer default '-1'::integer not null,
+    auth_token_deleted_time public.wh_timestamp default 'infinity'::timestamp with time zone,
+    auth_token_approximate_last_access_date_key integer default '-1'::integer not null,
+    auth_token_approximate_last_access_time_key integer default '-1'::integer not null,
+    auth_token_approximate_last_access_time public.wh_timestamp,
+    auth_token_approximate_active_time_range tstzrange default tstzrange(current_timestamp, current_timestamp, '[]'::text) not null,
+    auth_token_valid_time_range tstzrange default tstzrange(current_timestamp, 'infinity'::timestamp with time zone, '[]'::text) not null,
+    auth_token_count smallint default 1 not null,
+    constraint active_time_contained_by_valid_time check ((auth_token_approximate_active_time_range <@ auth_token_valid_time_range)),
+    constraint active_time_lower_eq_issued_time check ((lower(auth_token_approximate_active_time_range) = (auth_token_issued_time)::timestamp with time zone)),
+    constraint active_time_upper_eq_last_accessed_time check ((upper(auth_token_approximate_active_time_range) = (auth_token_approximate_last_access_time)::timestamp with time zone)),
+    constraint auth_token_count_must_be_1 check ((auth_token_count = 1)),
+    constraint last_accessed_time_lte_deleted_time check (((auth_token_approximate_last_access_time)::timestamp with time zone <= (auth_token_deleted_time)::timestamp with time zone)),
+    constraint valid_time_lower_eq_issued check ((lower(auth_token_valid_time_range) = (auth_token_issued_time)::timestamp with time zone)),
+    constraint valid_time_upper_eq_deleted check ((upper(auth_token_valid_time_range) = (auth_token_deleted_time)::timestamp with time zone))
+);
+
+
+--
+-- name: table wh_auth_token_accumulating_fact; type: comment; schema: public; owner: -
+--
+
+comment on table public.wh_auth_token_accumulating_fact is 'the wh auth token accumulating fact table is an accumulating fact table. the grain of the fact table is one row per auth token.';
+
+
+--
+-- postgresql database dump complete
+--
+

Views

diff --git a/.schema-diff/views_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_all.sql b/.schema-diff/views_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_all.sql
new file mode 100644
index 000000000..3895e0b80
--- /dev/null
+++ b/.schema-diff/views_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_all.sql
@@ -0,0 +1,56 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: hcp_billing_monthly_active_users_all; type: view; schema: public; owner: -
+--
+
+create view public.hcp_billing_monthly_active_users_all as
+ with monthly_range(month) as (
+         select date_trunc('month'::text, "time"."time") as month
+           from generate_series(date_trunc('month'::text, ( select min((wh_auth_token_accumulating_fact.auth_token_issued_time)::timestamp with time zone) as min
+                   from public.wh_auth_token_accumulating_fact)), now(), '1 mon'::interval) "time"("time")
+        ), final(start_time, end_time, active_users_count) as (
+         select monthly_range.month,
+                case
+                    when (monthly_range.month = date_trunc('month'::text, now())) then date_trunc('hour'::text, now())
+                    else (monthly_range.month + '1 mon'::interval)
+                end as "case",
+            count(distinct wh_ataf.user_id) as count
+           from (monthly_range
+             left join public.wh_auth_token_accumulating_fact wh_ataf on ((wh_ataf.auth_token_approximate_active_time_range && tstzrange(monthly_range.month, (monthly_range.month + '1 mon'::interval), '[)'::text))))
+          group by monthly_range.month
+        )
+ select final.start_time,
+    final.end_time,
+    final.active_users_count
+   from final
+  order by final.start_time desc;
+
+
+--
+-- name: view hcp_billing_monthly_active_users_all; type: comment; schema: public; owner: -
+--
+
+comment on view public.hcp_billing_monthly_active_users_all is 'hcp_billing_monthly_active_users_all is a view that contains the count of active users for the all months.the current month is a sum from the beginning of the current month until the start of the current hour (exclusive).';
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/views_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_last_2_months.sql b/.schema-diff/views_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_last_2_months.sql
new file mode 100644
index 000000000..6679805a5
--- /dev/null
+++ b/.schema-diff/views_a356edf1622d4714672dd2387589c7929c59153a/hcp_billing_monthly_active_users_last_2_months.sql
@@ -0,0 +1,55 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: hcp_billing_monthly_active_users_last_2_months; type: view; schema: public; owner: -
+--
+
+create view public.hcp_billing_monthly_active_users_last_2_months as
+ with monthly_range(month) as (
+         select date_trunc('month'::text, "time"."time") as month
+           from generate_series(date_trunc('month'::text, (now() - '1 mon'::interval)), now(), '1 mon'::interval) "time"("time")
+        ), final(start_time, end_time, active_users_count) as (
+         select monthly_range.month,
+                case
+                    when (monthly_range.month = date_trunc('month'::text, now())) then date_trunc('hour'::text, now())
+                    else (monthly_range.month + '1 mon'::interval)
+                end as "case",
+            count(distinct wh_ataf.user_id) as count
+           from (monthly_range
+             left join public.wh_auth_token_accumulating_fact wh_ataf on ((wh_ataf.auth_token_approximate_active_time_range && tstzrange(monthly_range.month, (monthly_range.month + '1 mon'::interval), '[)'::text))))
+          group by monthly_range.month
+        )
+ select final.start_time,
+    final.end_time,
+    final.active_users_count
+   from final
+  order by final.start_time desc;
+
+
+--
+-- name: view hcp_billing_monthly_active_users_last_2_months; type: comment; schema: public; owner: -
+--
+
+comment on view public.hcp_billing_monthly_active_users_last_2_months is 'hcp_billing_monthly_active_users_last_2_months is a view that contains the count of active users for the current month and the previous month. the current month is a sum from the beginning of the current month until the start of the current hour (exclusive).';
+
+
+--
+-- postgresql database dump complete
+--
+

Triggers

diff --git a/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_before_inserted.sql b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_before_inserted.sql
new file mode 100644
index 000000000..ed9a7dcc5
--- /dev/null
+++ b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_before_inserted.sql	
@@ -0,0 +1,29 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token auth_token_before_inserted; type: trigger; schema: public; owner: -
+--
+
+create trigger auth_token_before_inserted before insert on public.auth_token for each row execute function public.auth_token_before_issued();
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_before_updated.sql b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_before_updated.sql
new file mode 100644
index 000000000..2d1958220
--- /dev/null
+++ b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_before_updated.sql	
@@ -0,0 +1,29 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token auth_token_before_updated; type: trigger; schema: public; owner: -
+--
+
+create trigger auth_token_before_updated before update on public.auth_token for each row execute function public.auth_token_before_issued();
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_deleted.sql b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_deleted.sql
new file mode 100644
index 000000000..97137014a
--- /dev/null
+++ b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_deleted.sql	
@@ -0,0 +1,29 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token auth_token_deleted; type: trigger; schema: public; owner: -
+--
+
+create trigger auth_token_deleted after delete on public.auth_token for each row execute function public.auth_token_deleted();
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_inserted.sql b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_inserted.sql
new file mode 100644
index 000000000..8b9a194d1
--- /dev/null
+++ b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_inserted.sql	
@@ -0,0 +1,29 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token auth_token_inserted; type: trigger; schema: public; owner: -
+--
+
+create trigger auth_token_inserted after insert on public.auth_token for each row execute function public.auth_token_inserted();
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_updated.sql b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_updated.sql
new file mode 100644
index 000000000..7734cce19
--- /dev/null
+++ b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/auth_token auth_token_updated.sql	
@@ -0,0 +1,29 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: auth_token auth_token_updated; type: trigger; schema: public; owner: -
+--
+
+create trigger auth_token_updated after update on public.auth_token for each row execute function public.auth_token_updated();
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact immutable_columns.sql b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact immutable_columns.sql
new file mode 100644
index 000000000..632225479
--- /dev/null
+++ b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact immutable_columns.sql	
@@ -0,0 +1,29 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: wh_auth_token_accumulating_fact immutable_columns; type: trigger; schema: public; owner: -
+--
+
+create trigger immutable_columns before update on public.wh_auth_token_accumulating_fact for each row execute function public.immutable_columns('user_id', 'user_key', 'auth_token_issued_time', 'auth_token_issued_time_key', 'auth_token_issued_date_key');
+
+
+--
+-- postgresql database dump complete
+--
+
diff --git a/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact wh_insert_auth_token.sql b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact wh_insert_auth_token.sql
new file mode 100644
index 000000000..3e97f0cab
--- /dev/null
+++ b/.schema-diff/triggers_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact wh_insert_auth_token.sql	
@@ -0,0 +1,29 @@
+--
+-- postgresql database dump
+--
+
+-- dumped from database version 13.14
+-- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
+
+set statement_timeout = 0;
+set lock_timeout = 0;
+set idle_in_transaction_session_timeout = 0;
+set client_encoding = 'utf8';
+set standard_conforming_strings = on;
+select pg_catalog.set_config('search_path', '', false);
+set check_function_bodies = false;
+set xmloption = content;
+set client_min_messages = warning;
+set row_security = off;
+
+--
+-- name: wh_auth_token_accumulating_fact wh_insert_auth_token; type: trigger; schema: public; owner: -
+--
+
+create trigger wh_insert_auth_token before insert on public.wh_auth_token_accumulating_fact for each row execute function public.wh_insert_auth_token();
+
+
+--
+-- postgresql database dump complete
+--
+

Indexes

diff --git a/.schema-diff/indexes_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/census_last_uploaded_one_row.sql b/.schema-diff/indexes_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/census_last_uploaded_one_row.sql
deleted file mode 100644
index 886da63d5..000000000
--- a/.schema-diff/indexes_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/census_last_uploaded_one_row.sql
+++ /dev/null
@@ -1,31 +0,0 @@
---
--- postgresql database dump
---
-
--- dumped from database version 13.14
--- dumped by pg_dump version 14.11 (ubuntu 14.11-1.pgdg22.04+1)
-
-set statement_timeout = 0;
-set lock_timeout = 0;
-set idle_in_transaction_session_timeout = 0;
-set client_encoding = 'utf8';
-set standard_conforming_strings = on;
-select pg_catalog.set_config('search_path', '', false);
-set check_function_bodies = false;
-set xmloption = content;
-set client_min_messages = warning;
-set row_security = off;
-
-set default_tablespace = '';
-
---
--- name: census_last_uploaded_one_row; type: index; schema: public; owner: -
---
-
-create unique index census_last_uploaded_one_row on public.census_last_uploaded using btree (((last_uploaded_at is not null)));
-
-
---
--- postgresql database dump complete
---
-

Constraints

diff --git a/.schema-diff/constraints_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/census_last_uploaded_pkey.sql b/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/census_last_uploaded_pkey.sql
index 88b44a019..a1467c016 100644
--- a/.schema-diff/constraints_c3297bb96f26efe0fe729f40308e7d431a6c7c1c/census_last_uploaded_pkey.sql
+++ b/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/census_last_uploaded_pkey.sql
@@ -1,2 +1,2 @@
 -- name: census_last_uploaded census_last_uploaded_pkey; type: constraint; schema: public; owner: -
-    add constraint census_last_uploaded_pkey primary key (last_uploaded_at);
+    add constraint census_last_uploaded_pkey primary key (metric);
diff --git a/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm_pkey.sql b/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm_pkey.sql
new file mode 100644
index 000000000..2b99861a2
--- /dev/null
+++ b/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm_pkey.sql
@@ -0,0 +1,2 @@
+-- name: census_metric_enm census_metric_enm_pkey; type: constraint; schema: public; owner: -
+    add constraint census_metric_enm_pkey primary key (name);
diff --git a/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_pkey.sql b/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_pkey.sql
new file mode 100644
index 000000000..b74557e70
--- /dev/null
+++ b/.schema-diff/constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_pkey.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_fact_pkey; type: constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_fact_pkey primary key (auth_token_id);

Foreign Key Constraints

diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm_fkey.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm_fkey.sql
new file mode 100644
index 000000000..2426eb04e
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/census_metric_enm_fkey.sql
@@ -0,0 +1,2 @@
+-- name: census_last_uploaded census_metric_enm_fkey; type: fk constraint; schema: public; owner: -
+    add constraint census_metric_enm_fkey foreign key (metric) references public.census_metric_enm(name) on update restrict on delete restrict;
diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_f_auth_token_approximate_last__fkey1.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_f_auth_token_approximate_last__fkey1.sql
new file mode 100644
index 000000000..fe5406f4a
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_f_auth_token_approximate_last__fkey1.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_f_auth_token_approximate_last__fkey1; type: fk constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_f_auth_token_approximate_last__fkey1 foreign key (auth_token_approximate_last_access_time_key) references public.wh_time_of_day_dimension(key) on update cascade on delete restrict;
diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fa_auth_token_approximate_last__fkey.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fa_auth_token_approximate_last__fkey.sql
new file mode 100644
index 000000000..86bccabd7
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fa_auth_token_approximate_last__fkey.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_fa_auth_token_approximate_last__fkey; type: fk constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_fa_auth_token_approximate_last__fkey foreign key (auth_token_approximate_last_access_date_key) references public.wh_date_dimension(key) on update cascade on delete restrict;
diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fac_auth_token_deleted_date_key_fkey.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fac_auth_token_deleted_date_key_fkey.sql
new file mode 100644
index 000000000..3d6be9fe7
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fac_auth_token_deleted_date_key_fkey.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_fac_auth_token_deleted_date_key_fkey; type: fk constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_fac_auth_token_deleted_date_key_fkey foreign key (auth_token_deleted_date_key) references public.wh_date_dimension(key) on update cascade on delete restrict;
diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fac_auth_token_deleted_time_key_fkey.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fac_auth_token_deleted_time_key_fkey.sql
new file mode 100644
index 000000000..2909d2100
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fac_auth_token_deleted_time_key_fkey.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_fac_auth_token_deleted_time_key_fkey; type: fk constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_fac_auth_token_deleted_time_key_fkey foreign key (auth_token_deleted_time_key) references public.wh_time_of_day_dimension(key) on update cascade on delete restrict;
diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_auth_token_issued_date_key_fkey.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_auth_token_issued_date_key_fkey.sql
new file mode 100644
index 000000000..37119895b
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_auth_token_issued_date_key_fkey.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_fact_auth_token_issued_date_key_fkey; type: fk constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_fact_auth_token_issued_date_key_fkey foreign key (auth_token_issued_date_key) references public.wh_date_dimension(key) on update cascade on delete restrict;
diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_auth_token_issued_time_key_fkey.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_auth_token_issued_time_key_fkey.sql
new file mode 100644
index 000000000..00559ece5
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_auth_token_issued_time_key_fkey.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_fact_auth_token_issued_time_key_fkey; type: fk constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_fact_auth_token_issued_time_key_fkey foreign key (auth_token_issued_time_key) references public.wh_time_of_day_dimension(key) on update cascade on delete restrict;
diff --git a/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_user_key_fkey.sql b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_user_key_fkey.sql
new file mode 100644
index 000000000..c5a8c37d3
--- /dev/null
+++ b/.schema-diff/fk_constraints_a356edf1622d4714672dd2387589c7929c59153a/wh_auth_token_accumulating_fact_user_key_fkey.sql
@@ -0,0 +1,2 @@
+-- name: wh_auth_token_accumulating_fact wh_auth_token_accumulating_fact_user_key_fkey; type: fk constraint; schema: public; owner: -
+    add constraint wh_auth_token_accumulating_fact_user_key_fkey foreign key (user_key) references public.wh_user_dimension(key) on update cascade on delete restrict;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants