Package com.tnsai.channels.api
Record Class ChannelScopedId
java.lang.Object
java.lang.Record
com.tnsai.channels.api.ChannelScopedId
- Record Components:
channelId- adapter id that produced the inbound message (e.g."telegram","cli"); never null, blank, or containing the':'separatorsenderId- platform-specific user identifier; never null or blank, but may contain colons
Strongly-typed identity for "this user, on this channel" — a value
type replacing the
channelId + ":" + senderId string concat
that used to live in every adapter and every consumer.
Why not just a string?
The string form has three independent failure modes:
- Type confusion. A method taking
String scopedIdaccepts any string — including a bare senderId, aconversationId, or an unrelated key. The compiler can't tell which is intended. - Separator collision. If
senderIdever contains the separator character (a colon, on platforms that useuser:fragmentids), naive string-split parses the wrong way. ChannelScopedId pins the contract: thechannelIdside may not contain the separator (validated in the compact constructor), and parsing always splits on the first colon. - No equals/hashCode contract. Two records with the same channel + sender are equal by record default; two raw strings depend on what the producer happened to write.
Wire format
toKey() renders the canonical disk / log / map-key form
as channelId + ":" + senderId. parse(String) is the
inverse. Round-tripping is total when the channelId obeys the
"no colon" rule the constructor enforces.
ChannelScopedId id = ChannelScopedId.of(message); // typed String key = id.toKey(); // "telegram:12345" ChannelScopedId parsed = ChannelScopedId.parse(key); // back to typed assert id.equals(parsed);
Senders with colons
A senderId may contain colons — some platforms
issue identifiers like workspace:user:fragment. parse(java.lang.String)
splits on the first colon only, so "telegram:workspace:user"
round-trips to channel "telegram" and sender
"workspace:user". The channel side never contains a colon
because the compact constructor rejects it.
- Since:
- 0.8.4
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final charCanonical separator betweenchannelIdandsenderId. -
Constructor Summary
ConstructorsConstructorDescriptionChannelScopedId(String channelId, String senderId) Creates an instance of aChannelScopedIdrecord class. -
Method Summary
Modifier and TypeMethodDescriptionReturns the value of thechannelIdrecord component.final booleanIndicates whether some other object is "equal to" this one.final inthashCode()Returns a hash code value for this object.static ChannelScopedIdof(UnifiedMessage message) static ChannelScopedIdParse a string previously produced bytoKey().senderId()Returns the value of thesenderIdrecord component.toKey()Canonical wire / log / map-key form:channelId + ":" + senderId.toString()Same string astoKey()— convenient for log lines (log.info("user {}", scopedId)) and string concat without forcing every call site to remember to calltoKey().
-
Field Details
-
SEPARATOR
public static final char SEPARATORCanonical separator betweenchannelIdandsenderId.- See Also:
-
-
Constructor Details
-
Method Details
-
of
Convenience factory: pullchannelId+senderIdout of an inboundUnifiedMessage. Equivalent tonew ChannelScopedId(msg.channelId(), msg.senderId())but reads at the call site like the intent ("scoped id of this message"). -
parse
Parse a string previously produced bytoKey(). Splits on the first colon so asenderIdwith internal colons survives the round trip.- Throws:
IllegalArgumentException- ifkeyis null, blank, contains no colon, or has a blank channel/sender side
-
toKey
Canonical wire / log / map-key form:channelId + ":" + senderId. Inverse ofparse(String). -
toString
Same string astoKey()— convenient for log lines (log.info("user {}", scopedId)) and string concat without forcing every call site to remember to calltoKey(). -
hashCode
public final int hashCode()Returns a hash code value for this object. The value is derived from the hash code of each of the record components. -
equals
Indicates whether some other object is "equal to" this one. The objects are equal if the other object is of the same class and if all the record components are equal. All components in this record class are compared withObjects::equals(Object,Object). -
channelId
Returns the value of thechannelIdrecord component.- Returns:
- the value of the
channelIdrecord component
-
senderId
Returns the value of thesenderIdrecord component.- Returns:
- the value of the
senderIdrecord component
-