From 6f9ee3e9b63d238e7d7156f64e828e57c4885cc6 Mon Sep 17 00:00:00 2001 From: Johanna Larsson Date: Wed, 29 Jul 2026 08:33:16 +0100 Subject: [PATCH] Adds record key validation Just a little module with a regex, some docs, and a test. All from the spec --- lib/latch/record_key.ex | 22 ++++++++++++++++++++++ test/latch/record_key_test.exs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 lib/latch/record_key.ex create mode 100644 test/latch/record_key_test.exs diff --git a/lib/latch/record_key.ex b/lib/latch/record_key.ex new file mode 100644 index 0000000..d7eef49 --- /dev/null +++ b/lib/latch/record_key.ex @@ -0,0 +1,22 @@ +defmodule Latch.RecordKey do + @moduledoc """ + Record keys have a defined set of allowed shapes: TID, NSID, `literal:`, and `any`. + + https://atproto.com/specs/record-key + """ + + @regex ~r/^[A-Za-z0-9._:~-]{1,512}$/ + + @doc """ + Validates record key syntax according to the spec. + + From the spec: + * the allowed characters are alphanumeric (A-Za-z0-9), period, dash, underscore, colon, or tilde (.-_:~) + * must have at least 1 and at most 512 characters + * the specific record key values . and .. are not allowed + """ + @spec valid?(String.t()) :: boolean() + def valid?(key) when is_binary(key) do + key not in [".", ".."] and String.match?(key, @regex) + end +end diff --git a/test/latch/record_key_test.exs b/test/latch/record_key_test.exs new file mode 100644 index 0000000..2752ee3 --- /dev/null +++ b/test/latch/record_key_test.exs @@ -0,0 +1,30 @@ +defmodule Latch.RecordKeyTest do + use ExUnit.Case, async: true + + alias Latch.RecordKey + + describe "valid?/1" do + test "validates" do + # Examples taken from https://atproto.com/specs/record-key#usage-and-implementation-guidelines + assert RecordKey.valid?("3jui7kd54zh2y") + assert RecordKey.valid?("self") + assert RecordKey.valid?("example.com") + assert RecordKey.valid?("~1.2-3_") + assert RecordKey.valid?("dHJ1ZQ") + assert RecordKey.valid?("pre:fix") + assert RecordKey.valid?("_") + + refute RecordKey.valid?("alpha/beta") + refute RecordKey.valid?(".") + refute RecordKey.valid?("..") + refute RecordKey.valid?("#extra") + refute RecordKey.valid?("@handle") + refute RecordKey.valid?("any space") + refute RecordKey.valid?("any+space") + refute RecordKey.valid?("number[3]") + refute RecordKey.valid?("number(3)") + refute RecordKey.valid?("\"quote\"") + refute RecordKey.valid?("dHJ1ZQ==") + end + end +end -- 2.51.2