From c2467c51157143f0116e260684b81236e62830d6 Mon Sep 17 00:00:00 2001 From: Mario Fernando Araya Royo Date: Tue, 3 Dec 2024 17:37:08 -0600 Subject: [PATCH 1/2] Feat/create escrow transactions (#39) * dockerize hasura command * review errors on hasura * docker changes * add escrow_transaction table * escrow_xdr_transactions table * add escrow_api_calls tables * add include tables * add migrations --------- Co-authored-by: Randall Valenciano Fallas --- Dockerfile | 18 +-- docker-compose.yml | 32 +++-- .../safetrust/tables/escrow_api_calls.yaml | 26 ++++ .../safetrust/tables/escrow_transactions.yaml | 60 +++++++++ .../tables/escrow_xdr_transactions.yaml | 28 ++++ .../databases/safetrust/tables/tables.yaml | 5 +- .../down.sql | 18 +++ .../up.sql | 122 ++++++++++++++++++ .../down.sql | 6 + .../1733159608947_escrow_transactions/up.sql | 35 +++++ .../down.sql | 5 + .../up.sql | 18 +++ .../1733171122098_escrow_api_calls/down.sql | 2 + .../1733171122098_escrow_api_calls/up.sql | 14 ++ 14 files changed, 368 insertions(+), 21 deletions(-) create mode 100644 metadata/databases/safetrust/tables/escrow_api_calls.yaml create mode 100644 metadata/databases/safetrust/tables/escrow_transactions.yaml create mode 100644 metadata/databases/safetrust/tables/escrow_xdr_transactions.yaml create mode 100644 migrations/safetrust/1732599256424_add_user_profile_columns_to_users/down.sql create mode 100644 migrations/safetrust/1732599256424_add_user_profile_columns_to_users/up.sql create mode 100644 migrations/safetrust/1733159608947_escrow_transactions/down.sql create mode 100644 migrations/safetrust/1733159608947_escrow_transactions/up.sql create mode 100644 migrations/safetrust/1733160060158_escrow_xdr_transactions/down.sql create mode 100644 migrations/safetrust/1733160060158_escrow_xdr_transactions/up.sql create mode 100644 migrations/safetrust/1733171122098_escrow_api_calls/down.sql create mode 100644 migrations/safetrust/1733171122098_escrow_api_calls/up.sql diff --git a/Dockerfile b/Dockerfile index 7ea2a75..45dd1c9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,18 @@ -# Use Alpine Linux as the base image -FROM alpine:3.18 +# Use a lightweight Linux base image +FROM debian:bullseye-slim + +# Set environment variables for non-interactive installations +ENV DEBIAN_FRONTEND=noninteractive # Install necessary dependencies -RUN apk add --no-cache \ +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ curl \ - bash \ ca-certificates \ - && update-ca-certificates + && rm -rf /var/lib/apt/lists/* # Download and install Hasura CLI RUN curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash -# Set the working directory inside the container -WORKDIR /app - # Set the default command to show Hasura CLI help -CMD ["hasura", "help"] +CMD ["hasura", "console"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index aa056c9..4e660c5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,6 @@ services: postgres: image: postgis/postgis:15-3.3 - platform: linux/amd64 restart: always volumes: - db_data:/var/lib/postgresql/data @@ -9,6 +8,12 @@ services: POSTGRES_PASSWORD: postgrespassword ports: - "5432:5432" + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + start_period: 10s graphql-engine: image: hasura/graphql-engine:v2.42.0 @@ -21,22 +26,18 @@ services: HASURA_GRAPHQL_ENABLE_CONSOLE: "true" HASURA_GRAPHQL_DEV_MODE: "true" HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log - ## uncomment next line to run console offline (i.e load console assets from server instead of CDN) - # HASURA_GRAPHQL_CONSOLE_ASSETS_DIR: /srv/console-assets - ## uncomment next line to set an admin secret - HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey HASURA_GRAPHQL_METADATA_DEFAULTS: '{"backend_configs":{"dataconnector":{"athena":{"uri":"http://data-connector-agent:8081/api/v1/athena"},"mariadb":{"uri":"http://data-connector-agent:8081/api/v1/mariadb"},"mysql8":{"uri":"http://data-connector-agent:8081/api/v1/mysql"},"oracle":{"uri":"http://data-connector-agent:8081/api/v1/oracle"},"snowflake":{"uri":"http://data-connector-agent:8081/api/v1/snowflake"}}}}' - HASURA_GRAPHQL_JWT_SECRET: '{"type":"RS256","jwk_url": "https://www.googleapis.com/service_accounts/v1/jwk/securetoken@system.gserviceaccount.com","audience": "safetrust-dev", "claims_map": {"x-hasura-allowed-roles": ["user","anonymous","tenant","landlord"],"x-hasura-default-role": "user","x-hasura-user-id": {"path": "$.sub"}},"issuer": "https://securetoken.google.com/safetrust-dev"}' depends_on: + postgres: + condition: service_healthy data-connector-agent: condition: service_healthy data-connector-agent: image: hasura/graphql-data-connector:v2.42.0 - platform: linux/amd64 restart: always ports: - - 8081:8081 + - "8081:8081" environment: QUARKUS_LOG_LEVEL: ERROR QUARKUS_OPENTELEMETRY_ENABLED: "false" @@ -51,11 +52,20 @@ services: build: context: . dockerfile: Dockerfile - entrypoint: ["/bin/bash"] tty: true volumes: - - ./hasura-project:/hasura-project # Mount your Hasura project directory + - .:/app # Mount your Hasura project directory + environment: + HASURA_GRAPHQL_ENDPOINT: http://graphql-engine:8080 + PG_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres working_dir: /app + command: hasura console --address 0.0.0.0 --console-port 9695 + ports: + - "9695:9695" + depends_on: + graphql-engine: + condition: service_healthy + volumes: - db_data: + db_data: \ No newline at end of file diff --git a/metadata/databases/safetrust/tables/escrow_api_calls.yaml b/metadata/databases/safetrust/tables/escrow_api_calls.yaml new file mode 100644 index 0000000..aeb2358 --- /dev/null +++ b/metadata/databases/safetrust/tables/escrow_api_calls.yaml @@ -0,0 +1,26 @@ +table: + name: escrow_api_calls + schema: public + columns: + - name: id + type: uuid + - name: escrow_transaction_id + type: uuid + - name: endpoint + type: text + - name: method + type: text + - name: request_body + type: jsonb + - name: http_status_code + type: integer + - name: response_body + type: jsonb + - name: error_details + type: jsonb + - name: created_at + type: timestamptz + object_relationships: + - name: escrow_transaction + using: + foreign_key_constraint_on: escrow_transaction_id \ No newline at end of file diff --git a/metadata/databases/safetrust/tables/escrow_transactions.yaml b/metadata/databases/safetrust/tables/escrow_transactions.yaml new file mode 100644 index 0000000..1414ea9 --- /dev/null +++ b/metadata/databases/safetrust/tables/escrow_transactions.yaml @@ -0,0 +1,60 @@ +table: + name: escrow_transactions + schema: public + columns: + - name: id + type: uuid + - name: bid_request_id + type: uuid + - name: engagement_id + type: text + - name: contract_id + type: text + - name: signer_address + type: text + - name: transaction_type + type: escrow_transaction_type + - name: status + type: escrow_status + - name: http_status_code + type: integer + - name: http_response_body + type: jsonb + - name: http_error_details + type: jsonb + - name: amount + type: numeric + - name: initial_deposit_percentage + type: integer + - name: cancellation_reason + type: text + - name: cancelled_by + type: text + - name: refund_status + type: escrow_status + - name: metadata + type: jsonb + - name: created_at + type: timestamptz + - name: updated_at + type: timestamptz + - name: completed_at + type: timestamptz + object_relationships: + - name: bid_request + using: + foreign_key_constraint_on: bid_request_id + - name: cancelled_by_user + using: + foreign_key_constraint_on: cancelled_by + array_relationships: + - name: xdr_transactions + using: + foreign_key_constraint_on: + column: escrow_transaction_id + table: escrow_xdr_transactions + - name: api_calls + using: + foreign_key_constraint_on: + column: escrow_transaction_id + table: escrow_api_calls \ No newline at end of file diff --git a/metadata/databases/safetrust/tables/escrow_xdr_transactions.yaml b/metadata/databases/safetrust/tables/escrow_xdr_transactions.yaml new file mode 100644 index 0000000..b005908 --- /dev/null +++ b/metadata/databases/safetrust/tables/escrow_xdr_transactions.yaml @@ -0,0 +1,28 @@ +table: + name: escrow_xdr_transactions + schema: public + columns: + - name: id + type: uuid + - name: escrow_transaction_id + type: uuid + - name: xdr_type + type: escrow_transaction_type + - name: unsigned_xdr + type: text + - name: signed_xdr + type: text + - name: status + type: xdr_status + - name: signing_address + type: text + - name: error_message + type: text + - name: created_at + type: timestamptz + - name: updated_at + type: timestamptz + object_relationships: + - name: escrow_transaction + using: + foreign_key_constraint_on: escrow_transaction_id \ No newline at end of file diff --git a/metadata/databases/safetrust/tables/tables.yaml b/metadata/databases/safetrust/tables/tables.yaml index 79293b3..c5e7f8a 100644 --- a/metadata/databases/safetrust/tables/tables.yaml +++ b/metadata/databases/safetrust/tables/tables.yaml @@ -3,5 +3,8 @@ - "!include public_spatial_ref_sys.yaml" - "!include public_user_wallets.yaml" - "!include public_users.yaml" +- "!include escrow_api_calls.yaml" +- "!include escrow_transactions.yaml" +- "!include escrow_xdr_transactions.yaml" - "!include public_apartments.yaml" -- "!include public_apartment_images.yaml" \ No newline at end of file +- "!include public_apartment_images.yaml" diff --git a/migrations/safetrust/1732599256424_add_user_profile_columns_to_users/down.sql b/migrations/safetrust/1732599256424_add_user_profile_columns_to_users/down.sql new file mode 100644 index 0000000..148a521 --- /dev/null +++ b/migrations/safetrust/1732599256424_add_user_profile_columns_to_users/down.sql @@ -0,0 +1,18 @@ +-- Drop indexes +DROP INDEX IF EXISTS idx_escrow_transactions_bid; +DROP INDEX IF EXISTS idx_escrow_transactions_contract; +DROP INDEX IF EXISTS idx_escrow_transactions_status; +DROP INDEX IF EXISTS idx_escrow_transactions_type_status; +DROP INDEX IF EXISTS idx_escrow_xdr_transaction; +DROP INDEX IF EXISTS idx_escrow_api_calls_transaction; +DROP INDEX IF EXISTS idx_escrow_api_calls_status; + +-- Drop tables +DROP TABLE IF EXISTS escrow_api_calls; +DROP TABLE IF EXISTS escrow_xdr_transactions; +DROP TABLE IF EXISTS escrow_transactions; + +-- Drop types +DROP TYPE IF EXISTS escrow_transaction_type; +DROP TYPE IF EXISTS escrow_status; +DROP TYPE IF EXISTS xdr_status; \ No newline at end of file diff --git a/migrations/safetrust/1732599256424_add_user_profile_columns_to_users/up.sql b/migrations/safetrust/1732599256424_add_user_profile_columns_to_users/up.sql new file mode 100644 index 0000000..f064b7c --- /dev/null +++ b/migrations/safetrust/1732599256424_add_user_profile_columns_to_users/up.sql @@ -0,0 +1,122 @@ +-- Create transaction type enum +CREATE TYPE escrow_transaction_type AS ENUM ( + 'CREATE_ESCROW', + 'FUND_ESCROW', + 'COMPLETE_ESCROW', + 'CANCEL_ESCROW', + 'REFUND_ESCROW' +); + +-- Create escrow status enum +CREATE TYPE escrow_status AS ENUM ( + 'PENDING', + 'AWAITING_SIGNATURE', + 'SIGNED', + 'PROCESSING', + 'COMPLETED', + 'FAILED', + 'CANCELLED', + 'REFUNDED' +); + +-- Create XDR status enum +CREATE TYPE xdr_status AS ENUM ( + 'PENDING', + 'GENERATED', + 'SIGNED', + 'SUBMITTED', + 'CONFIRMED', + 'FAILED' +); + +-- Create main escrow_transactions table +CREATE TABLE escrow_transactions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + bid_request_id UUID REFERENCES bid_requests(id) ON DELETE CASCADE, + + -- Trustless API fields + engagement_id TEXT, + contract_id TEXT, + signer_address TEXT, + + -- Transaction details + transaction_type escrow_transaction_type NOT NULL, + status escrow_status NOT NULL DEFAULT 'PENDING', + + -- HTTP response tracking + http_status_code INTEGER, + http_response_body JSONB, + http_error_details JSONB, + + -- Amount and deposit tracking + amount DECIMAL(10,2) NOT NULL CHECK (amount > 0), + initial_deposit_percentage INTEGER DEFAULT 50, + + -- Cancellation details + cancellation_reason TEXT, + cancelled_by TEXT REFERENCES users(id), + refund_status escrow_status, + + -- Metadata and timestamps + metadata JSONB, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + completed_at TIMESTAMP WITH TIME ZONE, + + -- Constraints + CONSTRAINT valid_http_status CHECK ( + http_status_code >= 100 AND + http_status_code < 600 + ), + CONSTRAINT valid_contract_id_when_needed CHECK ( + (transaction_type IN ('FUND_ESCROW', 'COMPLETE_ESCROW', 'CANCEL_ESCROW')) = + (contract_id IS NOT NULL) + ) +); + +-- Create XDR transactions tracking table +CREATE TABLE escrow_xdr_transactions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + escrow_transaction_id UUID REFERENCES escrow_transactions(id) ON DELETE CASCADE, + xdr_type escrow_transaction_type NOT NULL, + unsigned_xdr TEXT NOT NULL, + signed_xdr TEXT, + status xdr_status DEFAULT 'PENDING', + signing_address TEXT, + error_message TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + CONSTRAINT valid_xdr_fields CHECK ( + (status = 'SIGNED' AND signed_xdr IS NOT NULL) OR + (status != 'SIGNED') + ) +); + +-- Create API calls tracking table +CREATE TABLE escrow_api_calls ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + escrow_transaction_id UUID REFERENCES escrow_transactions(id) ON DELETE CASCADE, + endpoint TEXT NOT NULL, + method TEXT NOT NULL, + request_body JSONB, + http_status_code INTEGER, + response_body JSONB, + error_details JSONB, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + CONSTRAINT valid_http_status CHECK ( + http_status_code >= 100 AND + http_status_code < 600 + ), + CONSTRAINT valid_http_method CHECK ( + method IN ('GET', 'POST', 'PUT', 'DELETE', 'PATCH') + ) +); + +-- Create required indexes +CREATE INDEX idx_escrow_transactions_bid ON escrow_transactions(bid_request_id); +CREATE INDEX idx_escrow_transactions_contract ON escrow_transactions(contract_id); +CREATE INDEX idx_escrow_transactions_status ON escrow_transactions(status); +CREATE INDEX idx_escrow_transactions_type_status ON escrow_transactions(transaction_type, status); +CREATE INDEX idx_escrow_xdr_transaction ON escrow_xdr_transactions(escrow_transaction_id); +CREATE INDEX idx_escrow_api_calls_transaction ON escrow_api_calls(escrow_transaction_id); +CREATE INDEX idx_escrow_api_calls_status ON escrow_api_calls(http_status_code); diff --git a/migrations/safetrust/1733159608947_escrow_transactions/down.sql b/migrations/safetrust/1733159608947_escrow_transactions/down.sql new file mode 100644 index 0000000..fa551b4 --- /dev/null +++ b/migrations/safetrust/1733159608947_escrow_transactions/down.sql @@ -0,0 +1,6 @@ +-- delete table escrow_transactions +DROP TABLE IF EXISTS escrow_transactions; + +-- delete types ENUM +DROP TYPE IF EXISTS escrow_transaction_type; +DROP TYPE IF EXISTS escrow_status; diff --git a/migrations/safetrust/1733159608947_escrow_transactions/up.sql b/migrations/safetrust/1733159608947_escrow_transactions/up.sql new file mode 100644 index 0000000..46ed9bd --- /dev/null +++ b/migrations/safetrust/1733159608947_escrow_transactions/up.sql @@ -0,0 +1,35 @@ +-- create types ENUM for escrow_transaction_type and escrow_status +CREATE TYPE escrow_transaction_type AS ENUM ( + 'CREATE_ESCROW', 'FUND_ESCROW', 'COMPLETE_ESCROW', 'CANCEL_ESCROW', 'REFUND_ESCROW' +); + +CREATE TYPE escrow_status AS ENUM ( + 'PENDING', 'AWAITING_SIGNATURE', 'SIGNED', 'PROCESSING', 'COMPLETED', 'FAILED', 'CANCELLED', 'REFUNDED' +); + +-- create table escrow_transactions +CREATE TABLE escrow_transactions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + bid_request_id UUID REFERENCES bid_requests(id) ON DELETE CASCADE, + engagement_id TEXT, + contract_id TEXT, + signer_address TEXT, + transaction_type escrow_transaction_type NOT NULL, + status escrow_status NOT NULL DEFAULT 'PENDING', + http_status_code INTEGER, + http_response_body JSONB, + http_error_details JSONB, + amount DECIMAL(10,2) NOT NULL CHECK (amount > 0), + initial_deposit_percentage INTEGER DEFAULT 50, + cancellation_reason TEXT, + cancelled_by TEXT REFERENCES users(id), + refund_status escrow_status, + metadata JSONB, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + completed_at TIMESTAMP WITH TIME ZONE, + CONSTRAINT valid_http_status CHECK (http_status_code >= 100 AND http_status_code < 600) +); + + + diff --git a/migrations/safetrust/1733160060158_escrow_xdr_transactions/down.sql b/migrations/safetrust/1733160060158_escrow_xdr_transactions/down.sql new file mode 100644 index 0000000..e213dc5 --- /dev/null +++ b/migrations/safetrust/1733160060158_escrow_xdr_transactions/down.sql @@ -0,0 +1,5 @@ +-- Eliminar tabla escrow_xdr_transactions +DROP TABLE IF EXISTS escrow_xdr_transactions; + +-- Eliminar tipo ENUM xdr_status +DROP TYPE IF EXISTS xdr_status; diff --git a/migrations/safetrust/1733160060158_escrow_xdr_transactions/up.sql b/migrations/safetrust/1733160060158_escrow_xdr_transactions/up.sql new file mode 100644 index 0000000..cd594d4 --- /dev/null +++ b/migrations/safetrust/1733160060158_escrow_xdr_transactions/up.sql @@ -0,0 +1,18 @@ +-- Crear tipos ENUM para xdr_status si no existe +CREATE TYPE xdr_status AS ENUM ( + 'PENDING', 'GENERATED', 'SIGNED', 'SUBMITTED', 'CONFIRMED', 'FAILED' +); + +-- Crear tabla escrow_xdr_transactions +CREATE TABLE escrow_xdr_transactions ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + escrow_transaction_id UUID REFERENCES escrow_transactions(id) ON DELETE CASCADE, + xdr_type escrow_transaction_type NOT NULL, + unsigned_xdr TEXT NOT NULL, + signed_xdr TEXT, + status xdr_status DEFAULT 'PENDING', + signing_address TEXT, + error_message TEXT, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); diff --git a/migrations/safetrust/1733171122098_escrow_api_calls/down.sql b/migrations/safetrust/1733171122098_escrow_api_calls/down.sql new file mode 100644 index 0000000..d2d63b5 --- /dev/null +++ b/migrations/safetrust/1733171122098_escrow_api_calls/down.sql @@ -0,0 +1,2 @@ +-- Eliminar tabla escrow_api_calls +DROP TABLE IF EXISTS escrow_api_calls; diff --git a/migrations/safetrust/1733171122098_escrow_api_calls/up.sql b/migrations/safetrust/1733171122098_escrow_api_calls/up.sql new file mode 100644 index 0000000..e09ad7d --- /dev/null +++ b/migrations/safetrust/1733171122098_escrow_api_calls/up.sql @@ -0,0 +1,14 @@ +-- Crear tabla escrow_api_calls +CREATE TABLE escrow_api_calls ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + escrow_transaction_id UUID REFERENCES escrow_transactions(id) ON DELETE CASCADE, + endpoint TEXT NOT NULL, + method TEXT NOT NULL, + request_body JSONB, + http_status_code INTEGER, + response_body JSONB, + error_details JSONB, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + CONSTRAINT valid_http_status CHECK (http_status_code >= 100 AND http_status_code < 600), + CONSTRAINT valid_http_method CHECK (method IN ('GET', 'POST', 'PUT', 'DELETE', 'PATCH')) +); From 66585cd2c582660ad363c55ebecd59f3f4855ea9 Mon Sep 17 00:00:00 2001 From: Juan Diego Carballo <101369290+juandiegocv27@users.noreply.github.com> Date: Wed, 4 Dec 2024 22:43:33 -0600 Subject: [PATCH 2/2] feat: add bid tables (#41) * feat: add bid tables with triggers, functions, and indexes * fix: modify comments for better resolution * feat add bid tables into the metadata * fix: resolve merge conflict in tables.yaml and include public_bid_tables.yaml --- .../safetrust/tables/public_bid_tables.yaml | 25 ++++ .../databases/safetrust/tables/tables.yaml | 1 + .../1732865994413_create_bid_tables/down.sql | 21 ++++ .../1732865994413_create_bid_tables/up.sql | 112 ++++++++++++++++++ 4 files changed, 159 insertions(+) create mode 100644 metadata/databases/safetrust/tables/public_bid_tables.yaml create mode 100644 migrations/safetrust/1732865994413_create_bid_tables/down.sql create mode 100644 migrations/safetrust/1732865994413_create_bid_tables/up.sql diff --git a/metadata/databases/safetrust/tables/public_bid_tables.yaml b/metadata/databases/safetrust/tables/public_bid_tables.yaml new file mode 100644 index 0000000..e373413 --- /dev/null +++ b/metadata/databases/safetrust/tables/public_bid_tables.yaml @@ -0,0 +1,25 @@ +- table: + schema: public + name: bid_requests + configuration: + custom_root_fields: {} + custom_column_names: {} + array_relationships: + - name: status_histories + using: + foreign_key_constraint_on: + column: bid_request_id + table: + schema: public + name: bid_status_histories + +- table: + schema: public + name: bid_status_histories + configuration: + custom_root_fields: {} + custom_column_names: {} + object_relationships: + - name: bid_request + using: + foreign_key_constraint_on: bid_request_id diff --git a/metadata/databases/safetrust/tables/tables.yaml b/metadata/databases/safetrust/tables/tables.yaml index c5e7f8a..893044e 100644 --- a/metadata/databases/safetrust/tables/tables.yaml +++ b/metadata/databases/safetrust/tables/tables.yaml @@ -8,3 +8,4 @@ - "!include escrow_xdr_transactions.yaml" - "!include public_apartments.yaml" - "!include public_apartment_images.yaml" +- "!include public_bid_tables.yaml" diff --git a/migrations/safetrust/1732865994413_create_bid_tables/down.sql b/migrations/safetrust/1732865994413_create_bid_tables/down.sql new file mode 100644 index 0000000..e37e82e --- /dev/null +++ b/migrations/safetrust/1732865994413_create_bid_tables/down.sql @@ -0,0 +1,21 @@ +-- Drop triggers +DROP TRIGGER IF EXISTS check_tenant_active_bids ON bid_requests; +DROP TRIGGER IF EXISTS record_bid_status ON bid_requests; +DROP TRIGGER IF EXISTS update_bid_requests_updated_at ON bid_requests; + +-- Drop functions +DROP FUNCTION IF EXISTS check_active_bids(); +DROP FUNCTION IF EXISTS record_bid_status_change(); +DROP FUNCTION IF EXISTS update_updated_at_column(); + +-- Drop indexes +DROP INDEX IF EXISTS idx_bid_requests_apartment; +DROP INDEX IF EXISTS idx_bid_requests_tenant; +DROP INDEX IF EXISTS idx_bid_requests_status; +DROP INDEX IF EXISTS idx_bid_requests_dates; +DROP INDEX IF EXISTS idx_bid_histories_request; +DROP INDEX IF EXISTS idx_bid_histories_dates; + +-- Drop tables +DROP TABLE IF EXISTS bid_status_histories; +DROP TABLE IF EXISTS bid_requests; diff --git a/migrations/safetrust/1732865994413_create_bid_tables/up.sql b/migrations/safetrust/1732865994413_create_bid_tables/up.sql new file mode 100644 index 0000000..0451fa1 --- /dev/null +++ b/migrations/safetrust/1732865994413_create_bid_tables/up.sql @@ -0,0 +1,112 @@ +-- Function to automatically update updated_at column +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Main bid requests table +CREATE TABLE bid_requests ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + apartment_id UUID REFERENCES apartments(id) ON DELETE CASCADE, + tenant_id TEXT REFERENCES users(id) ON DELETE CASCADE, + current_status TEXT NOT NULL DEFAULT 'PENDING', + proposed_price DECIMAL(10,2) NOT NULL CHECK (proposed_price > 0), + desired_move_in TIMESTAMP WITH TIME ZONE NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + deleted_at TIMESTAMP WITH TIME ZONE, + CONSTRAINT valid_status CHECK ( + current_status IN ( + 'PENDING', + 'VIEWED', + 'APPROVED', + 'CONFIRMED', + 'ESCROW_FUNDED', + 'ESCROW_COMPLETED', + 'CANCELLED' + ) + ) +); + +-- Status history table to track all status changes +CREATE TABLE bid_status_histories ( + id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), + bid_request_id UUID REFERENCES bid_requests(id) ON DELETE CASCADE, + status TEXT NOT NULL, + notes TEXT, + changed_by TEXT REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Function to record status changes in history table +CREATE OR REPLACE FUNCTION record_bid_status_change() +RETURNS TRIGGER AS $$ +BEGIN + IF (TG_OP = 'INSERT') OR (OLD.current_status IS DISTINCT FROM NEW.current_status) THEN + INSERT INTO bid_status_histories ( + bid_request_id, + status, + changed_by + ) VALUES ( + NEW.id, + NEW.current_status, + current_setting('hasura.user', true)::text + ); + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Trigger to automatically record status changes +CREATE TRIGGER record_bid_status + AFTER INSERT OR UPDATE ON bid_requests + FOR EACH ROW + EXECUTE FUNCTION record_bid_status_change(); + +-- Indexes for query optimization +CREATE INDEX idx_bid_requests_apartment + ON bid_requests(apartment_id); +CREATE INDEX idx_bid_requests_tenant + ON bid_requests(tenant_id); +CREATE INDEX idx_bid_requests_status + ON bid_requests(current_status); +CREATE INDEX idx_bid_requests_dates + ON bid_requests(created_at, desired_move_in); +CREATE INDEX idx_bid_histories_request + ON bid_status_histories(bid_request_id); +CREATE INDEX idx_bid_histories_dates + ON bid_status_histories(created_at); + +-- Trigger to automatically update the updated_at timestamp +CREATE TRIGGER update_bid_requests_updated_at + BEFORE UPDATE ON bid_requests + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +-- Function to check for active bids by the same tenant +CREATE OR REPLACE FUNCTION check_active_bids() +RETURNS TRIGGER AS $$ +BEGIN + IF NEW.current_status IN ('PENDING', 'VIEWED', 'APPROVED') THEN + IF EXISTS ( + SELECT 1 FROM bid_requests + WHERE tenant_id = NEW.tenant_id + AND id != NEW.id + AND current_status IN ('PENDING', 'VIEWED', 'APPROVED') + AND deleted_at IS NULL + ) THEN + RAISE EXCEPTION 'Tenant already has an active bid'; + END IF; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Trigger to prevent multiple active bids from the same tenant +CREATE TRIGGER check_tenant_active_bids + BEFORE INSERT OR UPDATE ON bid_requests + FOR EACH ROW + EXECUTE FUNCTION check_active_bids(); \ No newline at end of file