Repository: apache/airflow
File: airflow-core/src/airflow/api_fastapi/core_api/datamodels/connections.py (Python)

Code before:
    @field_validator("extra_fields", mode="after")
    @classmethod
    def redact_extra_fields(cls, v: Mapping | None):
        if v is None:
            return None

        # Check if extra_fields contains param spec structures (result of SerializedParam.dump())
        # which have "value" and "schema" keys, or simple dictionary structures
        has_param_spec_structure = any(
            isinstance(field_spec, dict) and "value" in field_spec and "schema" in field_spec
            for field_spec in v.values()
        )

        if has_param_spec_structure:
            redacted_extra_fields = {}
            for field_name, field_spec in v.items():
                if isinstance(field_spec, dict) and "value" in field_spec and "schema" in field_spec:
                    if should_hide_value_for_key(field_name) and field_spec.get("value") is not None:
                        # Mask only the value, preserve everything else including schema.type
                        redacted_extra_fields[field_name] = {**field_spec, "value": "***"}
                    else:
                        # Not sensitive or no value, keep as is
                        redacted_extra_fields[field_name] = field_spec
                else:
                    # Not a param spec structure, keep as is
                    redacted_extra_fields[field_name] = field_spec
            return redacted_extra_fields
        # For simple dictionary structures, use the standard redact function
        return redact(v)


# Request Models
class ConnectionBody(StrictBaseModel):
    """Connection Serializer for requests body."""

    connection_id: str = Field(serialization_alias="conn_id", max_length=200, pattern=r"^[\w.-]+$")
    conn_type: str
    description: str | None = Field(default=None)
    host: str | None = Field(default=None)
    login: str | None = Field(default=None)
    schema_: str | None = Field(None, alias="schema")
    port: int | None = Field(default=None)
    password: str | None = Field(default=None)
    extra: str | None = Field(default=None)
    team_name: str | None = Field(max_length=50, default=None)

    @field_validator("extra")
    @classmethod
    def validate_extra(cls, v: str | None) -> str | None:
        """
        Validate that `extra` field is a JSON-encoded Python dict.

Review comment (suggestion):
For the else case here, would it be better to add `redact` by default?

```suggestion
                    # Not a param spec structure, keep as is
                    redacted_extra_fields[field_name] = redact(field_spec)
```



Code after:
    @field_validator("extra_fields", mode="after")
    @classmethod
    def redact_extra_fields(cls, v: Mapping | None):
        if v is None:
            return None

        # Check if extra_fields contains param spec structures (result of SerializedParam.dump())
        # which have "value" and "schema" keys, or simple dictionary structures
        has_param_spec_structure = any(
            isinstance(field_spec, dict) and "value" in field_spec and "schema" in field_spec
            for field_spec in v.values()
        )

        if has_param_spec_structure:
            redacted_extra_fields: dict[str, Any] = {}
            for field_name, field_spec in v.items():
                if isinstance(field_spec, dict) and "value" in field_spec and "schema" in field_spec:
                    if should_hide_value_for_key(field_name) and field_spec.get("value") is not None:
                        # Mask only the value, preserve everything else including schema.type
                        redacted_extra_fields[field_name] = {**field_spec, "value": "***"}
                    else:
                        # Not sensitive or no value, keep as is
                        redacted_extra_fields[field_name] = field_spec
                else:
                    # Not a param spec structure, apply redact by default
                    redacted_extra_fields[field_name] = redact(field_spec)

            return redacted_extra_fields

        # For simple dictionary structures, use the standard redact function
        return redact(v)


# Request Models
class ConnectionBody(StrictBaseModel):
    """Connection Serializer for requests body."""

    connection_id: str = Field(serialization_alias="conn_id", max_length=200, pattern=r"^[\w.-]+$")
    conn_type: str
    description: str | None = Field(default=None)
    host: str | None = Field(default=None)
    login: str | None = Field(default=None)
    schema_: str | None = Field(None, alias="schema")
    port: int | None = Field(default=None)
    password: str | None = Field(default=None)
    extra: str | None = Field(default=None)
    team_name: str | None = Field(max_length=50, default=None)

    @field_validator("extra")
    @classmethod
    def validate_extra(cls, v: str | None) -> str | None: