feat: Add hotel and webhook endpoint management
- Introduced Hotel and WebhookEndpoint models to manage hotel configurations and webhook settings. - Implemented sync_config_to_database function to synchronize hotel data from configuration to the database. - Added HotelService for accessing hotel configurations and managing customer data. - Created WebhookProcessor interface and specific processors for handling different webhook types (Wix form and generic). - Enhanced webhook processing logic to handle incoming requests and create/update reservations and customers. - Added logging for better traceability of operations related to hotels and webhooks.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
"""add_hotels_and_webhook_tables
|
||||
|
||||
Revision ID: e7ee03d8f430
|
||||
Revises: a1b2c3d4e5f6
|
||||
Create Date: 2025-11-25 11:55:18.872715
|
||||
|
||||
"""
|
||||
from typing import Sequence, Union
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision: str = 'e7ee03d8f430'
|
||||
down_revision: Union[str, Sequence[str], None] = 'a1b2c3d4e5f6'
|
||||
branch_labels: Union[str, Sequence[str], None] = None
|
||||
depends_on: Union[str, Sequence[str], None] = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
"""Upgrade schema."""
|
||||
# Create hotels table
|
||||
op.create_table(
|
||||
'hotels',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('hotel_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('hotel_name', sa.String(length=200), nullable=False),
|
||||
sa.Column('username', sa.String(length=100), nullable=False),
|
||||
sa.Column('password_hash', sa.String(length=200), nullable=False),
|
||||
sa.Column('meta_account_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('google_account_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('push_endpoint_url', sa.String(length=500), nullable=True),
|
||||
sa.Column('push_endpoint_token', sa.String(length=200), nullable=True),
|
||||
sa.Column('push_endpoint_username', sa.String(length=100), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('updated_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('is_active', sa.Boolean(), nullable=False, default=True),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_hotels_hotel_id'), 'hotels', ['hotel_id'], unique=True)
|
||||
op.create_index(op.f('ix_hotels_username'), 'hotels', ['username'], unique=True)
|
||||
op.create_index(op.f('ix_hotels_is_active'), 'hotels', ['is_active'], unique=False)
|
||||
|
||||
# Create webhook_endpoints table
|
||||
op.create_table(
|
||||
'webhook_endpoints',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('hotel_id', sa.String(length=50), nullable=False),
|
||||
sa.Column('webhook_secret', sa.String(length=64), nullable=False),
|
||||
sa.Column('webhook_type', sa.String(length=50), nullable=False),
|
||||
sa.Column('description', sa.String(length=200), nullable=True),
|
||||
sa.Column('is_enabled', sa.Boolean(), nullable=False, default=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.ForeignKeyConstraint(['hotel_id'], ['hotels.hotel_id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_webhook_endpoints_hotel_id'), 'webhook_endpoints', ['hotel_id'], unique=False)
|
||||
op.create_index(op.f('ix_webhook_endpoints_webhook_secret'), 'webhook_endpoints', ['webhook_secret'], unique=True)
|
||||
op.create_index('idx_webhook_endpoint_hotel_type', 'webhook_endpoints', ['hotel_id', 'webhook_type'], unique=False)
|
||||
|
||||
# Create webhook_requests table
|
||||
op.create_table(
|
||||
'webhook_requests',
|
||||
sa.Column('id', sa.Integer(), nullable=False),
|
||||
sa.Column('payload_hash', sa.String(length=64), nullable=False),
|
||||
sa.Column('webhook_endpoint_id', sa.Integer(), nullable=True),
|
||||
sa.Column('hotel_id', sa.String(length=50), nullable=True),
|
||||
sa.Column('status', sa.String(length=20), nullable=False, default='pending'),
|
||||
sa.Column('processing_started_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('processing_completed_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('retry_count', sa.Integer(), nullable=True, default=0),
|
||||
sa.Column('last_error', sa.String(length=2000), nullable=True),
|
||||
sa.Column('payload_json', sa.JSON(), nullable=True),
|
||||
sa.Column('payload_size_bytes', sa.Integer(), nullable=True),
|
||||
sa.Column('purged_at', sa.DateTime(timezone=True), nullable=True),
|
||||
sa.Column('created_at', sa.DateTime(timezone=True), nullable=False),
|
||||
sa.Column('source_ip', sa.String(length=45), nullable=True),
|
||||
sa.Column('user_agent', sa.String(length=500), nullable=True),
|
||||
sa.Column('created_customer_id', sa.Integer(), nullable=True),
|
||||
sa.Column('created_reservation_id', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['webhook_endpoint_id'], ['webhook_endpoints.id'], ),
|
||||
sa.ForeignKeyConstraint(['hotel_id'], ['hotels.hotel_id'], ),
|
||||
sa.ForeignKeyConstraint(['created_customer_id'], ['customers.id'], ),
|
||||
sa.ForeignKeyConstraint(['created_reservation_id'], ['reservations.id'], ),
|
||||
sa.PrimaryKeyConstraint('id')
|
||||
)
|
||||
op.create_index(op.f('ix_webhook_requests_payload_hash'), 'webhook_requests', ['payload_hash'], unique=True)
|
||||
op.create_index(op.f('ix_webhook_requests_webhook_endpoint_id'), 'webhook_requests', ['webhook_endpoint_id'], unique=False)
|
||||
op.create_index(op.f('ix_webhook_requests_hotel_id'), 'webhook_requests', ['hotel_id'], unique=False)
|
||||
op.create_index(op.f('ix_webhook_requests_status'), 'webhook_requests', ['status'], unique=False)
|
||||
op.create_index(op.f('ix_webhook_requests_created_at'), 'webhook_requests', ['created_at'], unique=False)
|
||||
op.create_index('idx_webhook_status_created', 'webhook_requests', ['status', 'created_at'], unique=False)
|
||||
op.create_index('idx_webhook_hotel_created', 'webhook_requests', ['hotel_id', 'created_at'], unique=False)
|
||||
op.create_index('idx_webhook_purge_candidate', 'webhook_requests', ['status', 'purged_at', 'created_at'], unique=False)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
"""Downgrade schema."""
|
||||
# Drop tables in reverse order (respecting foreign key constraints)
|
||||
op.drop_index('idx_webhook_purge_candidate', table_name='webhook_requests')
|
||||
op.drop_index('idx_webhook_hotel_created', table_name='webhook_requests')
|
||||
op.drop_index('idx_webhook_status_created', table_name='webhook_requests')
|
||||
op.drop_index(op.f('ix_webhook_requests_created_at'), table_name='webhook_requests')
|
||||
op.drop_index(op.f('ix_webhook_requests_status'), table_name='webhook_requests')
|
||||
op.drop_index(op.f('ix_webhook_requests_hotel_id'), table_name='webhook_requests')
|
||||
op.drop_index(op.f('ix_webhook_requests_webhook_endpoint_id'), table_name='webhook_requests')
|
||||
op.drop_index(op.f('ix_webhook_requests_payload_hash'), table_name='webhook_requests')
|
||||
op.drop_table('webhook_requests')
|
||||
|
||||
op.drop_index('idx_webhook_endpoint_hotel_type', table_name='webhook_endpoints')
|
||||
op.drop_index(op.f('ix_webhook_endpoints_webhook_secret'), table_name='webhook_endpoints')
|
||||
op.drop_index(op.f('ix_webhook_endpoints_hotel_id'), table_name='webhook_endpoints')
|
||||
op.drop_table('webhook_endpoints')
|
||||
|
||||
op.drop_index(op.f('ix_hotels_is_active'), table_name='hotels')
|
||||
op.drop_index(op.f('ix_hotels_username'), table_name='hotels')
|
||||
op.drop_index(op.f('ix_hotels_hotel_id'), table_name='hotels')
|
||||
op.drop_table('hotels')
|
||||
Reference in New Issue
Block a user