Source code for sf_toolkit.formatting
"""Formatting helpers that perform quoting and escaping"""
import urllib.parse
from datetime import date, datetime, timezone
from string import Formatter
# https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_quotedstringescapes.htm
soql_escapes = str.maketrans(
{
"\\": "\\\\",
"'": "\\'",
'"': '\\"',
"\n": "\\n",
"\r": "\\r",
"\t": "\\t",
"\b": "\\b",
"\f": "\\f",
}
)
soql_like_escapes = str.maketrans(
{
"%": "\\%",
"_": "\\_",
}
)
SF_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S.%f%z"
[docs]
class SoqlFormatter(Formatter):
"""Custom formatter to apply quoting or the :literal format spec"""
[docs]
def format_field(self, value, format_spec):
if not format_spec:
return quote_soql_value(value)
if format_spec == "literal":
# literal: allows circumventing everything while still using
# the same format string
return value
if format_spec == "like":
# like: allows escaping substring used in LIKE expression
# does not quote
return str(value).translate(soql_escapes).translate(soql_like_escapes)
return super().format_field(value, format_spec)
[docs]
def format_soql(query, *args, **kwargs):
"""Insert values quoted for SOQL into a format string"""
return SoqlFormatter().vformat(query, args, kwargs)
# pylint: disable=too-many-return-statements
[docs]
def quote_soql_value(value):
"""Quote/escape either an individual value or a list of values
for a SOQL value expression"""
if isinstance(value, str):
return "'" + value.translate(soql_escapes) + "'"
if value is True:
return "TRUE"
if value is False:
return "FALSE"
if value is None:
return "NULL"
if isinstance(value, (int, float)):
return str(value)
if isinstance(value, (list, set, tuple)):
quoted_items = [quote_soql_value(member) for member in value]
return "(" + ",".join(quoted_items) + ")"
if isinstance(value, datetime):
# Salesforce spec requires a datetime literal
# that is not naive and without MS
value = value.replace(microsecond=0)
value = value.astimezone(tz=timezone.utc)
return value.isoformat()
if isinstance(value, date):
return value.isoformat()
raise ValueError("unquotable value type")
[docs]
def format_external_id(field, value):
"""Create an external ID string for use with get() or upsert()"""
return field + "/" + urllib.parse.quote(value, safe="")
[docs]
def format_datetime(value: datetime):
"""Get the provided datetime as a Salesforce-compliant formatted string"""
if value.tzinfo is None:
# just assume the current timezone if there is none
value = value.astimezone()
return value.strftime(SF_DATETIME_FORMAT)
[docs]
def parse_datetime(value: str):
"""Parse a datetime from a Salesforce datetime string value"""
return datetime.strptime(value, SF_DATETIME_FORMAT)