Skip to content

Instantly share code, notes, and snippets.

@marz619
Last active November 18, 2022 13:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marz619/674d36bb90f1cb91a72486741c6d692c to your computer and use it in GitHub Desktop.
Save marz619/674d36bb90f1cb91a72486741c6d692c to your computer and use it in GitHub Desktop.
Convert a 15 character Salesforce Identier into an 18 character Identifier
# suffix characters (base 32, sort of)
SUFFIX_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
def id18(s: str, *, check_suffix: bool = False) -> str:
"""
Converts a 15 character Salesforce ID (s) to its 18 character version.
If the ID provided has length 18 and check_suffix is True, we will assert
that the suffix is correct!
>>> assert id18("a0BA0000000L2ZC") == "a0BA0000000L2ZCMA0"
>>> assert id18("AaaAABbbBBCccCC") == "AaaAABbbBBCccCCZZZ"
>>> assert id18("ABCDEFGHIJKLMNO") == "ABCDEFGHIJKLMNO555"
>>> assert id18("AAAAABBBBBCCCCC") == "AAAAABBBBBCCCCC555"
>>> assert id18("ABCDEABCDEABCDE") == "ABCDEABCDEABCDE555"
>>> assert id18("AAaAAbbBBBCcCCC") == "AAaAAbbBBBCcCCC123"
"""
if not isinstance(s, str):
raise TypeError("id must be type str")
if "" in (s, s.strip()):
raise ValueError("id must not be empty")
if len(s) not in (15, 18):
raise ValueError("id must have length 15, 18")
def suffix(p: str) -> str:
"""
Calculates the suffix for given a 15 character Salesforce ID
"""
return "".join([
SUFFIX_CHARS[sum(
1 << i
for i, c in enumerate(p[n:n+5])
if c.isupper()
)] for n in (0, 5, 10)
])
if len(s) == 18:
if check_suffix:
exp, act = s[15:], suffix(s[:15])
assert exp == act, f'id18: suffix mismatch for "{s}": "{exp}" != "{act}"'
return s
# length is 15; generate and return the id with the suffix
return f"{s}{suffix(s)}"
if __name__ == "__main__":
import doctest
doctest.testmod()
from typing import List
def id18(s: str, *, check_suffix: bool = False) -> str:
"""
Converts a 15 character Salesforce ID (s) to its 18 character version.
If the ID provided has length 18 and check_suffix is True, we will assert
that the suffix is correct!
>>> assert id18("a0BA0000000L2ZC") == "a0BA0000000L2ZCMA0"
>>> assert id18("AaaAABbbBBCccCC") == "AaaAABbbBBCccCCZZZ"
>>> assert id18("ABCDEFGHIJKLMNO") == "ABCDEFGHIJKLMNO555"
>>> assert id18("AAAAABBBBBCCCCC") == "AAAAABBBBBCCCCC555"
>>> assert id18("ABCDEABCDEABCDE") == "ABCDEABCDEABCDE555"
>>> assert id18("AAaAAbbBBBCcCCC") == "AAaAAbbBBBCcCCC123"
>>> id18("")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in id18
AssertionError: id18: id cannot be an empty string
>>> id18("AbCdEfGhIjKlMn")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in id18
AssertionError: id18: id must have length 15 or 18
>>> id18("AbCdEfGhIjKlMnOpQrS")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in id18
AssertionError: id18: id must have length 15 or 18
>>> id18("AAaAAbbBBBCcCCC ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in id18
AssertionError: id18: id cannot have trailing spaces
>>> id18(" AAaAAbbBBBCcCCC")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in id18
AssertionError: id18: id cannot have trailing spaces
>>> id18(" AAaAAbbBBBCcCCC ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in id18
AssertionError: id18: id cannot have trailing spaces
"""
assert isinstance(s, str), "id18: id must be type str"
assert len(s), "id18: id cannot be an empty string"
assert s.strip() == s, "id18: id cannot have trailing spaces"
assert len(s) in (15, 18), "id18: id must have length 15 or 18"
if len(s) == 18:
if check_suffix:
exp, act = s[15:], suffix(s[:15])
assert exp == act, f'id18: suffix mismatch for "{s}": "{exp}" != "{act}"'
return s
# length is 15; generate and return the ID with the suffix
return f"{s}{suffix(s)}"
def quintets(s: str) -> List[str]:
"""
>>> assert quintets("AAAAA") == ["AAAAA"]
>>> assert quintets("AAAAABBBBB") == ["AAAAA", "BBBBB"]
>>> assert quintets("AAAAABBBBBCCCCC") == ["AAAAA", "BBBBB", "CCCCC"]
>>> quintets("")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in quintets
AssertionError: quintets: argument cannot be an empty string
>>> quintets(" ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in quintets
AssertionError: quintets: argument cannot have trailing spaces
>>> quintets(" ABCD")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in quintets
AssertionError: quintets: argument cannot have trailing spaces
>>> quintets("ABCD ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in quintets
AssertionError: quintets: argument cannot have trailing spaces
>>> quintets(" ABCD ")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in quintets
AssertionError: quintets: argument cannot have trailing spaces
>>> quintets("ACBD")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in quintets
AssertionError: quintets: argument length must be divisible by 5
>>> quintets("ACBDEABCD")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in quintets
AssertionError: quintets: argument length must be divisible by 5
"""
assert isinstance(s, str), "quintets: argument must be type str"
assert len(s), "quintets: argument cannot be an empty string"
assert s.strip() == s, "quintets: argument cannot have trailing spaces"
assert len(s) and len(s) % 5 == 0, "quintets: argument length must be divisible by 5"
# generate the list of quintets
return list(s[n:n+5] for n in range(0, len(s), 5))
def suffix_char(i: int) -> str:
"""
>>> assert suffix_char(0) == "A"
>>> assert suffix_char(31) == "5"
>>> assert "".join(suffix_char(n) for n in range(0, 32)) == "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"
>>> suffix_char(-1)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in suffix_char
AssertionError: suffix_char: invalid index -1
>>> suffix_char(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in suffix_char
AssertionError: suffix_char: invalid index 32
>>> suffix_char(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in suffix_char
AssertionError: suffix_char: index must be type int
"""
assert isinstance(i, int), "suffix_char: index must be type int"
assert 0 <= i <= 31, f"suffix_char: invalid index {i}"
return "ABCDEFGHIJKLMNOPQRSTUVWXYZ012345"[i]
def suffix_index(s: str) -> int:
assert isinstance(s, str), "suffix_index: argument must be type str"
assert len(s), "suffix_index: argument cannot be an empty str"
assert s.strip() == s, "suffix_index: argument cannot have trailing spaces"
assert len(s) == 5, "suffix_index: argument must have length 5"
idx = 0
for i, c in enumerate(s):
if c.isupper():
idx += 1 << i
return idx
def suffix(s: str) -> str:
"""
>>> assert suffix("a0BA0000000L2ZC") == "MA0"
>>> assert suffix("AaaAABbbBBCccCC") == "ZZZ"
>>> assert suffix("ABCDEFGHIJKLMNO") == "555"
>>> assert suffix("AAAAABBBBBCCCCC") == "555"
>>> assert suffix("ABCDEABCDEABCDE") == "555"
>>> assert suffix("AAaAAbbBBBCcCCC") == "123"
"""
assert isinstance(s, str), "suffix: argument must be type str"
assert s.strip() == s, "suffix: argument cannot have trailing spaces"
assert len(s) == 15, "suffix: argument must have length 15"
return "".join(suffix_char(suffix_index(q)) for q in quintets(s))
if __name__ == "__main__":
import doctest
doctest.testmod()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment