diff --git a/Sources/PubNub/APIs/File+PubNub.swift b/Sources/PubNub/APIs/File+PubNub.swift index 17ce8ef1..177f2ded 100644 --- a/Sources/PubNub/APIs/File+PubNub.swift +++ b/Sources/PubNub/APIs/File+PubNub.swift @@ -80,6 +80,8 @@ public extension PubNub { struct PublishFileRequest { /// The optional message that will be include alongside the File information public var additionalMessage: JSONCodable? + /// Custom message type + public var customMessageType: String? = nil /// If true the published message is stored in history. public var store: Bool? /// Set a per message time to live in storage. @@ -98,12 +100,14 @@ public extension PubNub { /// - customRequestConfig: Custom configuration overrides for this request public init( additionalMessage: JSONCodable? = nil, + customMessageType: String? = nil, store: Bool? = nil, ttl: Int? = nil, meta: JSONCodable? = nil, customRequestConfig: RequestConfiguration = RequestConfiguration() ) { self.additionalMessage = additionalMessage + self.customMessageType = customMessageType self.store = store self.ttl = ttl self.meta = meta @@ -222,7 +226,7 @@ public extension PubNub { let router = PublishRouter( .file( - message: fileMessage, + message: fileMessage, customMessageType: request.customMessageType, shouldStore: request.store, ttl: request.ttl, meta: request.meta?.codableValue ), configuration: configuration diff --git a/Sources/PubNub/Models/PubNubMessage.swift b/Sources/PubNub/Models/PubNubMessage.swift index dcf6c92c..ad7baaff 100644 --- a/Sources/PubNub/Models/PubNubMessage.swift +++ b/Sources/PubNub/Models/PubNubMessage.swift @@ -16,7 +16,6 @@ public enum PubNubMessageType: Int, Codable, Hashable { case object = 2 case messageAction = 3 case file = 4 - case unknown = 999 } @@ -38,6 +37,8 @@ public protocol PubNubMessage { var metadata: JSONCodable? { get set } /// The type of message that was received var messageType: PubNubMessageType { get set } + /// A user-provided custom message type + var customMessageType: String? { get set } /// An error (if any) occured while getting this message var error: PubNubError? { get set } @@ -76,6 +77,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { public var subscription: String? public var published: Timetoken public var messageType: PubNubMessageType + public var customMessageType: String? public var error: PubNubError? var concretePayload: AnyJSON @@ -112,6 +114,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { subscription: other.subscription, published: other.published, metadata: other.metadata?.codableValue, + type: other.customMessageType, messageType: other.messageType, error: other.error ) @@ -126,6 +129,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { subscription: subscribe.subscription, published: subscribe.publishTimetoken.timetoken, metadata: subscribe.metadata, + type: subscribe.customMessageType, messageType: subscribe.messageType.asPubNubMessageType, error: subscribe.error ) @@ -146,7 +150,8 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { subscription: nil, published: history.timetoken, metadata: history.meta, - messageType: history.messageType ?? .unknown, + type: history.customMessageType, + messageType: history.messageType?.asPubNubMessageType ?? .unknown, error: history.error ) } @@ -159,6 +164,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { subscription: String?, published: Timetoken, metadata: AnyJSON?, + type: String? = nil, messageType: PubNubMessageType = .unknown, error: PubNubError? = nil ) { @@ -170,6 +176,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { self.published = published self.concreteMetadata = metadata self.messageType = messageType + self.customMessageType = type self.error = error } @@ -184,6 +191,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { try container.encode(self.published, forKey: .published) try container.encodeIfPresent(self.concreteMetadata, forKey: .concreteMetadata) try container.encode(self.messageType, forKey: .messageType) + try container.encode(self.customMessageType, forKey: .customMessageType) } enum CodingKeys: CodingKey { @@ -195,6 +203,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { case published case concreteMetadata case messageType + case customMessageType } public init(from decoder: Decoder) throws { @@ -208,6 +217,7 @@ public struct PubNubMessageBase: PubNubMessage, Codable, Hashable { self.published = try container.decode(Timetoken.self, forKey: .published) self.concreteMetadata = try container.decodeIfPresent(AnyJSON.self, forKey: .concreteMetadata) self.messageType = try container.decode(PubNubMessageType.self, forKey: .messageType) + self.customMessageType = try container.decodeIfPresent(String.self, forKey: .customMessageType) } } diff --git a/Sources/PubNub/Networking/HTTPRouter.swift b/Sources/PubNub/Networking/HTTPRouter.swift index 7b70d043..c03c8b6d 100644 --- a/Sources/PubNub/Networking/HTTPRouter.swift +++ b/Sources/PubNub/Networking/HTTPRouter.swift @@ -104,6 +104,7 @@ enum QueryKey: String { case remove case add case type + case customMessageType = "custom_message_type" case start case end case channel @@ -111,6 +112,7 @@ enum QueryKey: String { case max case includeMeta = "include_meta" case includeMessageType = "include_message_type" + case includeCustomMessageType = "include_custom_message_type" case includeUUID = "include_uuid" case timetoken case channelsTimetoken diff --git a/Sources/PubNub/Networking/Routers/HistoryRouter.swift b/Sources/PubNub/Networking/Routers/HistoryRouter.swift index cabafc7c..90edd916 100644 --- a/Sources/PubNub/Networking/Routers/HistoryRouter.swift +++ b/Sources/PubNub/Networking/Routers/HistoryRouter.swift @@ -17,11 +17,11 @@ struct HistoryRouter: HTTPRouter { enum Endpoint: CustomStringConvertible { case fetch( channels: [String], max: Int?, start: Timetoken?, end: Timetoken?, - includeMeta: Bool, includeMessageType: Bool, includeUUID: Bool + includeMeta: Bool, includeMessageType: Bool, includeCustomMessageType: Bool, includeUUID: Bool ) case fetchWithActions( channel: String, max: Int?, start: Timetoken?, end: Timetoken?, - includeMeta: Bool, includeMessageType: Bool, includeUUID: Bool + includeMeta: Bool, includeMessageType: Bool, includeCustomMessageType: Bool, includeUUID: Bool ) case delete(channel: String, start: Timetoken?, end: Timetoken?) case messageCounts(channels: [String], timetoken: Timetoken?, channelsTimetoken: [Timetoken]?) @@ -41,9 +41,9 @@ struct HistoryRouter: HTTPRouter { var firstChannel: String? { switch self { - case let .fetchWithActions(channel, _, _, _, _, _, _): + case let .fetchWithActions(channel, _, _, _, _, _, _, _): return channel - case let .fetch(channels, _, _, _, _, _, _): + case let .fetch(channels, _, _, _, _, _, _, _): return channels.first case let .delete(channel, _, _): return channel @@ -75,9 +75,9 @@ struct HistoryRouter: HTTPRouter { let path: String switch endpoint { - case let .fetchWithActions(channel, _, _, _, _, _, _): + case let .fetchWithActions(channel, _, _, _, _, _, _, _): path = "/v3/history-with-actions/sub-key/\(subscribeKey)/channel/\(channel)" - case let .fetch(channels, _, _, _, _, _, _): + case let .fetch(channels, _, _, _, _, _, _, _): path = "/v3/history/sub-key/\(subscribeKey)/channel/\(channels.csvString.urlEncodeSlash)" case let .delete(channel, _, _): path = "/v3/history/sub-key/\(subscribeKey)/channel/\(channel.urlEncodeSlash)" @@ -91,27 +91,28 @@ struct HistoryRouter: HTTPRouter { var query = defaultQueryItems switch endpoint { - case let .fetchWithActions(_, max, start, end, includeMeta, includeMessageType, includeUUID): + case let .fetchWithActions(_, max, start, end, includeMeta, includeMessageType, includeCustomMessageType, includeUUID): query.appendIfPresent(key: .max, value: max?.description) query.appendIfPresent(key: .start, value: start?.description) query.appendIfPresent(key: .end, value: end?.description) query.appendIfPresent(key: .includeMeta, value: includeMeta.description) query.appendIfPresent(key: .includeMessageType, value: includeMessageType.description) + query.appendIfPresent(key: .includeCustomMessageType, value: includeCustomMessageType.description) query.appendIfPresent(key: .includeUUID, value: includeUUID.description) - case let .fetch(_, max, start, end, includeMeta, includeMessageType, includeUUID): + case let .fetch(_, max, start, end, includeMeta, includeMessageType, includeCustomMessageType, includeUUID): query.appendIfPresent(key: .max, value: max?.description) query.appendIfPresent(key: .start, value: start?.description) query.appendIfPresent(key: .end, value: end?.description) query.appendIfPresent(key: .includeMeta, value: includeMeta.description) query.appendIfPresent(key: .includeMessageType, value: includeMessageType.description) + query.appendIfPresent(key: .includeCustomMessageType, value: includeCustomMessageType.description) query.appendIfPresent(key: .includeUUID, value: includeUUID.description) case let .delete(_, startTimetoken, endTimetoken): query.appendIfPresent(key: .start, value: startTimetoken?.description) query.appendIfPresent(key: .end, value: endTimetoken?.description) case let .messageCounts(_, timetoken, channelsTimetoken): query.appendIfPresent(key: .timetoken, value: timetoken?.description) - query.appendIfPresent(key: .channelsTimetoken, - value: channelsTimetoken?.map { $0.description }.csvString) + query.appendIfPresent(key: .channelsTimetoken, value: channelsTimetoken?.map { $0.description }.csvString) } return .success(query) @@ -129,9 +130,9 @@ struct HistoryRouter: HTTPRouter { // Validated var validationErrorDetail: String? { switch endpoint { - case let .fetchWithActions(channel, _, _, _, _, _, _): + case let .fetchWithActions(channel, _, _, _, _, _, _, _): return isInvalidForReason((channel.isEmpty, ErrorDescription.emptyChannelString)) - case let .fetch(channels, _, _, _, _, _, _): + case let .fetch(channels, _, _, _, _, _, _, _): return isInvalidForReason((channels.isEmpty, ErrorDescription.emptyChannelArray)) case let .delete(channel, _, _): return isInvalidForReason((channel.isEmpty, ErrorDescription.emptyChannelString)) @@ -152,16 +153,15 @@ struct MessageHistoryResponseDecoder: ResponseDecoder { func decode(response: EndpointResponse) -> Result, Error> { do { // Version3 - let payload = try Constant.jsonDecoder.decode(MessageHistoryResponse.self, from: response.payload) - let decodedResponse = EndpointResponse(router: response.router, - request: response.request, - response: response.response, - data: response.data, - payload: payload) - - // Attempt to decode message response - - return .success(decodedResponse) + return .success( + EndpointResponse( + router: response.router, + request: response.request, + response: response.response, + data: response.data, + payload: try Constant.jsonDecoder.decode(MessageHistoryResponse.self, from: response.payload) + ) + ) } catch { return .failure(PubNubError(.jsonDataDecodingFailure, response: response, error: error)) } @@ -191,6 +191,7 @@ struct MessageHistoryResponseDecoder: ResponseDecoder { meta: message.meta, uuid: message.uuid, messageType: message.messageType, + customMessageType: message.customMessageType, error: nil ) case .failure(let error): @@ -200,6 +201,7 @@ struct MessageHistoryResponseDecoder: ResponseDecoder { meta: message.meta, uuid: message.uuid, messageType: message.messageType, + customMessageType: message.customMessageType, error: error ) PubNub.log.warn("History message failed to decrypt due to \(error)") @@ -211,6 +213,7 @@ struct MessageHistoryResponseDecoder: ResponseDecoder { meta: message.meta, uuid: message.uuid, messageType: message.messageType, + customMessageType: message.customMessageType, error: PubNubError( .decryptionFailure, additional: ["Cannot decrypt message due to invalid Base-64 input"] @@ -218,20 +221,23 @@ struct MessageHistoryResponseDecoder: ResponseDecoder { ) } } - + return messages } // Replace previous payload with decrypted one - let decryptedPayload = MessageHistoryResponse(status: response.payload.status, - error: response.payload.error, - errorMessage: response.payload.errorMessage, - channels: channels) - let decryptedResponse = EndpointResponse(router: response.router, - request: response.request, - response: response.response, - data: response.data, - payload: decryptedPayload) + let decryptedResponse = EndpointResponse( + router: response.router, + request: response.request, + response: response.response, + data: response.data, + payload: MessageHistoryResponse( + status: response.payload.status, + error: response.payload.error, + errorMessage: response.payload.errorMessage, + channels: channels + ) + ) return .success(decryptedResponse) } } @@ -299,12 +305,14 @@ struct MessageHistoryMessagePayload: Codable { typealias ActionType = String typealias ActionValue = String typealias RawMessageAction = [ActionType: [ActionValue: [MessageHistoryMessageAction]]] + typealias LegacyPubNubMessageType = SubscribeMessagePayload.Action let message: AnyJSON let timetoken: Timetoken let meta: AnyJSON? let uuid: String? - let messageType: PubNubMessageType? + let messageType: LegacyPubNubMessageType? + let customMessageType: String? let actions: RawMessageAction let error: PubNubError? @@ -313,7 +321,8 @@ struct MessageHistoryMessagePayload: Codable { timetoken: Timetoken = 0, meta: JSONCodable? = nil, uuid: String?, - messageType: PubNubMessageType?, + messageType: LegacyPubNubMessageType?, + customMessageType: String? = nil, actions: RawMessageAction = [:], error: PubNubError? ) { @@ -321,6 +330,7 @@ struct MessageHistoryMessagePayload: Codable { self.timetoken = timetoken self.uuid = uuid self.messageType = messageType + self.customMessageType = customMessageType self.meta = meta?.codableValue self.actions = actions self.error = error @@ -332,6 +342,7 @@ struct MessageHistoryMessagePayload: Codable { case meta case uuid case messageType = "message_type" + case customMessageType = "custom_message_type" case actions } @@ -341,9 +352,10 @@ struct MessageHistoryMessagePayload: Codable { message = try container.decode(AnyJSON.self, forKey: .message) meta = try container.decodeIfPresent(AnyJSON.self, forKey: .meta) uuid = try container.decodeIfPresent(String.self, forKey: .uuid) - messageType = try container.decodeIfPresent(PubNubMessageType.self, forKey: .messageType) timetoken = Timetoken(try container.decode(String.self, forKey: .timetoken)) ?? 0 actions = try container.decodeIfPresent(RawMessageAction.self, forKey: .actions) ?? [:] + messageType = try container.decodeIfPresent(LegacyPubNubMessageType.self, forKey: .messageType) ?? .message + customMessageType = try container.decodeIfPresent(String.self, forKey: .customMessageType) error = nil } @@ -354,8 +366,9 @@ struct MessageHistoryMessagePayload: Codable { try container.encode(timetoken.description, forKey: .timetoken) try container.encodeIfPresent(meta, forKey: .meta) try container.encodeIfPresent(uuid, forKey: .uuid) - try container.encodeIfPresent(messageType, forKey: .messageType) try container.encode(actions, forKey: .actions) + try container.encodeIfPresent(messageType, forKey: .messageType) + try container.encodeIfPresent(customMessageType, forKey: .customMessageType) } } diff --git a/Sources/PubNub/Networking/Routers/PublishRouter.swift b/Sources/PubNub/Networking/Routers/PublishRouter.swift index b7246b75..daee6710 100644 --- a/Sources/PubNub/Networking/Routers/PublishRouter.swift +++ b/Sources/PubNub/Networking/Routers/PublishRouter.swift @@ -15,11 +15,11 @@ import Foundation struct PublishRouter: HTTPRouter { // Nested Endpoint enum Endpoint: CustomStringConvertible { - case publish(message: AnyJSON, channel: String, shouldStore: Bool?, ttl: Int?, meta: AnyJSON?) - case compressedPublish(message: AnyJSON, channel: String, shouldStore: Bool?, ttl: Int?, meta: AnyJSON?) + case publish(message: AnyJSON, channel: String, customMessageType: String?, shouldStore: Bool?, ttl: Int?, meta: AnyJSON?) + case compressedPublish(message: AnyJSON, channel: String, customMessageType: String?, shouldStore: Bool?, ttl: Int?, meta: AnyJSON?) case fire(message: AnyJSON, channel: String, meta: AnyJSON?) - case signal(message: AnyJSON, channel: String) - case file(message: FilePublishPayload, shouldStore: Bool?, ttl: Int?, meta: AnyJSON?) + case signal(message: AnyJSON, channel: String, customMessageType: String?) + case file(message: FilePublishPayload, customMessageType: String?, shouldStore: Bool?, ttl: Int?, meta: AnyJSON?) var description: String { switch self { @@ -57,20 +57,16 @@ struct PublishRouter: HTTPRouter { var path: Result { switch endpoint { - case let .publish(message, channel, _, _, _): - return append(message: message, - to: "/publish/\(publishKey)/\(subscribeKey)/0/\(channel.urlEncodeSlash)/0/") + case let .publish(message, channel, _, _, _, _): + return append(message: message, to: "/publish/\(publishKey)/\(subscribeKey)/0/\(channel.urlEncodeSlash)/0/") case let .fire(message, channel, _): - return append(message: message, - to: "/publish/\(publishKey)/\(subscribeKey)/0/\(channel.urlEncodeSlash)/0/") - case let .compressedPublish(_, channel, _, _, _): + return append(message: message, to: "/publish/\(publishKey)/\(subscribeKey)/0/\(channel.urlEncodeSlash)/0/") + case let .compressedPublish(_, channel, _, _, _, _): return .success("/publish/\(publishKey)/\(subscribeKey)/0/\(channel.urlEncodeSlash)/0") - case let .signal(message, channel): - return append(message: message, - to: "/signal/\(publishKey)/\(subscribeKey)/0/\(channel.urlEncodeSlash)/0/") - case let .file(message, _, _, _): - return append(message: message, - to: "/v1/files/publish-file/\(publishKey)/\(subscribeKey)/0/\(message.channel.urlEncodeSlash)/0/") + case let .signal(message, channel, _): + return append(message: message, to: "/signal/\(publishKey)/\(subscribeKey)/0/\(channel.urlEncodeSlash)/0/") + case let .file(message, _, _, _, _): + return append(message: message, to: "/v1/files/publish-file/\(publishKey)/\(subscribeKey)/0/\(message.channel.urlEncodeSlash)/0/") } } @@ -88,24 +84,29 @@ struct PublishRouter: HTTPRouter { var query = defaultQueryItems switch endpoint { - case let .publish(_, _, shouldStore, ttl, meta): - return parsePublish(query: &query, store: shouldStore, ttl: ttl, meta: meta) - case let .compressedPublish(_, _, shouldStore, ttl, meta): - return parsePublish(query: &query, store: shouldStore, ttl: ttl, meta: meta) + case let .publish(_, _, customMessageType, shouldStore, ttl, meta): + return parsePublish(query: &query, customMessageType: customMessageType, store: shouldStore, ttl: ttl, meta: meta) + case let .compressedPublish(_, _, customMessageType, shouldStore, ttl, meta): + return parsePublish(query: &query, customMessageType: customMessageType, store: shouldStore, ttl: ttl, meta: meta) case let .fire(_, _, meta): - return parsePublish(query: &query, store: false, ttl: 0, meta: meta) - case .signal: - break - case let .file(_, shouldStore, ttl, meta): - return parsePublish(query: &query, store: shouldStore, ttl: ttl, meta: meta) + return parsePublish(query: &query, customMessageType: nil, store: false, ttl: 0, meta: meta) + case let .signal(_, _, customMessageType): + return parsePublish(query: &query, customMessageType: customMessageType, store: nil, ttl: nil, meta: nil) + case let .file(_, customMessageType, shouldStore, ttl, meta): + return parsePublish(query: &query, customMessageType: customMessageType, store: shouldStore, ttl: ttl, meta: meta) } - - return .success(query) } - func parsePublish(query: inout [URLQueryItem], store: Bool?, ttl: Int?, meta: AnyJSON?) -> QueryResult { + func parsePublish( + query: inout [URLQueryItem], + customMessageType: String?, + store: Bool?, + ttl: Int?, + meta: AnyJSON? + ) -> QueryResult { query.appendIfPresent(key: .store, value: store?.stringNumber) query.appendIfPresent(key: .ttl, value: ttl?.description) + query.appendIfPresent(key: .customMessageType, value: customMessageType) if let meta = meta, !meta.isEmpty { return meta.jsonStringifyResult.map { json -> [URLQueryItem] in @@ -127,7 +128,7 @@ struct PublishRouter: HTTPRouter { var body: Result { switch endpoint { - case let .compressedPublish(message, _, _, _, _): + case let .compressedPublish(message, _, _, _, _, _): if let cryptoModule = configuration.cryptoModule { return message.jsonStringifyResult.flatMap { cryptoModule.encrypt(string: $0) @@ -152,12 +153,12 @@ struct PublishRouter: HTTPRouter { var validationErrorDetail: String? { switch endpoint { - case let .publish(message, channel, _, _, _): + case let .publish(message, channel, _, _, _, _): return isInvalidForReason( (message.isEmpty, ErrorDescription.emptyMessagePayload), (channel.isEmpty, ErrorDescription.emptyChannelString) ) - case let .compressedPublish(message, channel, _, _, _): + case let .compressedPublish(message, channel, _, _, _, _): return isInvalidForReason( (message.isEmpty, ErrorDescription.emptyMessagePayload), (channel.isEmpty, ErrorDescription.emptyChannelString) @@ -167,12 +168,12 @@ struct PublishRouter: HTTPRouter { (message.isEmpty, ErrorDescription.emptyMessagePayload), (channel.isEmpty, ErrorDescription.emptyChannelString) ) - case let .signal(message, channel): + case let .signal(message, channel, _): return isInvalidForReason( (message.isEmpty, ErrorDescription.emptyMessagePayload), (channel.isEmpty, ErrorDescription.emptyChannelString) ) - case let .file(message, _, _, _): + case let .file(message, _, _, _, _): return message.validationErrorDetail } } diff --git a/Sources/PubNub/Networking/Routers/SubscribeRouter.swift b/Sources/PubNub/Networking/Routers/SubscribeRouter.swift index 0f863c14..dc09e2de 100644 --- a/Sources/PubNub/Networking/Routers/SubscribeRouter.swift +++ b/Sources/PubNub/Networking/Routers/SubscribeRouter.swift @@ -139,8 +139,14 @@ struct SubscribeDecoder: ResponseDecoder { if let timetokenResponse = try? Constant.jsonDecoder.decode( Payload.self, from: truncatedData ).cursor { - return .failure(PubNubError(.jsonDataDecodingFailure, response: response, error: error, - affected: [.subscribe(timetokenResponse)])) + return .failure( + PubNubError( + .jsonDataDecodingFailure, + response: response, + error: error, + affected: [.subscribe(timetokenResponse)] + ) + ) } } @@ -276,6 +282,7 @@ public struct SubscribeMessagePayload: Codable, Hashable { public let subscription: String? public let channel: String public let messageType: Action + public var customMessageType: String? public var payload: AnyJSON public let flags: Int public let publisher: String? @@ -291,6 +298,7 @@ public struct SubscribeMessagePayload: Codable, Hashable { case channel = "c" case payload = "d" case messageType = "e" + case customMessageType = "cmt" case flags = "f" case publisher = "i" case subscribeKey = "k" @@ -308,7 +316,7 @@ public struct SubscribeMessagePayload: Codable, Hashable { /// Presence Event type /// - warning: This is a client-side type and will be encoded as nil case presence = 99 - + var asPubNubMessageType: PubNubMessageType { switch self { case .message: @@ -327,41 +335,11 @@ public struct SubscribeMessagePayload: Codable, Hashable { } } - init( - shard: String, - subscription: String?, - channel: String, - messageType: Action, - payload: AnyJSON, - flags: Int, - publisher: String?, - subscribeKey: String, - originTimetoken: SubscribeCursor?, - publishTimetoken: SubscribeCursor, - meta: AnyJSON?, - error: PubNubError? - ) { - self.shard = shard - self.subscription = subscription - self.channel = channel - self.messageType = messageType - self.payload = payload - self.flags = flags - self.publisher = publisher - self.subscribeKey = subscribeKey - self.originTimetoken = originTimetoken - self.publishTimetoken = publishTimetoken - self.metadata = meta - self.error = error - } - public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) shard = try container.decode(String.self, forKey: .shard) - subscription = try container - .decodeIfPresent(String.self, forKey: .subscription)? - .trimmingPresenceChannelSuffix + subscription = try container.decodeIfPresent(String.self, forKey: .subscription)?.trimmingPresenceChannelSuffix payload = try container.decode(AnyJSON.self, forKey: .payload) flags = try container.decode(Int.self, forKey: .flags) publisher = try container.decodeIfPresent(String.self, forKey: .publisher) @@ -369,11 +347,12 @@ public struct SubscribeMessagePayload: Codable, Hashable { originTimetoken = try container.decodeIfPresent(SubscribeCursor.self, forKey: .originTimetoken) publishTimetoken = try container.decode(SubscribeCursor.self, forKey: .publishTimetoken) metadata = try container.decodeIfPresent(AnyJSON.self, forKey: .meta) + customMessageType = try container.decodeIfPresent(String.self, forKey: .customMessageType) - let messageType = try container.decodeIfPresent(Int.self, forKey: .messageType) + let pubNubMessageType = try container.decodeIfPresent(Int.self, forKey: .messageType) let fullChannel = try container.decode(String.self, forKey: .channel) - if let messageType = messageType, let action = Action(rawValue: messageType) { + if let pubNubMessageType = pubNubMessageType, let action = Action(rawValue: pubNubMessageType) { self.messageType = action } else { // If channel endswith -pnpres we assume it's a presence event @@ -406,6 +385,8 @@ public struct SubscribeMessagePayload: Codable, Hashable { if messageType != .presence { try container.encode(messageType, forKey: .messageType) } + + try container.encode(customMessageType, forKey: .customMessageType) } // swiftlint:disable:next file_length } diff --git a/Sources/PubNub/PubNub.swift b/Sources/PubNub/PubNub.swift index 300f07f4..167e28b9 100644 --- a/Sources/PubNub/PubNub.swift +++ b/Sources/PubNub/PubNub.swift @@ -205,8 +205,9 @@ public extension PubNub { /// 4. If `storeTTL` is not specified, then expiration of the message defaults back to the expiry value for the key. /// /// - Parameters: - /// - channel: The destination of the message - /// - message: The message to publish + /// - channel: The destination of the message. + /// - message: The message to publish. + /// - customMessageType: Custom message type. /// - shouldStore: If true the published message is stored in history. /// - storeTTL: Set a per message time to live in storage. /// - meta: Publish extra metadata with the request. @@ -218,6 +219,7 @@ public extension PubNub { func publish( channel: String, message: JSONCodable, + customMessageType: String? = nil, shouldStore: Bool? = nil, storeTTL: Int? = nil, meta: JSONCodable? = nil, @@ -231,6 +233,7 @@ public extension PubNub { .compressedPublish( message: message.codableValue, channel: channel, + customMessageType: customMessageType, shouldStore: shouldStore, ttl: storeTTL, meta: meta?.codableValue @@ -242,6 +245,7 @@ public extension PubNub { .publish( message: message.codableValue, channel: channel, + customMessageType: customMessageType, shouldStore: shouldStore, ttl: storeTTL, meta: meta?.codableValue @@ -305,6 +309,7 @@ public extension PubNub { /// - Parameters: /// - channel: The destination of the message /// - message: The message to publish + /// - customMessageType: Custom signal type. /// - custom: Custom configuration overrides for this request /// - completion: The async `Result` of the method call /// - **Success**: The `Timetoken` of the published Message @@ -312,12 +317,13 @@ public extension PubNub { func signal( channel: String, message: JSONCodable, + customMessageType: String? = nil, custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result) -> Void)? ) { route( PublishRouter( - .signal(message: message.codableValue, channel: channel), + .signal(message: message.codableValue, channel: channel, customMessageType: customMessageType), configuration: requestConfig.customConfiguration ?? configuration ), requestOperator: configuration.automaticRetry?.retryOperator(for: .messageSend), @@ -1059,6 +1065,7 @@ public extension PubNub { /// - includeMeta: If `true` the meta properties of messages will be included in the response /// - includeUUID: If `true` the UUID of the message publisher will be included with each message in the response /// - includeMessageType: If `true` the message type will be included with each message + /// - includeType: If `true` the user-provided custom message type will be included with each message /// - page: The paging object used for pagination /// - custom: Custom configuration overrides for this request /// - completion: The async `Result` of the method call @@ -1066,8 +1073,11 @@ public extension PubNub { /// - **Failure**: An `Error` describing the failure func fetchMessageHistory( for channels: [String], - includeActions: Bool = false, includeMeta: Bool = false, - includeUUID: Bool = true, includeMessageType: Bool = true, + includeActions: Bool = false, + includeMeta: Bool = false, + includeUUID: Bool = true, + includeMessageType: Bool = true, + includeCustomMessageType: Bool = true, page: PubNubBoundedPage? = PubNubBoundedPageBase(), custom requestConfig: RequestConfiguration = RequestConfiguration(), completion: ((Result<(messagesByChannel: [String: [PubNubMessage]], next: PubNubBoundedPage?), Error>) -> Void)? @@ -1080,7 +1090,7 @@ public extension PubNub { .fetchWithActions( channel: channels.first ?? "", max: page?.limit ?? 25, start: page?.start, end: page?.end, - includeMeta: includeMeta, includeMessageType: includeMessageType, + includeMeta: includeMeta, includeMessageType: includeMessageType, includeCustomMessageType: includeCustomMessageType, includeUUID: includeUUID ), configuration: requestConfig.customConfiguration ?? configuration @@ -1090,7 +1100,7 @@ public extension PubNub { .fetch( channels: channels, max: page?.limit ?? 100, start: page?.start, end: page?.end, - includeMeta: includeMeta, includeMessageType: includeMessageType, + includeMeta: includeMeta, includeMessageType: includeMessageType, includeCustomMessageType: includeCustomMessageType, includeUUID: includeUUID ), configuration: requestConfig.customConfiguration ?? configuration @@ -1100,7 +1110,7 @@ public extension PubNub { .fetch( channels: channels, max: page?.limit ?? 25, start: page?.start, end: page?.end, - includeMeta: includeMeta, includeMessageType: includeMessageType, + includeMeta: includeMeta, includeMessageType: includeMessageType, includeCustomMessageType: includeCustomMessageType, includeUUID: includeUUID ), configuration: requestConfig.customConfiguration ?? configuration diff --git a/Tests/PubNubContractTest/PubNubContractTestCase.swift b/Tests/PubNubContractTest/PubNubContractTestCase.swift index bec9fba2..963f09a6 100644 --- a/Tests/PubNubContractTest/PubNubContractTestCase.swift +++ b/Tests/PubNubContractTest/PubNubContractTestCase.swift @@ -18,10 +18,10 @@ let defaultSubscribeKey = "demo-36" let defaultPublishKey = "demo-36" @objc public class PubNubContractTestCase: XCTestCase { - fileprivate var listener: SubscriptionListener! public var messageReceivedHandler: ((PubNubMessage, [PubNubMessage]) -> Void)? + public var fileReceivedHandler: ((PubNubFileEvent, [PubNubFileEvent]) -> Void)? public var statusReceivedHandler: ((SubscriptionListener.StatusEvent, [SubscriptionListener.StatusEvent]) -> Void)? public var presenceChangeReceivedHandler: ((PubNubPresenceChange, [PubNubPresenceChange]) -> Void)? @@ -29,11 +29,12 @@ let defaultPublishKey = "demo-36" fileprivate static var _receivedStatuses: [SubscriptionListener.StatusEvent] = [] fileprivate static var _receivedMessages: [PubNubMessage] = [] fileprivate static var _receivedPresenceChanges: [PubNubPresenceChange] = [] - + fileprivate static var _receivedFiles: [PubNubFileEvent] = [] fileprivate static var _currentScenario: CCIScenarioDefinition? fileprivate static var _apiCallResults: [Any] = [] - fileprivate static var _currentConfiguration = PubNubContractTestCase._defaultConfiguration + fileprivate static var currentClient: PubNub? + fileprivate static var _defaultConfiguration: PubNubConfiguration { PubNubConfiguration( publishKey: defaultPublishKey, @@ -44,8 +45,6 @@ let defaultPublishKey = "demo-36" supressLeaveEvents: true ) } - - fileprivate static var currentClient: PubNub? public var configuration: PubNubConfiguration { PubNubContractTestCase._currentConfiguration } @@ -74,6 +73,11 @@ let defaultPublishKey = "demo-36" set { PubNubContractTestCase._receivedMessages = newValue } } + public var receivedFiles: [PubNubFileEvent] { + get { PubNubContractTestCase._receivedFiles } + set { PubNubContractTestCase._receivedFiles = newValue } + } + public var receivedPresenceChanges: [PubNubPresenceChange] { get { PubNubContractTestCase._receivedPresenceChanges } set { PubNubContractTestCase._receivedPresenceChanges = newValue } @@ -97,9 +101,14 @@ let defaultPublishKey = "demo-36" } PubNubContractTestCase._currentConfiguration = configuration } - + func createPubNubClient() -> PubNub { - PubNub(configuration: configuration) + // In the unit test target only, URLSession with a background configuration fails to create a URLSessionUploadTask. + // Therefore, it is replaced with a standard configuration: https://developer.apple.com/forums/thread/725625 + PubNub( + configuration: configuration, + fileSession: URLSession(configuration: .default, delegate: FileSessionManager(), delegateQueue: .main) + ) } public func startCucumberHookEventsListening() { @@ -125,6 +134,8 @@ let defaultPublishKey = "demo-36" receivedMessages.removeAll() receivedPresenceChanges.removeAll() apiCallResults.removeAll() + receivedFiles.removeAll() + listener = nil } @objc public func setup() { @@ -178,7 +189,7 @@ let defaultPublishKey = "demo-36" XCTAssertFalse(result is Error, "Last API call shouldn't fail.") } - Then("I receive error response") { _, _ in + Then("I receive (an )?error response") { _, _ in let lastResult = self.lastResult() XCTAssertNotNil(lastResult, "There is no API calls results.") @@ -298,16 +309,34 @@ let defaultPublishKey = "demo-36" } } } - + listener.didReceiveMessage = { [weak self] message in guard let strongSelf = self else { return } - strongSelf.receivedMessages.append(message) + strongSelf.receivedMessages.append(message as! PubNubMessageBase) + + if let handler = strongSelf.messageReceivedHandler { + handler(message, strongSelf.receivedMessages) + } + } + + listener.didReceiveSignal = { [weak self] message in + guard let strongSelf = self else { return } + strongSelf.receivedMessages.append(message as! PubNubMessageBase) if let handler = strongSelf.messageReceivedHandler { handler(message, strongSelf.receivedMessages) } } + listener.didReceiveFileUpload = { [weak self] file in + guard let strongSelf = self else { return } + strongSelf.receivedFiles.append(file) + + if let handler = strongSelf.fileReceivedHandler { + handler(file, strongSelf.receivedFiles) + } + } + listener.didReceivePresence = { [weak self] presenceChange in guard let strongSelf = self else { return } strongSelf.receivedPresenceChanges.append(presenceChange) @@ -319,7 +348,7 @@ let defaultPublishKey = "demo-36" client.add(listener) client.subscribe(to: channels, and: groups, at: timetoken, withPresence: presence) - + wait(for: [subscribeStatusExpect], timeout: 10.0) } @@ -343,8 +372,26 @@ let defaultPublishKey = "demo-36" } } - // MARK: - Presence - + public func waitForFiles(_: PubNub, count: Int) -> [PubNubFileEvent]? { + if receivedFiles.count < count { + let subscribeFileExpect = expectation(description: "Subscribe files") + subscribeFileExpect.assertForOverFulfill = false + fileReceivedHandler = { _, files in + if files.count >= count { + subscribeFileExpect.fulfill() + } + } + + wait(for: [subscribeFileExpect], timeout: 30.0) + } + + if receivedFiles.count > count { + return Array(receivedFiles[.. 0 ? receivedFiles : nil + } + } + @discardableResult public func waitForPresenceChanges(_: PubNub, count: Int) -> [PubNubPresenceChange]? { if receivedPresenceChanges.count < count { diff --git a/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift index 8de81dfe..ce85fea4 100644 --- a/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Files/PubNubFilesContractTestSteps.swift @@ -104,5 +104,31 @@ public class PubNubFilesContractTestSteps: PubNubContractTestCase { self.wait(for: [sendFileExpect], timeout: 60.0) } + + When("^I send a file with '(.+)' space id and '(.+)' type$") { args, _ in + let spaceId = args?.first ?? String() + let type = args?.last ?? String() + + let sendFileExpect = self.expectation(description: "Send file Response") + + guard let data = "test file data".data(using: .utf8) else { + XCTAssert(false, "Unable prepare file data") + return + } + + let publishFileRequest = PubNub.PublishFileRequest(customMessageType: type) + + self.client.send(.data(data, contentType: nil), channel: "test", remoteFilename: "name.txt", publishRequest: publishFileRequest) { result in + switch result { + case let .success(sendResults): + self.handleResult(result: sendResults) + case let .failure(error): + self.handleResult(result: error) + } + sendFileExpect.fulfill() + } + + self.wait(for: [sendFileExpect], timeout: 60.0) + } } } diff --git a/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift b/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift index e0721ee4..65dc8ff4 100644 --- a/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/History/PubNubHistoryContractTestSteps.swift @@ -15,8 +15,89 @@ import PubNubSDK public class PubNubHistoryContractTestSteps: PubNubContractTestCase { override public func setup() { startCucumberHookEventsListening() + + When("^I fetch message history for '(.*)' channel$") { args, _ in + guard let channel = args?.first else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + let historyExpect = self.expectation(description: "Fetch history Response") - When("^I fetch message history for (.*) channel(s)?$") { args, _ in + self.client.fetchMessageHistory(for: [channel]) { result in + switch result { + case let .success((messagesByChannel, next)): + self.handleResult(result: (messagesByChannel, next)) + case let .failure(error): + self.handleResult(result: error) + } + historyExpect.fulfill() + } + + self.wait(for: [historyExpect], timeout: 60.0) + } + + Match(["And"], "^history response contains messages (with|without) ('(.*)' and '(.*)') message types$") { args, _ in + guard let matches = args, let inclusionFlag = matches.first else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + guard let lastResult = self.lastResult() else { + XCTAssert(false, "Fetch history didn't returned response") + return + } + + guard let result = lastResult as? (messagesByChannel: [String: [PubNubMessageBase]], next: PubNubBoundedPage?), + let channel = result.messagesByChannel.first?.key, let messages = result.messagesByChannel[channel] else { + XCTAssert(false, "Fetch history returned unexpected response") + return + } + + XCTAssertGreaterThan(messages.count, 0) + + let messagesWithTypes = messages.compactMap { $0.messageType } + XCTAssertFalse(inclusionFlag == "with" && messagesWithTypes.count == 0) + XCTAssertFalse(inclusionFlag == "without" && messagesWithTypes.count > 0) + + if matches.count > 1 { + XCTAssertTrue(messagesWithTypes.map { String(describing: $0.rawValue) }.allSatisfy { Array(matches[1...]).contains($0) }) + } else { + XCTAssertEqual(inclusionFlag == "with" ? messages.count : 0, messagesWithTypes.count) + } + } + + Match(["And"], "^history response contains messages (with|without) ('(.*)' and '(.*)' )?types$") { args, _ in + guard let matches = args, let inclusionFlag = matches.first else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + guard let lastResult = self.lastResult() else { + XCTAssert(false, "Fetch history didn't returned response") + return + } + + guard let result = lastResult as? (messagesByChannel: [String: [PubNubMessageBase]], next: PubNubBoundedPage?), + let channel = result.messagesByChannel.first?.key, let messages = result.messagesByChannel[channel] else { + XCTAssert(false, "Fetch history returned unexpected response") + return + } + + XCTAssertGreaterThan(messages.count, 0) + + let messagesWithTypes = messages.compactMap { $0.customMessageType } + XCTAssertFalse(inclusionFlag == "with" && messagesWithTypes.count == 0) + XCTAssertFalse(inclusionFlag == "without" && messagesWithTypes.count > 0) + + if matches.count > 1 { + XCTAssertTrue(messagesWithTypes.map { $0 }.allSatisfy { Array(matches[1...]).contains($0) }) + } else { + XCTAssertEqual(inclusionFlag == "with" ? messages.count : 0, messagesWithTypes.count) + } + } + + When("^I fetch message history for (single|multiple) channel(s)?$") { args, _ in guard let type = args?.first else { XCTAssertNotNil(args?.first, "Step match failed") return @@ -66,5 +147,36 @@ public class PubNubHistoryContractTestSteps: PubNubContractTestCase { self.wait(for: [historyExpect], timeout: 60.0) } + + When("^I fetch message history with '(.*)' set to '(.*)' for '(.*)' channel$") { args, _ in + guard args?.count == 3, let channel = args?[2] else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + /// Message types enabled by default and user can only opt-out them. + var includeType = true + + if args?.first == "include_custom_message_type" { + includeType = args?[1] == "true" + } + + let historyExpect = self.expectation(description: "Fetch history Response") + + self.client.fetchMessageHistory( + for: [channel], + includeCustomMessageType: includeType + ) { result in + switch result { + case let .success((messagesByChannel, next)): + self.handleResult(result: (messagesByChannel, next)) + case let .failure(error): + self.handleResult(result: error) + } + historyExpect.fulfill() + } + + self.wait(for: [historyExpect], timeout: 60.0) + } } } diff --git a/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift index 633171a9..f9232704 100644 --- a/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Publish/PubNubPublishContractTestSteps.swift @@ -16,7 +16,7 @@ public class PubNubPublishContractTestSteps: PubNubContractTestCase { override public func setup() { startCucumberHookEventsListening() - When("I publish a message") { _, _ in + When("^I publish a message$") { _, _ in let publishMessageExpect = self.expectation(description: "Publish message Response") self.client.publish(channel: "test", message: "hello") { result in @@ -54,7 +54,32 @@ public class PubNubPublishContractTestSteps: PubNubContractTestCase { self.wait(for: [publishMessageExpect], timeout: 60.0) } - When("I send a signal") { _, _ in + When("^I publish message with '(.*)' type$") { args, _ in + guard let type = args?.first as? String? else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + let publishMessageExpect = self.expectation(description: "Publish message with space and type Response") + + self.client.publish( + channel: "test", + message: "hello", + customMessageType: type + ) { result in + switch result { + case let .success(timetoken): + self.handleResult(result: timetoken) + case let .failure(error): + self.handleResult(result: error) + } + publishMessageExpect.fulfill() + } + + self.wait(for: [publishMessageExpect], timeout: 60.0) + } + + When("^I send a signal$") { _, _ in let sendSignalExpect = self.expectation(description: "Send signal Response") self.client.signal(channel: "test", message: "hello") { result in @@ -69,5 +94,31 @@ public class PubNubPublishContractTestSteps: PubNubContractTestCase { self.wait(for: [sendSignalExpect], timeout: 60.0) } + + + When("^I send a signal with '(.*)' type$") { args, _ in + guard let type = args?.last else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + let publishMessageExpect = self.expectation(description: "Publish message with space and type Response") + + self.client.signal( + channel: "test", + message: "hello", + customMessageType: type + ) { result in + switch result { + case let .success(timetoken): + self.handleResult(result: timetoken) + case let .failure(error): + self.handleResult(result: error) + } + publishMessageExpect.fulfill() + } + + self.wait(for: [publishMessageExpect], timeout: 60.0) + } } } diff --git a/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift b/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift index 0713ebe9..f82126ae 100644 --- a/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift +++ b/Tests/PubNubContractTest/Steps/Subscribe/PubNubSubscribeContractTestSteps.swift @@ -46,22 +46,38 @@ public class PubNubSubscribeContractTestSteps: PubNubContractTestCase { override public func setup() { startCucumberHookEventsListening() - Given("the crypto keyset") { _, _ in + Given("^the crypto keyset$") { _, _ in self.cryptoModule = CryptoModule.legacyCryptoModule(with: "enigma") } - Given("the invalid-crypto keyset") { _, _ in + Given("^the invalid-crypto keyset$") { _, _ in self.cryptoModule = CryptoModule.legacyCryptoModule(with: "secret") } - - When("I subscribe") { _, _ in + + When("^I subscribe$") { _, _ in self.subscribeSynchronously(self.client, to: ["test"]) // Give some time to rotate received timetokens. self.waitFor(delay: 0.25) } + + When("^I subscribe to '(.*)' channel$") { args, _ in + guard let matches = args, let channel = matches.first else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + self.subscribeSynchronously(self.client, to: [channel]) + // Give some time to rotate received timetokens. + self.waitFor(delay: 0.25) + } - Then("I receive the message in my subscribe response") { _, userInfo in - let messages = self.waitForMessages(self.client, count: 1) + Then("^I receive (the|[0-9]+) message(s)? in my subscribe response$") { args, userInfo in + guard let match = args?.first else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + let messages = self.waitForMessages(self.client, count: Int(match) ?? 1) XCTAssertNotNil(messages) if self.checkTestingFeature(feature: "MessageEncryption", userInfo: userInfo!) { @@ -99,5 +115,18 @@ public class PubNubSubscribeContractTestSteps: PubNubContractTestCase { /// Give some more time for SDK to check that it won't retry after 0.1 seconds. self.waitFor(delay: 0.3) } + + Match(["And"], "^response contains messages with '(.*)' and '(.*)' types$") { args, _ in + guard let matches = args else { + XCTAssertNotNil(args?.first, "Step match failed") + return + } + + let messages = self.waitForMessages(self.client, count: 2)! + XCTAssertNotNil(messages) + + let messagesWithTypes = messages.compactMap { $0.customMessageType } + XCTAssertTrue(messagesWithTypes.map { $0.description }.allSatisfy { matches.contains($0) }) + } } } diff --git a/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift b/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift index 4ad1ba0e..395bab5a 100644 --- a/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift +++ b/Tests/PubNubTests/Events/Old/SubscriptionStreamTests.swift @@ -19,7 +19,8 @@ class SubscriptionListenerTests: XCTestCase { channel: "Channel", subscription: "Channel", published: 0, - metadata: "Message" + metadata: "Message", + messageType: .message ) let connectionEvent: ConnectionStatus = .connected let statusEvent: SubscriptionListener.StatusEvent = .success(.connected) diff --git a/Tests/PubNubTests/Networking/Routers/HistoryRouterTests.swift b/Tests/PubNubTests/Networking/Routers/HistoryRouterTests.swift index 56d3895b..4f2abd65 100644 --- a/Tests/PubNubTests/Networking/Routers/HistoryRouterTests.swift +++ b/Tests/PubNubTests/Networking/Routers/HistoryRouterTests.swift @@ -33,7 +33,8 @@ extension HistoryRouterTests { let router = HistoryRouter( .fetch( channels: testMultiChannels, max: nil, start: nil, end: nil, - includeMeta: false, includeMessageType: false, includeUUID: false + includeMeta: false, includeMessageType: false, + includeCustomMessageType: false, includeUUID: false ), configuration: config ) @@ -54,7 +55,8 @@ extension HistoryRouterTests { let router = HistoryRouter( .fetch( channels: [], max: nil, start: nil, end: nil, - includeMeta: false, includeMessageType: false, includeUUID: false + includeMeta: false, includeMessageType: false, + includeCustomMessageType: false, includeUUID: false ), configuration: config ) @@ -67,7 +69,8 @@ extension HistoryRouterTests { let router = HistoryRouter( .fetch( channels: testMultiChannels, max: nil, start: nil, end: nil, - includeMeta: false, includeMessageType: false, includeUUID: false + includeMeta: false, includeMessageType: false, + includeCustomMessageType: false, includeUUID: false ), configuration: config ) @@ -264,7 +267,8 @@ extension HistoryRouterTests { let router = HistoryRouter( .fetchWithActions( channel: testChannel, max: nil, start: nil, end: nil, - includeMeta: false, includeMessageType: false, includeUUID: false + includeMeta: false, includeMessageType: false, + includeCustomMessageType: false, includeUUID: false ), configuration: config ) @@ -278,7 +282,8 @@ extension HistoryRouterTests { let router = HistoryRouter( .fetchWithActions( channel: "", max: nil, start: nil, end: nil, - includeMeta: false, includeMessageType: false, includeUUID: false + includeMeta: false, includeMessageType: false, + includeCustomMessageType: false, includeUUID: false ), configuration: config ) @@ -291,7 +296,8 @@ extension HistoryRouterTests { let router = HistoryRouter( .fetchWithActions( channel: testChannel, max: nil, start: nil, end: nil, - includeMeta: false, includeMessageType: false, includeUUID: false + includeMeta: false, includeMessageType: false, + includeCustomMessageType: false, includeUUID: false ), configuration: config ) diff --git a/Tests/PubNubTests/Networking/Routers/PublishRouterTests.swift b/Tests/PubNubTests/Networking/Routers/PublishRouterTests.swift index 3f220621..beb9d3c7 100644 --- a/Tests/PubNubTests/Networking/Routers/PublishRouterTests.swift +++ b/Tests/PubNubTests/Networking/Routers/PublishRouterTests.swift @@ -31,7 +31,7 @@ final class PublishRouterTests: XCTestCase { extension PublishRouterTests { func testPublish_Router() { let router = PublishRouter( - .publish(message: testMessage, channel: testChannel, shouldStore: nil, ttl: nil, meta: nil), + .publish(message: testMessage, channel: testChannel, customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil), configuration: config ) @@ -47,7 +47,7 @@ extension PublishRouterTests { func testPublish_Router_ValidationError() { let router = PublishRouter( - .publish(message: [], channel: testChannel, shouldStore: nil, ttl: nil, meta: nil), + .publish(message: [], channel: testChannel, customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil), configuration: config ) @@ -221,7 +221,7 @@ extension PublishRouterTests { extension PublishRouterTests { func testCompressedPublish_Router() { let router = PublishRouter( - .compressedPublish(message: testMessage, channel: testChannel, shouldStore: nil, ttl: nil, meta: nil), + .compressedPublish(message: testMessage, channel: testChannel, customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil), configuration: config ) @@ -232,7 +232,7 @@ extension PublishRouterTests { func testCompressedPublish_Router_ValidationError() { let router = PublishRouter( - .compressedPublish(message: [], channel: testChannel, shouldStore: nil, ttl: nil, meta: nil), + .compressedPublish(message: [], channel: testChannel, customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil), configuration: config ) @@ -268,7 +268,7 @@ extension PublishRouterTests { let router = PublishRouter( .file( message: FilePublishPayload(channel: testChannel, fileId: testFileId, filename: testFilename), - shouldStore: nil, ttl: nil, meta: nil + customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil ), configuration: config ) @@ -277,6 +277,38 @@ extension PublishRouterTests { XCTAssertEqual(router.category, "Publish a File Message") XCTAssertEqual(router.service, .publish) } + + func testFile_Router_nilMessageTypeAndSpaceId() { + let router = PublishRouter( + .file( + message: FilePublishPayload(channel: testChannel, fileId: testFileId, filename: testFilename), + customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil + ), + configuration: config + ) + + guard let queryItems = try? router.queryItems.get() else { + return XCTAssert(false, "'queryItems' not set") + } + + XCTAssertNil(queryItems.first(where: { $0.name == QueryKey.type.rawValue })) + } + + func testFile_Router_notNilMessageTypeAndSpaceId() { + let router = PublishRouter( + .file( + message: FilePublishPayload(channel: testChannel, fileId: testFileId, filename: testFilename), + customMessageType: "type", shouldStore: nil, ttl: nil, meta: nil + ), + configuration: config + ) + + guard let queryItems = try? router.queryItems.get() else { + return XCTAssert(false, "'queryItems' not set") + } + + XCTAssertTrue(queryItems.contains(URLQueryItem(name: QueryKey.type.rawValue, value: "type"))) + } func testFile_Router_Validate_Message() { let file = FilePublishPayload( @@ -299,7 +331,7 @@ extension PublishRouterTests { let router = PublishRouter( .file( message: FilePublishPayload(channel: "", fileId: testFileId, filename: testFilename), - shouldStore: nil, ttl: nil, meta: nil + customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil ), configuration: config ) @@ -310,7 +342,7 @@ extension PublishRouterTests { let router = PublishRouter( .file( message: FilePublishPayload(channel: testChannel, fileId: "", filename: testFilename), - shouldStore: nil, ttl: nil, meta: nil + customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil ), configuration: config ) @@ -321,7 +353,7 @@ extension PublishRouterTests { let router = PublishRouter( .file( message: FilePublishPayload(channel: testChannel, fileId: testFileId, filename: ""), - shouldStore: nil, ttl: nil, meta: nil + customMessageType: nil, shouldStore: nil, ttl: nil, meta: nil ), configuration: config ) @@ -420,7 +452,7 @@ extension PublishRouterTests { extension PublishRouterTests { func testSignal_Router() { - let router = PublishRouter(.signal(message: testMessage, channel: testChannel), configuration: config) + let router = PublishRouter(.signal(message: testMessage, channel: testChannel, customMessageType: nil), configuration: config) XCTAssertEqual(router.endpoint.description, "Signal") XCTAssertEqual(router.category, "Signal") @@ -428,7 +460,7 @@ extension PublishRouterTests { } func testSignal_Router_ValidationError() { - let router = PublishRouter(.signal(message: "", channel: testChannel), configuration: config) + let router = PublishRouter(.signal(message: "", channel: testChannel, customMessageType: nil), configuration: config) XCTAssertNotEqual(router.validationError?.pubNubError, PubNubError(.invalidEndpointType, router: router)) }