blob: 51e4ab16ba7df73ce0dfc9997ef8a40549c241e5 [file]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/common.sh"
ensure_docker_prerequisites
load_environment
SOURCE_DB_CONTAINER="${SOURCE_DB_CONTAINER:-fineract-db-1}"
SOURCE_DB_HOST="${SOURCE_DB_HOST:-localhost}"
SOURCE_DB_PORT="${SOURCE_DB_HOST_PORT:-5432}"
log() { echo "[bootstrap-source] $*"; }
fail() { echo "[bootstrap-source] ERROR: $*" >&2; exit 1; }
run_sql() {
local sql="$1"
docker exec -i "${SOURCE_DB_CONTAINER}" \
env PGPASSWORD="${SOURCE_BOOTSTRAP_PASSWORD}" \
psql -v ON_ERROR_STOP=1 \
-U "${SOURCE_BOOTSTRAP_USER}" \
-d "${SOURCE_DB_NAME}" \
-c "${sql}"
}
run_sql_stdin() {
docker exec -i "${SOURCE_DB_CONTAINER}" \
env PGPASSWORD="${SOURCE_BOOTSTRAP_PASSWORD}" \
psql -v ON_ERROR_STOP=1 \
-U "${SOURCE_BOOTSTRAP_USER}" \
-d "${SOURCE_DB_NAME}"
}
log "Checking connection to '${SOURCE_DB_CONTAINER}'..."
docker exec "${SOURCE_DB_CONTAINER}" \
env PGPASSWORD="${SOURCE_BOOTSTRAP_PASSWORD}" \
pg_isready -U "${SOURCE_BOOTSTRAP_USER}" -d postgres \
|| fail "Cannot reach ${SOURCE_DB_CONTAINER}. Is it running?"
log "Connection OK"
log "Creating compatibility views in schema '${SOURCE_DB_SCHEMA}'..."
run_sql_stdin <<SQL
CREATE SCHEMA IF NOT EXISTS ${SOURCE_DB_SCHEMA};
-- m_office: no created_on_utc in real Fineract schema, use epoch fallback
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_office AS
SELECT
id, parent_id, hierarchy, external_id, name, opening_date,
'1970-01-01 00:00:00+00'::timestamptz AS created_on_utc,
'1970-01-01 00:00:00+00'::timestamptz AS last_modified_on_utc
FROM public.m_office;
-- m_currency: no audit columns in real Fineract schema
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_currency AS
SELECT
id, code, decimal_places, currency_multiplesof, display_symbol,
name, internationalized_name_code,
'1970-01-01 00:00:00+00'::timestamptz AS created_on_utc,
'1970-01-01 00:00:00+00'::timestamptz AS last_modified_on_utc
FROM public.m_currency;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_client AS
SELECT
id, account_no, external_id, status_enum, activation_date,
office_joining_date, office_id, staff_id, gender_cv_id,
date_of_birth, legal_form_enum, submittedon_date, updated_on,
created_on_utc, last_modified_on_utc
FROM public.m_client;
-- m_product_loan: no created_on_utc in real Fineract schema
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_product_loan AS
SELECT
id, short_name, currency_code, currency_digits, currency_multiplesof,
principal_amount, min_principal_amount, max_principal_amount,
arrearstolerance_amount, name, description,
nominal_interest_rate_per_period, annual_nominal_interest_rate,
repay_every, repayment_period_frequency_enum, number_of_repayments,
overdue_days_for_npa, start_date, close_date,
'1970-01-01 00:00:00+00'::timestamptz AS created_on_utc,
'1970-01-01 00:00:00+00'::timestamptz AS last_modified_on_utc
FROM public.m_product_loan;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_loan AS
SELECT
id, account_no, external_id, client_id, product_id,
loan_status_id, loan_type_enum, currency_code, currency_digits,
currency_multiplesof, principal_amount_proposed, principal_amount,
approved_principal, net_disbursal_amount,
annual_nominal_interest_rate, nominal_interest_rate_per_period,
interest_method_enum, interest_calculated_in_period_enum,
term_frequency, term_period_frequency_enum, repay_every,
repayment_period_frequency_enum, number_of_repayments,
amortization_method_enum, submittedon_date, approvedon_date,
expected_disbursedon_date, expected_firstrepaymenton_date,
disbursedon_date, expected_maturedon_date, maturedon_date,
principal_disbursed_derived, principal_repaid_derived,
principal_writtenoff_derived, principal_outstanding_derived,
interest_charged_derived, interest_repaid_derived,
interest_writtenoff_derived, interest_outstanding_derived,
fee_charges_outstanding_derived, penalty_charges_outstanding_derived,
total_expected_repayment_derived, total_repayment_derived,
total_writtenoff_derived, total_outstanding_derived,
loan_counter, is_npa,
created_on_utc, last_modified_on_utc
FROM public.m_loan;
-- m_loan_transaction: real schema has no submitted_on_date, use transaction_date
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_loan_transaction AS
SELECT
id, loan_id, office_id, is_reversed, transaction_type_enum,
transaction_date, amount, principal_portion_derived,
interest_portion_derived, fee_charges_portion_derived,
penalty_charges_portion_derived, outstanding_loan_balance_derived,
transaction_date AS submitted_on_date,
created_on_utc, last_modified_on_utc
FROM public.m_loan_transaction;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_delinquency_range AS
SELECT id, classification, min_age_days, max_age_days,
created_on_utc, last_modified_on_utc
FROM public.m_delinquency_range;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_delinquency_bucket AS
SELECT id, name, created_on_utc, last_modified_on_utc
FROM public.m_delinquency_bucket;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_delinquency_bucket_mappings AS
SELECT id, delinquency_range_id, delinquency_bucket_id,
created_on_utc, last_modified_on_utc
FROM public.m_delinquency_bucket_mappings;
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.m_loan_delinquency_tag_history AS
SELECT id, delinquency_range_id, loan_id, addedon_date, liftedon_date,
created_on_utc, last_modified_on_utc
FROM public.m_loan_delinquency_tag_history;
-- batch_job_execution: real schema uses timestamp without time zone
CREATE OR REPLACE VIEW ${SOURCE_DB_SCHEMA}.batch_job_execution AS
SELECT
job_execution_id,
status,
start_time ::timestamptz AS start_time,
end_time ::timestamptz AS end_time,
exit_code,
exit_message,
create_time ::timestamptz AS created_on_utc,
last_updated ::timestamptz AS last_modified_on_utc
FROM public.batch_job_execution;
SQL
log "Compatibility views created in schema '${SOURCE_DB_SCHEMA}'"
log "Creating replica user if not exists..."
run_sql "DO \$\$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = '${SOURCE_REPLICA_USER}') THEN
EXECUTE format('CREATE ROLE %I LOGIN PASSWORD %L', '${SOURCE_REPLICA_USER}', '${SOURCE_REPLICA_PASSWORD}');
END IF;
END
\$\$;"
log "Granting read access to '${SOURCE_REPLICA_USER}'..."
run_sql_stdin <<SQL
GRANT USAGE ON SCHEMA public TO ${SOURCE_REPLICA_USER};
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ${SOURCE_REPLICA_USER};
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO ${SOURCE_REPLICA_USER};
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO ${SOURCE_REPLICA_USER};
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT USAGE, SELECT ON SEQUENCES TO ${SOURCE_REPLICA_USER};
GRANT USAGE ON SCHEMA ${SOURCE_DB_SCHEMA} TO ${SOURCE_REPLICA_USER};
GRANT SELECT ON ALL TABLES IN SCHEMA ${SOURCE_DB_SCHEMA} TO ${SOURCE_REPLICA_USER};
SQL
log "Read access granted to '${SOURCE_REPLICA_USER}'"
log "=== Source bootstrap complete. You can now run the pipeline. ==="
log " bash scripts/run_pipeline.sh backfill"