Skip to content

Commit

Permalink
[feat]: add initial source SQL DDL for the database
Browse files Browse the repository at this point in the history
for issue #4
  • Loading branch information
jimbrig committed Dec 17, 2024
1 parent cd7eff7 commit 47ef645
Show file tree
Hide file tree
Showing 125 changed files with 565 additions and 2 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,6 @@ po/*~
rsconnect/

# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
Expand Down
Empty file added src/database/database.dbml
Empty file.
Empty file added src/database/database.sql
Empty file.
5 changes: 5 additions & 0 deletions src/database/queries/fetch_all_surveys_for_gmh_property.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SELECT s.survey_id, s.survey_date, s.status, u.name AS created_by
FROM mkt.surveys s
JOIN mkt.properties p ON s.property_id = p.property_id
JOIN auth.users u ON s.created_by = u.user_id
WHERE p.is_gmh = TRUE;
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
SELECT sr.field_name, sr.field_value, sr.previous_value
FROM mkt.survey_responses sr
JOIN mkt.survey_sections ss ON sr.section_id = ss.section_id
WHERE ss.survey_id = 123 AND ss.name = 'Leasing Summary';
Empty file added src/database/schemas/README.md
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
2 changes: 2 additions & 0 deletions src/database/schemas/auth/seed/seed.auth.tenants.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
INSERT INTO auth.tenants (tenant_name, tenant_domain, created_by)
VALUES ('Google', 'gmail.com', 1);
Empty file.
9 changes: 9 additions & 0 deletions src/database/schemas/auth/tables/auth.apps.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-- Apps: Represents applications tied to tenants
CREATE TABLE auth.apps (
app_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
tenant_id INTEGER REFERENCES auth.tenants(tenant_id),
app_name TEXT NOT NULL,
app_url TEXT NOT NULL,
app_admin UUID REFERENCES auth.users(user_id),
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
8 changes: 8 additions & 0 deletions src/database/schemas/auth/tables/auth.roles.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Roles: Defines roles for users
CREATE TABLE auth.roles (
role_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
role_name TEXT UNIQUE NOT NULL,
role_description TEXT,
role_enabled BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
);
10 changes: 10 additions & 0 deletions src/database/schemas/auth/tables/auth.sessions.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Sessions: Tracks active user sessions
CREATE TABLE auth.sessions (
session_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID REFERENCES auth.users(user_id) ON DELETE CASCADE,
tenant_id INT REFERENCES auth.tenants(tenant_id) ON DELETE CASCADE,
app_id INT REFERENCES auth.apps(app_id) ON DELETE CASCADE,
session_token TEXT NOT NULL,
expires_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
7 changes: 7 additions & 0 deletions src/database/schemas/auth/tables/auth.tenants.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-- Tenants: Represents organizations using the system
CREATE TABLE auth.tenants (
tenant_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
tenant_name TEXT NOT NULL,
tenant_domain TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
8 changes: 8 additions & 0 deletions src/database/schemas/auth/tables/auth.users.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
-- Users: Represents system users
CREATE TABLE auth.users (
user_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id INT NOT NULL REFERENCES auth.tenants(tenant_id) ON DELETE CASCADE,
email TEXT UNIQUE NOT NULL,
hashed_password TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);
10 changes: 10 additions & 0 deletions src/database/schemas/auth/tables/auth.verification.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE TABLE auth.verification (
verification_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
user_id INTEGER NOT NULL,
verification_code TEXT NOT NULL,
verification_type TEXT NOT NULL,
verification_status TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY (user_id) REFERENCES auth.users (user_id)
)
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
12 changes: 12 additions & 0 deletions src/database/schemas/gmh/tables/gmh.property_profiles.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CREATE TABLE gmh.property_profiles (
property_id BIGINT PRIMARY KEY REFERENCES gmh.properties(property_id),
profile_name TEXT,
profile_image_url TEXT,
profile_icon_url TEXT,
profile_website_url TEXT,
profile_booking_url TEXT,
profile_description TEXT,
profile_phone TEXT,
profile_email TEXT,
profile_address TEXT
);
5 changes: 5 additions & 0 deletions src/database/schemas/gmh/tables/gmh.property_units.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE TABLE gmh.property_units (
unit_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
property_id INTEGER REFERENCES gmh.properties(property_id),
-- TODO
)
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE OR REPLACE FUNCTION logs.add_audit_triggers_to_schema(schema_name TEXT)
RETURNS VOID AS $$
DECLARE
tbl RECORD;
BEGIN
FOR tbl IN SELECT tablename FROM pg_tables WHERE schemaname = schema_name LOOP
PERFORM logs.add_audit_triggers_to_table(schema_name, tbl.tablename);
END LOOP;
END;
$$ LANGUAGE plpgsql;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE OR REPLACE FUNCTION logs.add_audit_triggers_to_table(schema_name TEXT, table_name TEXT)
RETURNS VOID AS $$
BEGIN
EXECUTE format(
'CREATE TRIGGER audit_trigger_%I AFTER INSERT OR UPDATE OR DELETE ON %I.%I FOR EACH ROW EXECUTE FUNCTION logs.audit_trigger_func();',
table_name, schema_name, table_name
);
END;
$$ LANGUAGE plpgsql;
16 changes: 16 additions & 0 deletions src/database/schemas/logs/functions/logs.audit_trigger_func.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CREATE OR REPLACE FUNCTION logs.audit_trigger_func()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
INSERT INTO logs.audit_logs (schema_name, table_name, operation, new_data, user_id)
VALUES (TG_TABLE_SCHEMA, TG_TABLE_NAME, 'INSERT', row_to_json(NEW), COALESCE(current_setting('gmhdatahub.user_id', true)::INT, 0));
ELSIF TG_OP = 'UPDATE' THEN
INSERT INTO logs.audit_logs (schema_name, table_name, operation, old_data, new_data, user_id)
VALUES (TG_TABLE_SCHEMA, TG_TABLE_NAME, 'UPDATE', row_to_json(OLD), row_to_json(NEW), COALESCE(current_setting('gmhdatahub.user_id', true)::INT, 0));
ELSIF TG_OP = 'DELETE' THEN
INSERT INTO logs.audit_logs (schema_name, table_name, operation, old_data, user_id)
VALUES (TG_TABLE_SCHEMA, TG_TABLE_NAME, 'DELETE', row_to_json(OLD), COALESCE(current_setting('gmhdatahub.user_id', true)::INT, 0));
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
CREATE OR REPLACE FUNCTION logs.remove_audit_triggers_from_schema(schema_name TEXT)
RETURNS VOID AS $$
DECLARE
tbl RECORD;
BEGIN
FOR tbl IN SELECT tablename FROM pg_tables WHERE schemaname = schema_name LOOP
PERFORM logs.remove_audit_triggers_from_table(schema_name, tbl.tablename);
END LOOP;
END;
$$ LANGUAGE plpgsql;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE OR REPLACE FUNCTION logs.remove_audit_triggers_from_table(schema_name TEXT, table_name TEXT)
RETURNS VOID AS $$
BEGIN
EXECUTE format(
'DROP TRIGGER IF EXISTS audit_trigger_%I ON %I.%I;',
table_name, schema_name, table_name
);
END;
$$ LANGUAGE plpgsql;
80 changes: 80 additions & 0 deletions src/database/schemas/logs/logs.schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
CREATE SCHEMA IF NOT EXISTS logs;

-- Table: audit_logs
CREATE TABLE logs.audit_logs (
audit_log_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
schema_name TEXT NOT NULL,
table_name TEXT NOT NULL,
operation TEXT NOT NULL, -- INSERT, UPDATE, DELETE
old_data JSONB, -- Data before the operation
new_data JSONB, -- Data after the operation
user_id INTEGER, -- User responsible for the change
session_id UUID DEFAULT gen_random_uuid(), -- Unique session identifier
query TEXT, -- SQL query executed
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_audit_logs_table_name ON logs.audit_logs (table_name);
CREATE INDEX idx_audit_logs_operation ON logs.audit_logs (operation);
CREATE INDEX idx_audit_logs_created_at ON logs.audit_logs (created_at);

CREATE TABLE logs.error_logs (
error_log_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
error_message TEXT NOT NULL,
error_context JSONB, -- Context or additional metadata
user_id INTEGER,
session_id UUID DEFAULT gen_random_uuid(),
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_error_logs_created_at ON logs.error_logs (created_at);

CREATE TABLE logs.system_logs (
system_log_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
event_type TEXT NOT NULL, -- Example: startup, shutdown
event_details JSONB, -- Metadata about the event
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_system_logs_created_at ON logs.system_logs (created_at);

CREATE TABLE logs.entrata_logs (
entrata_log_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
request_id UUID NOT NULL, -- Unique request identifier
endpoint TEXT NOT NULL, -- Entrata API endpoint called
operation TEXT NOT NULL, -- Example: GET, POST
status_code INTEGER, -- HTTP response status code
request_payload JSONB, -- API request payload
response_body JSONB, -- API response body
user_id INTEGER,
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_entrata_logs_endpoint ON logs.entrata_logs (endpoint);
CREATE INDEX idx_entrata_logs_created_at ON logs.entrata_logs (created_at);

CREATE TABLE logs.market_survey_logs (
response_log_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
survey_id INTEGER NOT NULL, -- Survey associated with the log
section_id INTEGER, -- Section of the survey
response_id INTEGER, -- Individual response identifier
user_id INTEGER, -- User who submitted or changed the response
property_id INTEGER, -- Property related to the survey
leasing_week_id INTEGER, -- Leasing week associated with the survey
response_data JSONB, -- The survey response data
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_market_survey_logs_survey_id ON logs.market_survey_logs (survey_id);
CREATE INDEX idx_market_survey_logs_created_at ON logs.market_survey_logs (created_at);

CREATE TABLE logs.pipeline_logs (
pipeline_log_id INTEGER PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
pipeline_name TEXT NOT NULL, -- Name of the data pipeline
pipeline_status TEXT NOT NULL, -- Status: success, failure, running
data_lineage JSONB, -- Metadata about data lineage
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_pipeline_logs_created_at ON logs.pipeline_logs (created_at);
CREATE INDEX idx_pipeline_logs_pipeline_name ON logs.pipeline_logs (pipeline_name);
10 changes: 10 additions & 0 deletions src/database/schemas/logs/tables/logs.audit_logs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- Audit Logs
CREATE TABLE logs.audit_logs (
audit_log_id SERIAL PRIMARY KEY,
table_name TEXT NOT NULL,
operation TEXT NOT NULL,
old_data JSONB,
new_data JSONB,
user_id INT REFERENCES auth.users(user_id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Empty file.
Empty file.
4 changes: 4 additions & 0 deletions src/database/schemas/logs/views/logs.entrata_errors.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE VIEW logs.entrata_errors AS
SELECT *
FROM logs.entrata_logs
WHERE status_code >= 400;
6 changes: 6 additions & 0 deletions src/database/schemas/logs/views/logs.pipeline_runs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
CREATE VIEW logs.pipeline_runs AS
SELECT pipeline_name, COUNT(*) AS run_count,
COUNT(*) FILTER (WHERE pipeline_status = 'success') AS success_count,
COUNT(*) FILTER (WHERE pipeline_status = 'failure') AS failure_count
FROM logs.pipeline_logs
GROUP BY pipeline_name;
56 changes: 56 additions & 0 deletions src/database/schemas/logs/views/logs.recent_activity.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
CREATE OR REPLACE VIEW logs.recent_activity AS
SELECT
a.created_at,
'Audit Log' AS log_type,
a.schema_name || '.' || a.table_name AS context,
a.operation AS action,
u.email AS user_email,
CASE
WHEN a.operation = 'UPDATE' THEN (
SELECT string_agg(
key || ': ' || old_value || ' -> ' || new_value,
', '
)
FROM (
SELECT
key,
a.old_data -> key AS old_value,
a.new_data -> key AS new_value
FROM jsonb_object_keys(a.old_data) AS key
WHERE a.old_data -> key IS DISTINCT FROM a.new_data -> key
) AS changes
)
WHEN a.operation = 'INSERT' THEN 'New record: ' || a.new_data::TEXT
WHEN a.operation = 'DELETE' THEN 'Deleted record: ' || a.old_data::TEXT
ELSE 'Unknown operation'
END AS details
FROM logs.audit_logs a
LEFT JOIN auth.users u ON a.user_id = u.user_id
WHERE a.created_at > NOW() - INTERVAL '7 days'

UNION ALL

SELECT
e.created_at,
'Error Log' AS log_type,
NULL AS context,
'Error' AS action,
u.email AS user_email,
e.error_message || ': ' || e.error_context::TEXT AS details
FROM logs.error_logs e
LEFT JOIN auth.users u ON e.user_id = u.user_id
WHERE e.created_at > NOW() - INTERVAL '7 days'

UNION ALL

SELECT
s.created_at,
'System Log' AS log_type,
NULL AS context,
s.event_type AS action,
NULL AS user_email,
s.event_details::TEXT AS details
FROM logs.system_logs s
WHERE s.created_at > NOW() - INTERVAL '7 days'

ORDER BY created_at DESC;
4 changes: 4 additions & 0 deletions src/database/schemas/logs/views/logs.recent_audit_logs.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE VIEW logs.recent_audit_logs AS
SELECT *
FROM logs.audit_logs
WHERE created_at > NOW() - INTERVAL '7 days';
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
-- Function to update the is_current flag
CREATE OR REPLACE FUNCTION mkt.update_leasing_week_is_current_flag()
RETURNS TRIGGER AS $$
BEGIN
-- Reset all rows to FALSE
UPDATE mkt.leasing_weeks
SET is_current = FALSE;

-- Set the current leasing week to TRUE
UPDATE mkt.leasing_weeks
SET is_current = TRUE
WHERE CURRENT_DATE BETWEEN start_date AND end_date;

RETURN NULL; -- Triggers on UPDATE/INSERT do not return a value
END;
$$ LANGUAGE plpgsql;
Empty file.
15 changes: 15 additions & 0 deletions src/database/schemas/mkt/seed/seed.mkt.fee_types.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Application Fee', 'Fee for applying to lease an apartment');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Administration Fee', 'Fee for administrative costs');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Utility Set Up Fee', 'Fee for setting up utilities');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Utility Deposit', 'Deposit for utilities');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Amenity Fee', 'Fee for amenities');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Common Area Fee', 'Fee for common areas');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Smart Home Fee', 'Fee for smart home features');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Restoration Fee', 'Fee for restoration');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Security Deposit', 'Deposit for security');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Pet Fee - Dog', 'Fee for dogs');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Pet Deposit - Dog', 'Deposit for dogs');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Pet Rent - Dog', 'Rent for dogs');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Pet Fee - Cat', 'Fee for cats');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Pet Deposit - Cat', 'Deposit for cats');
INSERT INTO mkt.fee_types (fee_type_name, fee_type_description) VALUES ('Pet Rent - Cat', 'Rent for cats');
29 changes: 29 additions & 0 deletions src/database/schemas/mkt/seed/seed.mkt.leasing_weeks.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
-- Insert initial leasing weeks manually
INSERT INTO mkt.leasing_weeks (start_date)
VALUES
('2024-01-01'),
('2024-01-08'),
('2024-01-15');

-- Recursive CTE to generate the remaining weeks for 2024
WITH RECURSIVE generate_weeks AS (
-- Start with the first week of 2024
SELECT DATE '2024-01-01' AS start_date
UNION ALL
-- Generate subsequent weeks by adding 7 days
SELECT (start_date + INTERVAL '7 days')::DATE
FROM generate_weeks
WHERE start_date + INTERVAL '7 days' < DATE '2025-01-01'
)
-- Insert the generated weeks into the table, avoiding duplicates with LEFT JOIN
INSERT INTO mkt.leasing_weeks (start_date)
SELECT gw.start_date
FROM generate_weeks gw
LEFT JOIN mkt.leasing_weeks lw ON gw.start_date = lw.start_date
WHERE lw.start_date IS NULL;

-- Verify the table data
SELECT * FROM mkt.leasing_weeks;

-- Verify the is_current flag
SELECT * FROM mkt.leasing_weeks WHERE is_current = TRUE;
6 changes: 6 additions & 0 deletions src/database/schemas/mkt/seed/seed.mkt.parking_types.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

INSERT INTO mkt.parking_types (parking_type_name) VALUES ('Surface');
INSERT INTO mkt.parking_types (parking_type_name) VALUES ('Reserved');
INSERT INTO mkt.parking_types (parking_type_name) VALUES ('Covered');
INSERT INTO mkt.parking_types (parking_type_name) VALUES ('Garage');
INSERT INTO mkt.parking_types (parking_type_name) VALUES ('Other');
Loading

0 comments on commit 47ef645

Please sign in to comment.