Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions Sources/AblyChat/ChatAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ internal final class ChatAPI: Sendable {
// (CHA-M3a) When a message is sent successfully, the caller shall receive a struct representing the Message in response (as if it were received via Realtime event).
internal func sendMessage(roomName: String, params: SendMessageParams) async throws(InternalError) -> Message {
guard let clientId = realtime.clientId else {
throw ARTErrorInfo.create(withCode: 40000, message: "Ensure your Realtime instance is initialized with a clientId.").toInternalError()
throw ARTErrorInfo(chatError: .clientIdRequired).toInternalError()
}

let endpoint = "\(apiVersionV3)/rooms/\(roomName)/messages"
Expand Down Expand Up @@ -106,7 +106,7 @@ internal final class ChatAPI: Sendable {
// (CHA-M8a) A client may update a message via the Chat REST API by calling the update method.
internal func updateMessage(roomName: String, with modifiedMessage: Message, description: String?, metadata: OperationMetadata?) async throws(InternalError) -> Message {
guard let clientID = realtime.clientId else {
throw ARTErrorInfo.create(withCode: 40000, message: "Ensure your Realtime instance is initialized with a clientId.").toInternalError()
throw ARTErrorInfo(chatError: .clientIdRequired).toInternalError()
}

let endpoint = "\(apiVersionV3)/rooms/\(roomName)/messages/\(modifiedMessage.serial)"
Expand Down Expand Up @@ -199,7 +199,7 @@ internal final class ChatAPI: Sendable {

// (CHA-MR4) Users should be able to send a reaction to a message via the `send` method of the `MessagesReactions` object
internal func sendReactionToMessage(_ messageSerial: String, roomName: String, params: SendMessageReactionParams) async throws(InternalError) -> MessageReactionResponse {
// (CHA-MR11a1) If the serial passed to this method is invalid: undefined, null, empty string, an error with code 40000 must be thrown.
// (CHA-MR4a1) If the serial passed to this method is invalid: undefined, null, empty string, an error with code 40000 must be thrown.
guard !messageSerial.isEmpty else {
throw ChatError.messageReactionInvalidMessageSerial.toInternalError()
}
Expand Down
27 changes: 10 additions & 17 deletions Sources/AblyChat/DefaultMessageReactions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,15 @@ internal final class DefaultMessageReactions: MessageReactions {
private let logger: InternalLogger
private let clientID: String
private let chatAPI: ChatAPI

private var defaultReaction: MessageReactionType
private let options: MessagesOptions

internal init(channel: any InternalRealtimeChannelProtocol, chatAPI: ChatAPI, roomName: String, options: MessagesOptions, clientID: String, logger: InternalLogger) {
self.channel = channel
self.chatAPI = chatAPI
self.roomName = roomName
self.logger = logger
self.clientID = clientID
defaultReaction = options.defaultMessageReactionType
self.options = options
}

// (CHA-MR4) Users should be able to send a reaction to a message via the `send` method of the `MessagesReactions` object
Expand All @@ -28,7 +27,7 @@ internal final class DefaultMessageReactions: MessageReactions {
}

let apiParams: ChatAPI.SendMessageReactionParams = .init(
type: params.type ?? defaultReaction,
type: params.type ?? options.defaultMessageReactionType,
name: params.name,
count: count
)
Expand All @@ -42,7 +41,7 @@ internal final class DefaultMessageReactions: MessageReactions {

// (CHA-MR11) Users should be able to delete a reaction from a message via the `delete` method of the `MessagesReactions` object
internal func delete(from messageSerial: String, params: DeleteMessageReactionParams) async throws(ARTErrorInfo) {
let reactionType = params.type ?? defaultReaction
let reactionType = params.type ?? options.defaultMessageReactionType
if reactionType != .unique, params.name == nil {
throw ARTErrorInfo(chatError: .unableDeleteReactionWithoutName(reactionType: reactionType.rawValue))
}
Expand Down Expand Up @@ -101,6 +100,11 @@ internal final class DefaultMessageReactions: MessageReactions {
@discardableResult
internal func subscribeRaw(_ callback: @escaping @MainActor @Sendable (MessageReactionRawEvent) -> Void) -> SubscriptionProtocol {
logger.log(message: "Subscribing to reaction events", level: .debug)
guard options.rawMessageReactions else {
// (CHA-MR7a) The attempt to subscribe to raw message reactions must throw an ErrorInfo with code 40000 and status code 400 if the room is not configured to support raw message reactions
// I'm replacing throwing with `fatalError` because it's a programmer error to call this method with invalid options.
fatalError("Room is not configured to support raw message reactions")
}

let eventListener = channel.annotations.subscribe { [clientID, logger] annotation in
logger.log(message: "Received reaction (message annotation): \(annotation)", level: .debug)
Expand All @@ -114,25 +118,14 @@ internal final class DefaultMessageReactions: MessageReactions {
return
}

var reactionName = annotation.name
if reactionName == nil {
if reactionEventType == .delete, reactionType == .unique {
// deletes of type unique are allowed to have no name
reactionName = ""
} else {
logger.log(message: "Received annotation without name: \(annotation)", level: .debug)
return
}
}

let annotationClientID = annotation.clientId ?? "" // CHA-MR7b3

let reactionEvent = MessageReactionRawEvent(
type: reactionEventType,
timestamp: annotation.timestamp,
reaction: MessageReaction(
type: reactionType,
name: reactionName ?? "", // CHA-MR7b3
name: annotation.name ?? "", // CHA-MR7b3
messageSerial: annotation.messageSerial,
count: annotation.count?.intValue ?? (annotation.action == .create && reactionType == .multiple ? 1 : nil),
clientID: annotationClientID,
Expand Down
14 changes: 12 additions & 2 deletions Sources/AblyChat/Errors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ public enum ErrorCode: Int {
case cannotApplyEventForDifferentMessage
case messageRejectedByBeforePublishRule
case messageRejectedByModeration
case clientIdRequired

internal var toNumericErrorCode: ErrorCode {
switch self {
Expand All @@ -86,6 +87,8 @@ public enum ErrorCode: Int {
.messageRejectedByModeration
case .messageRejectedByBeforePublishRule:
.messageRejectedByBeforePublishRule
case .clientIdRequired:
.badRequest
}
}

Expand All @@ -99,7 +102,8 @@ public enum ErrorCode: Int {
.roomIsReleased,
.roomReleasedBeforeOperationCompleted,
.unableDeleteReactionWithoutName,
.cannotApplyEventForDifferentMessage:
.cannotApplyEventForDifferentMessage,
.clientIdRequired:
400
case .messageRejectedByModeration,
.messageRejectedByBeforePublishRule:
Expand Down Expand Up @@ -170,6 +174,7 @@ internal enum ChatError {
case cannotApplyEventForDifferentMessage
case messageRejectedByBeforePublishRule
case messageRejectedByModeration
case clientIdRequired

internal var codeAndStatusCode: ErrorCodeAndStatusCode {
switch self {
Expand Down Expand Up @@ -202,6 +207,8 @@ internal enum ChatError {
.fixedStatusCode(.messageRejectedByBeforePublishRule)
case .messageRejectedByModeration:
.fixedStatusCode(.messageRejectedByModeration)
case .clientIdRequired:
.fixedStatusCode(.clientIdRequired)
}
}

Expand Down Expand Up @@ -256,6 +263,8 @@ internal enum ChatError {
"The message was rejected before publishing by a rule on the chat room."
case .messageRejectedByModeration:
"The message was rejected before publishing by a moderation rule on the chat room."
case .clientIdRequired:
"Ensure your Realtime instance is initialized with a clientId."
}
}

Expand All @@ -276,7 +285,8 @@ internal enum ChatError {
.cannotApplyEventForDifferentMessage,
.unableDeleteReactionWithoutName,
.messageRejectedByBeforePublishRule,
.messageRejectedByModeration:
.messageRejectedByModeration,
.clientIdRequired:
nil
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/AblyChat/Room.swift
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ internal class DefaultRoom: InternalRoom {
self.chatAPI = chatAPI

guard let clientId = realtime.clientId else {
throw ARTErrorInfo.create(withCode: 40000, message: "Ensure your Realtime instance is initialized with a clientId.").toInternalError()
throw ARTErrorInfo(chatError: .clientIdRequired).toInternalError()
}

internalChannel = Self.createChannel(roomName: name, roomOptions: options, realtime: realtime)
Expand Down
2 changes: 1 addition & 1 deletion Sources/BuildTool/BuildTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ struct SpecCoverage: AsyncParsableCommand {
// ** @(CHA-RS4b)@ @[Testable]@ Room status update events must contain the previous room status.
// (This `Testable` is a convention that’s being used only in the Chat spec)

let specPointLineRegex = /^\*+ @\((.*?)\)@( @\[Testable\]@ )?/
let specPointLineRegex = /^\s*\*+ @\((.*?)\)@( @\[Testable\]@ )?/

// swiftlint:disable:next force_try
guard let match = try! specPointLineRegex.firstMatch(in: specLine) else {
Expand Down
Loading