Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.7.1] - 2026-02-11

### Fixed
- **`base_url` in local mode procedures**: `run_post_export_sql()` now accepts a `config` parameter from the caller instead of always creating a fresh `ExportConfig(load_from_env=True)`
- Prevents `"None"` appearing in curl command URLs when running in local mode without `.env` files
- Falls back to `dictionary_metadata` table in the database if `BASE_URL` is still None
- `substitute_parameters()` now skips substitution when a config value is `None` instead of converting to literal `"None"` string

## [1.7.0] - 2026-02-05

### Changed
Expand Down
5 changes: 4 additions & 1 deletion gooddata_export/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,11 @@ def run_enrich_command(args):
configure_logging(args.debug)

try:
# Load config for parameter substitution (procedures need base_url etc.)
config = ExportConfig(load_from_env=True)

# Run post-export processing
run_post_export_sql(args.db_path)
run_post_export_sql(args.db_path, config=config)

# Calculate duration
end_time = datetime.now()
Expand Down
8 changes: 6 additions & 2 deletions gooddata_export/export/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,14 @@ def export_all_metadata(
if config.INCLUDE_CHILD_WORKSPACES:
# Multi-workspace: enrich only parent workspace to avoid confusing duplicates
logger.debug("Multi-workspace mode: enriching parent workspace only")
run_post_export_sql(db_path, parent_workspace_id=config.WORKSPACE_ID)
run_post_export_sql(
db_path,
parent_workspace_id=config.WORKSPACE_ID,
config=config,
)
else:
# Single workspace: enrich all data (no filter needed)
run_post_export_sql(db_path)
run_post_export_sql(db_path, config=config)
except ExportError as e:
# Capture error but continue (database still usable without enrichment)
post_export_error = str(e)
Expand Down
56 changes: 50 additions & 6 deletions gooddata_export/post_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

from gooddata_export.common import ExportError
from gooddata_export.config import ExportConfig
from gooddata_export.db import connect_database
from gooddata_export.db import connect_database, database_connection

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -196,8 +196,15 @@ def substitute_parameters(sql_script, parameters, config):
config_key = param_template[2:-2].strip()
if hasattr(config, config_key):
value = getattr(config, config_key)
result = result.replace(f"{{{param_name}}}", str(value))
logger.debug(" Substituted {%s} with %s", param_name, value)
if value is None:
logger.warning(
" Config key %s is None, skipping substitution for {%s}",
config_key,
param_name,
)
else:
result = result.replace(f"{{{param_name}}}", str(value))
logger.debug(" Substituted {%s} with %s", param_name, value)
else:
logger.warning(
" Config key %s not found, skipping substitution", config_key
Expand Down Expand Up @@ -306,7 +313,34 @@ def ensure_columns_exist(cursor, table_name, required_columns):
)


def run_post_export_sql(db_path, parent_workspace_id: str | None = None) -> None:
def _read_metadata_value(db_path: str, key: str) -> str | None:
"""Read a single value from dictionary_metadata table.

Args:
db_path: Path to the SQLite database
key: Metadata key to look up

Returns:
str | None: The value if found, None otherwise
"""
try:
with database_connection(db_path) as conn:
cursor = conn.cursor()
cursor.execute(
"SELECT value FROM dictionary_metadata WHERE key = ?", (key,)
)
row = cursor.fetchone()
return row[0] if row and row[0] else None
except Exception as e:
logger.debug("Could not read metadata key '%s': %s", key, e)
return None


def run_post_export_sql(
db_path,
parent_workspace_id: str | None = None,
config: ExportConfig | None = None,
) -> None:
"""Run all post-export SQL operations on the database.

This is the main entry point for post-export processing.
Expand All @@ -317,6 +351,9 @@ def run_post_export_sql(db_path, parent_workspace_id: str | None = None) -> None
parent_workspace_id: Optional workspace ID to filter updates to.
When provided, UPDATE statements only affect rows for this workspace.
Used in multi-workspace exports to enrich only the parent workspace.
config: Optional ExportConfig instance. When provided, used for parameter
substitution in procedures. Falls back to ExportConfig(load_from_env=True)
if not provided, then to dictionary_metadata table for base_url.

Raises:
ExportError: If post-export processing fails
Expand All @@ -331,8 +368,15 @@ def run_post_export_sql(db_path, parent_workspace_id: str | None = None) -> None
yaml_config = load_post_export_config()
sql_dir = Path(__file__).parent / "sql"

# Load export config for parameter substitution
export_config = ExportConfig(load_from_env=True)
# Use provided config or fall back to env-loaded config
export_config = config or ExportConfig(load_from_env=True)

# If BASE_URL is still None (e.g. local mode without .env), read from DB
if not export_config.BASE_URL:
db_base_url = _read_metadata_value(db_path, "base_url")
if db_base_url:
export_config.BASE_URL = db_base_url
logger.debug("Loaded BASE_URL from database metadata: %s", db_base_url)

# Connect to database
conn = connect_database(db_path)
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "gooddata-export"
version = "1.7.0"
version = "1.7.1"
description = "Export GoodData workspace metadata to SQLite and CSV"
readme = "README.md"
license = {text = "MIT"}
Expand Down