Skip to content

graflo.object_storage.config

S3-compatible endpoint configuration (MinIO, AWS S3, etc.).

Used for TigerGraph bulk CSV staging and other boto3 S3 clients. Not a graph database target. Load from docker/minio/.env via :meth:MinioConfig.from_docker_env.

S3EndpointConfig is an alias for :class:MinioConfig (name reflects any S3 API).

S3EndpointConfig = MinioConfig module-attribute

MinioConfig

Bases: BaseModel

Credentials and endpoint for MinIO or any S3-compatible API (boto3).

Source code in graflo/object_storage/config.py
class MinioConfig(BaseModel):
    """Credentials and endpoint for MinIO or any S3-compatible API (boto3)."""

    model_config = ConfigDict(extra="forbid")

    endpoint_url: str = Field(
        description="Base URL for the S3 API, e.g. http://127.0.0.1:9000",
    )
    access_key: str = Field(
        description="S3 access key id (MinIO root user or IAM key)."
    )
    secret_key: str = Field(description="S3 secret access key.")
    bucket: str = Field(
        default="graflo-staging",
        description="Default bucket for staged bulk CSV objects.",
    )
    region: str = Field(
        default="us-east-1",
        description="AWS region string passed to boto3 (use us-east-1 for MinIO).",
    )
    loader_endpoint_url: str | None = Field(
        default=None,
        description=(
            "Optional S3 API URL for TigerGraph LOADING JOB (CREATE DATA_SOURCE). "
            "Set when TigerGraph runs in Docker and MinIO is on the host "
            "(e.g. http://172.17.0.1:9003). Boto3 uses endpoint_url above."
        ),
    )

    def to_s3_generalized_conn_config(self) -> "S3GeneralizedConnConfig":
        """Map to runtime provider config for :class:`~graflo.hq.connection_provider.S3GeneralizedConnConfig`."""
        from graflo.hq.connection_provider import S3GeneralizedConnConfig

        return S3GeneralizedConnConfig(
            bucket=self.bucket,
            region=self.region,
            endpoint_url=self.endpoint_url,
            aws_access_key_id=self.access_key,
            aws_secret_access_key=self.secret_key,
            loader_endpoint_url=self.loader_endpoint_url,
        )

    @classmethod
    def from_docker_env(cls, docker_dir: str | Path | None = None) -> MinioConfig:
        """Load from ``docker/minio/.env`` (same layout as other ``docker/*`` stacks)."""
        if docker_dir is None:
            docker_dir = (
                Path(__file__).resolve().parent.parent.parent / "docker" / "minio"
            )
        else:
            docker_dir = Path(docker_dir)

        env_file = docker_dir / ".env"
        if not env_file.exists():
            raise FileNotFoundError(f"Environment file not found: {env_file}")

        env_vars = parse_dotenv_file(env_file)
        data: dict[str, Any] = {}

        if raw_endpoint := env_vars.get("MINIO_ENDPOINT"):
            data["endpoint_url"] = raw_endpoint
        else:
            host = env_vars.get("MINIO_HOSTNAME", "127.0.0.1")
            port = env_vars.get("MINIO_API_PORT", "9000")
            protocol = env_vars.get("MINIO_PROTOCOL", "http")
            data["endpoint_url"] = f"{protocol}://{host}:{port}"

        access = env_vars.get("MINIO_ACCESS_KEY") or env_vars.get("MINIO_ROOT_USER")
        secret = env_vars.get("MINIO_SECRET_KEY") or env_vars.get("MINIO_ROOT_PASSWORD")
        if not access or not secret:
            raise ValueError(
                "MinIO docker .env must set MINIO_ROOT_USER/MINIO_ROOT_PASSWORD "
                "or MINIO_ACCESS_KEY/MINIO_SECRET_KEY"
            )
        data["access_key"] = access
        data["secret_key"] = secret

        if bucket := env_vars.get("MINIO_STAGING_BUCKET") or env_vars.get(
            "BULK_S3_BUCKET"
        ):
            data["bucket"] = bucket
        if region := env_vars.get("AWS_REGION"):
            data["region"] = region

        if loader_ep := env_vars.get("MINIO_LOADER_ENDPOINT") or env_vars.get(
            "MINIO_TIGERGRAPH_ENDPOINT"
        ):
            data["loader_endpoint_url"] = loader_ep.strip().strip('"').strip("'")

        return cls(**data)

from_docker_env(docker_dir=None) classmethod

Load from docker/minio/.env (same layout as other docker/* stacks).

Source code in graflo/object_storage/config.py
@classmethod
def from_docker_env(cls, docker_dir: str | Path | None = None) -> MinioConfig:
    """Load from ``docker/minio/.env`` (same layout as other ``docker/*`` stacks)."""
    if docker_dir is None:
        docker_dir = (
            Path(__file__).resolve().parent.parent.parent / "docker" / "minio"
        )
    else:
        docker_dir = Path(docker_dir)

    env_file = docker_dir / ".env"
    if not env_file.exists():
        raise FileNotFoundError(f"Environment file not found: {env_file}")

    env_vars = parse_dotenv_file(env_file)
    data: dict[str, Any] = {}

    if raw_endpoint := env_vars.get("MINIO_ENDPOINT"):
        data["endpoint_url"] = raw_endpoint
    else:
        host = env_vars.get("MINIO_HOSTNAME", "127.0.0.1")
        port = env_vars.get("MINIO_API_PORT", "9000")
        protocol = env_vars.get("MINIO_PROTOCOL", "http")
        data["endpoint_url"] = f"{protocol}://{host}:{port}"

    access = env_vars.get("MINIO_ACCESS_KEY") or env_vars.get("MINIO_ROOT_USER")
    secret = env_vars.get("MINIO_SECRET_KEY") or env_vars.get("MINIO_ROOT_PASSWORD")
    if not access or not secret:
        raise ValueError(
            "MinIO docker .env must set MINIO_ROOT_USER/MINIO_ROOT_PASSWORD "
            "or MINIO_ACCESS_KEY/MINIO_SECRET_KEY"
        )
    data["access_key"] = access
    data["secret_key"] = secret

    if bucket := env_vars.get("MINIO_STAGING_BUCKET") or env_vars.get(
        "BULK_S3_BUCKET"
    ):
        data["bucket"] = bucket
    if region := env_vars.get("AWS_REGION"):
        data["region"] = region

    if loader_ep := env_vars.get("MINIO_LOADER_ENDPOINT") or env_vars.get(
        "MINIO_TIGERGRAPH_ENDPOINT"
    ):
        data["loader_endpoint_url"] = loader_ep.strip().strip('"').strip("'")

    return cls(**data)

to_s3_generalized_conn_config()

Map to runtime provider config for :class:~graflo.hq.connection_provider.S3GeneralizedConnConfig.

Source code in graflo/object_storage/config.py
def to_s3_generalized_conn_config(self) -> "S3GeneralizedConnConfig":
    """Map to runtime provider config for :class:`~graflo.hq.connection_provider.S3GeneralizedConnConfig`."""
    from graflo.hq.connection_provider import S3GeneralizedConnConfig

    return S3GeneralizedConnConfig(
        bucket=self.bucket,
        region=self.region,
        endpoint_url=self.endpoint_url,
        aws_access_key_id=self.access_key,
        aws_secret_access_key=self.secret_key,
        loader_endpoint_url=self.loader_endpoint_url,
    )

parse_dotenv_file(env_file)

Parse a simple KEY=value .env file (no shell expansion).

Source code in graflo/object_storage/config.py
def parse_dotenv_file(env_file: Path) -> dict[str, str]:
    """Parse a simple ``KEY=value`` .env file (no shell expansion)."""
    env_vars: dict[str, str] = {}
    with open(env_file, encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if line and not line.startswith("#") and "=" in line:
                key, value = line.split("=", 1)
                env_vars[key.strip()] = value.strip().strip('"').strip("'")
    return env_vars