117 lines
4.7 KiB
Bash
Executable File
117 lines
4.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Validate the contracts-v1 PostgreSQL migration without touching any schema
|
|
# other than th_hotel_booking. This script is safe by default: it refuses a
|
|
# schema containing objects outside the booking_contract_* namespace.
|
|
set -euo pipefail
|
|
|
|
readonly BOOKING_SCHEMA='th_hotel_booking'
|
|
readonly MIGRATION_FILES=(
|
|
'server/src/main/resources/db/booking-postgresql/migration/V1__booking_contracts_v1.sql'
|
|
'server/src/main/resources/db/booking-postgresql/migration/V2__booking_sender_mapping_direction.sql'
|
|
)
|
|
|
|
database_url="${BOOKING_PG_VERIFY_URL:-${TH_HOTEL_BOOKING_PG_URL:-}}"
|
|
if [[ -z "${database_url}" ]]; then
|
|
echo 'BOOKING_PG_VERIFY_URL (or TH_HOTEL_BOOKING_PG_URL) is required.' >&2
|
|
exit 64
|
|
fi
|
|
if [[ "${BOOKING_PG_VERIFY_SCHEMA:-${BOOKING_SCHEMA}}" != "${BOOKING_SCHEMA}" ]]; then
|
|
echo "Only ${BOOKING_SCHEMA} may be verified by this script." >&2
|
|
exit 64
|
|
fi
|
|
for migration_file in "${MIGRATION_FILES[@]}"; do
|
|
if [[ ! -f "${migration_file}" ]]; then
|
|
echo "Canonical booking migration was not found: ${migration_file}" >&2
|
|
exit 66
|
|
fi
|
|
done
|
|
if ! command -v psql >/dev/null 2>&1; then
|
|
echo 'psql is required to run PostgreSQL migration verification.' >&2
|
|
exit 69
|
|
fi
|
|
|
|
psql_query() {
|
|
psql -X -qAt -v ON_ERROR_STOP=1 -d "${database_url}" -c "$1"
|
|
}
|
|
|
|
schema_exists="$(psql_query "SELECT EXISTS (SELECT 1 FROM pg_namespace WHERE nspname = '${BOOKING_SCHEMA}');")"
|
|
if [[ "${schema_exists}" == 't' ]]; then
|
|
unexpected_objects="$(psql_query "
|
|
SELECT count(*)
|
|
FROM pg_class relation
|
|
JOIN pg_namespace namespace ON namespace.oid = relation.relnamespace
|
|
WHERE namespace.nspname = '${BOOKING_SCHEMA}'
|
|
AND relation.relkind IN ('r', 'p', 'S', 'v', 'm')
|
|
AND relation.relname NOT LIKE 'booking_contract_%'
|
|
AND relation.relname <> 'flyway_schema_history';")"
|
|
if [[ "${unexpected_objects}" != '0' ]]; then
|
|
echo "Refusing to use ${BOOKING_SCHEMA}: it contains ${unexpected_objects} non-contract object(s)." >&2
|
|
exit 65
|
|
fi
|
|
fi
|
|
|
|
# Count user objects outside the project schema. The migration is allowed to
|
|
# create only th_hotel_booking objects; a count change is a hard failure.
|
|
other_objects_before="$(psql_query "
|
|
SELECT count(*)
|
|
FROM pg_class relation
|
|
JOIN pg_namespace namespace ON namespace.oid = relation.relnamespace
|
|
WHERE namespace.nspname NOT IN ('pg_catalog', 'information_schema', '${BOOKING_SCHEMA}')
|
|
AND namespace.nspname NOT LIKE 'pg_toast%'
|
|
AND relation.relkind IN ('r', 'p', 'S', 'v', 'm');")"
|
|
|
|
psql -X -q --single-transaction -v ON_ERROR_STOP=1 -d "${database_url}" \
|
|
-f "${MIGRATION_FILES[0]}" \
|
|
-f "${MIGRATION_FILES[1]}"
|
|
|
|
other_objects_after="$(psql_query "
|
|
SELECT count(*)
|
|
FROM pg_class relation
|
|
JOIN pg_namespace namespace ON namespace.oid = relation.relnamespace
|
|
WHERE namespace.nspname NOT IN ('pg_catalog', 'information_schema', '${BOOKING_SCHEMA}')
|
|
AND namespace.nspname NOT LIKE 'pg_toast%'
|
|
AND relation.relkind IN ('r', 'p', 'S', 'v', 'm');")"
|
|
if [[ "${other_objects_before}" != "${other_objects_after}" ]]; then
|
|
echo 'PostgreSQL migration verification detected a non-booking schema object change.' >&2
|
|
exit 70
|
|
fi
|
|
|
|
required_tables=(
|
|
booking_contract_source_message
|
|
booking_contract_source_attachment
|
|
booking_contract_processing_run
|
|
booking_contract_processing_attempt
|
|
booking_contract_evidence_reference
|
|
booking_contract_artifact
|
|
booking_contract_parsed_fact
|
|
booking_contract_candidate_target
|
|
booking_contract_confirmation_action
|
|
booking_contract_channel_profile
|
|
booking_contract_sender_mapping
|
|
booking_contract_rate_code_mapping
|
|
)
|
|
for table_name in "${required_tables[@]}"; do
|
|
present="$(psql_query "SELECT to_regclass('${BOOKING_SCHEMA}.${table_name}') IS NOT NULL;")"
|
|
if [[ "${present}" != 't' ]]; then
|
|
echo "Missing required booking table: ${BOOKING_SCHEMA}.${table_name}" >&2
|
|
exit 71
|
|
fi
|
|
done
|
|
|
|
cross_schema_fk_count="$(psql_query "
|
|
SELECT count(*)
|
|
FROM pg_constraint constraint
|
|
JOIN pg_class source_relation ON source_relation.oid = constraint.conrelid
|
|
JOIN pg_namespace source_namespace ON source_namespace.oid = source_relation.relnamespace
|
|
JOIN pg_class target_relation ON target_relation.oid = constraint.confrelid
|
|
JOIN pg_namespace target_namespace ON target_namespace.oid = target_relation.relnamespace
|
|
WHERE constraint.contype = 'f'
|
|
AND (source_namespace.nspname = '${BOOKING_SCHEMA}' OR target_namespace.nspname = '${BOOKING_SCHEMA}')
|
|
AND source_namespace.nspname <> target_namespace.nspname;")"
|
|
if [[ "${cross_schema_fk_count}" != '0' ]]; then
|
|
echo 'Booking PostgreSQL migration created or found a cross-schema foreign key.' >&2
|
|
exit 72
|
|
fi
|
|
|
|
echo "Booking PostgreSQL migration verified in ${BOOKING_SCHEMA}; other schema object count unchanged (${other_objects_after})."
|