Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 267cf26b2e | |||
| c3817bea15 | |||
| 492f51ce9b | |||
| dd72c619d3 | |||
| 43af10de88 | |||
| 7ba97e4893 | |||
| 0c24cb2262 | |||
| 7b76b76b94 | |||
| 565c44677b | |||
| 30dc3679c1 | |||
| 9a18cae265 | |||
| c04654a87b | |||
| bd7f67b3b0 | |||
| 833add707f | |||
| 9118a7688a | |||
| cba1eb36a2 | |||
| d15139d00d | |||
| c1cf9b47d8 | |||
| 986367be38 | |||
| 7d207136b3 | |||
| 0fa3b72a56 | |||
| 67d2bb3f62 | |||
| f2c333f57d |
+126
-36
@@ -22,6 +22,18 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
public struct Action {
|
||||
fileprivate enum CodingKeys: String {
|
||||
case name
|
||||
case text
|
||||
case type
|
||||
case value
|
||||
case url
|
||||
case style
|
||||
case confirm
|
||||
case options
|
||||
case dataSource = "data_source"
|
||||
}
|
||||
|
||||
public let name: String?
|
||||
public let text: String?
|
||||
public let type: String?
|
||||
@@ -31,17 +43,17 @@ public struct Action {
|
||||
public let confirm: Confirm?
|
||||
public let options: [Option]?
|
||||
public let dataSource: DataSource?
|
||||
|
||||
|
||||
public init(action: [String: Any]?) {
|
||||
name = action?["name"] as? String
|
||||
text = action?["text"] as? String
|
||||
type = action?["type"] as? String
|
||||
value = action?["value"] as? String
|
||||
url = action?["url"] as? String
|
||||
style = ActionStyle(rawValue: action?["style"] as? String ?? "")
|
||||
confirm = Confirm(confirm:action?["confirm"] as? [String: Any])
|
||||
options = (action?["options"] as? [[String: Any]])?.map { Option(option: $0) }
|
||||
dataSource = DataSource(rawValue: action?["data_source"] as? String ?? "")
|
||||
name = action?[CodingKeys.name.rawValue] as? String
|
||||
text = action?[CodingKeys.text.rawValue] as? String
|
||||
type = action?[CodingKeys.type.rawValue] as? String
|
||||
value = action?[CodingKeys.value.rawValue] as? String
|
||||
url = action?[CodingKeys.url.rawValue] as? String
|
||||
style = ActionStyle(rawValue: action?[CodingKeys.style.rawValue] as? String ?? "")
|
||||
confirm = Confirm(confirm:action?[CodingKeys.confirm.rawValue] as? [String: Any])
|
||||
options = (action?[CodingKeys.options.rawValue] as? [[String: Any]])?.map { Option(option: $0) }
|
||||
dataSource = DataSource(rawValue: action?[CodingKeys.dataSource.rawValue] as? String ?? "")
|
||||
}
|
||||
|
||||
public init(name: String, text: String, type: String = "button", style: ActionStyle = .defaultStyle, value: String? = nil,
|
||||
@@ -59,29 +71,36 @@ public struct Action {
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var dict = [String: Any]()
|
||||
dict["name"] = name
|
||||
dict["text"] = text
|
||||
dict["type"] = type
|
||||
dict["value"] = value
|
||||
dict["url"] = url
|
||||
dict["style"] = style?.rawValue
|
||||
dict["confirm"] = confirm?.dictionary
|
||||
dict["options"] = options?.map { $0.dictionary }
|
||||
dict["data_source"] = dataSource?.rawValue
|
||||
dict[CodingKeys.name.rawValue] = name
|
||||
dict[CodingKeys.text.rawValue] = text
|
||||
dict[CodingKeys.type.rawValue] = type
|
||||
dict[CodingKeys.value.rawValue] = value
|
||||
dict[CodingKeys.url.rawValue] = url
|
||||
dict[CodingKeys.style.rawValue] = style?.rawValue
|
||||
dict[CodingKeys.confirm.rawValue] = confirm?.dictionary
|
||||
dict[CodingKeys.options.rawValue] = options?.map { $0.dictionary }
|
||||
dict[CodingKeys.dataSource.rawValue] = dataSource?.rawValue
|
||||
return dict
|
||||
}
|
||||
|
||||
public struct Confirm {
|
||||
fileprivate enum CodingKeys: String {
|
||||
case title
|
||||
case text
|
||||
case okText = "ok_text"
|
||||
case dismissText = "dismiss_text"
|
||||
}
|
||||
|
||||
public let title: String?
|
||||
public let text: String?
|
||||
public let okText: String?
|
||||
public let dismissText: String?
|
||||
|
||||
|
||||
public init(confirm: [String: Any]?) {
|
||||
title = confirm?["title"] as? String
|
||||
text = confirm?["text"] as? String
|
||||
okText = confirm?["ok_text"] as? String
|
||||
dismissText = confirm?["dismiss_text"] as? String
|
||||
title = confirm?[CodingKeys.title.rawValue] as? String
|
||||
text = confirm?[CodingKeys.text.rawValue] as? String
|
||||
okText = confirm?[CodingKeys.okText.rawValue] as? String
|
||||
dismissText = confirm?[CodingKeys.dismissText.rawValue] as? String
|
||||
}
|
||||
|
||||
public init(text: String, title: String? = nil, okText: String? = nil, dismissText: String? = nil) {
|
||||
@@ -93,21 +112,26 @@ public struct Action {
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var dict = [String: Any]()
|
||||
dict["title"] = title
|
||||
dict["text"] = text
|
||||
dict["ok_text"] = okText
|
||||
dict["dismiss_text"] = dismissText
|
||||
dict[CodingKeys.title.rawValue] = title
|
||||
dict[CodingKeys.text.rawValue] = text
|
||||
dict[CodingKeys.okText.rawValue] = okText
|
||||
dict[CodingKeys.dismissText.rawValue] = dismissText
|
||||
return dict
|
||||
}
|
||||
}
|
||||
|
||||
public struct Option {
|
||||
fileprivate enum CodingKeys: String {
|
||||
case text
|
||||
case value
|
||||
}
|
||||
|
||||
public let text: String?
|
||||
public let value: String?
|
||||
|
||||
|
||||
public init(option: [String: Any]?) {
|
||||
text = option?["text"] as? String
|
||||
value = option?["value"] as? String
|
||||
text = option?[CodingKeys.text.rawValue] as? String
|
||||
value = option?[CodingKeys.value.rawValue] as? String
|
||||
}
|
||||
|
||||
public init(text: String, value: String) {
|
||||
@@ -117,26 +141,92 @@ public struct Action {
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var dict = [String: Any]()
|
||||
dict["text"] = text
|
||||
dict["value"] = value
|
||||
dict[CodingKeys.text.rawValue] = text
|
||||
dict[CodingKeys.value.rawValue] = value
|
||||
return dict
|
||||
}
|
||||
}
|
||||
|
||||
public enum DataSource: String {
|
||||
public enum DataSource: String, Codable {
|
||||
case users
|
||||
case channels
|
||||
case conversations
|
||||
}
|
||||
}
|
||||
|
||||
public enum ActionStyle: String {
|
||||
extension Action: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
name = try values.decodeIfPresent(String.self, forKey: .name)
|
||||
text = try values.decodeIfPresent(String.self, forKey: .text)
|
||||
type = try values.decodeIfPresent(String.self, forKey: .type)
|
||||
value = try values.decodeIfPresent(String.self, forKey: .value)
|
||||
url = try values.decodeIfPresent(String.self, forKey: .url)
|
||||
style = try values.decodeIfPresent(ActionStyle.self, forKey: .style)
|
||||
confirm = try values.decodeIfPresent(Confirm.self, forKey: .confirm)
|
||||
options = try values.decodeIfPresent([Option].self, forKey: .options)
|
||||
dataSource = try values.decodeIfPresent(DataSource.self, forKey: .dataSource)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(name, forKey: .name)
|
||||
try container.encode(text, forKey: .text)
|
||||
try container.encode(type, forKey: .type)
|
||||
try container.encode(value, forKey: .value)
|
||||
try container.encode(url, forKey: .url)
|
||||
try container.encode(style, forKey: .style)
|
||||
try container.encode(confirm, forKey: .confirm)
|
||||
try container.encode(options, forKey: .options)
|
||||
try container.encode(dataSource, forKey: .dataSource)
|
||||
}
|
||||
}
|
||||
|
||||
extension Action.CodingKeys: CodingKey { }
|
||||
|
||||
extension Action.Confirm: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
title = try values.decodeIfPresent(String.self, forKey: .title)
|
||||
text = try values.decodeIfPresent(String.self, forKey: .text)
|
||||
okText = try values.decodeIfPresent(String.self, forKey: .okText)
|
||||
dismissText = try values.decodeIfPresent(String.self, forKey: .dismissText)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encode(text, forKey: .text)
|
||||
try container.encode(okText, forKey: .okText)
|
||||
try container.encode(dismissText, forKey: .dismissText)
|
||||
}
|
||||
}
|
||||
|
||||
extension Action.Confirm.CodingKeys: CodingKey { }
|
||||
|
||||
extension Action.Option: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
text = try values.decodeIfPresent(String.self, forKey: .text)
|
||||
value = try values.decodeIfPresent(String.self, forKey: .value)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(text, forKey: .text)
|
||||
try container.encode(value, forKey: .value)
|
||||
}
|
||||
}
|
||||
|
||||
extension Action.Option.CodingKeys: CodingKey { }
|
||||
|
||||
public enum ActionStyle: String, Codable {
|
||||
case defaultStyle = "default"
|
||||
case primary = "primary"
|
||||
case danger = "danger"
|
||||
}
|
||||
|
||||
public enum MessageResponseType: String {
|
||||
public enum MessageResponseType: String, Codable {
|
||||
case inChannel = "in_channel"
|
||||
case ephemeral = "ephemeral"
|
||||
}
|
||||
|
||||
@@ -22,14 +22,20 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
public struct AttachmentField {
|
||||
fileprivate enum CodingKeys: String {
|
||||
case title
|
||||
case value
|
||||
case short
|
||||
}
|
||||
|
||||
public let title: String?
|
||||
public let value: String?
|
||||
public let short: Bool?
|
||||
|
||||
|
||||
public init(field: [String: Any]?) {
|
||||
title = field?["title"] as? String
|
||||
value = field?["value"] as? String
|
||||
short = field?["short"] as? Bool
|
||||
title = field?[CodingKeys.title.rawValue] as? String
|
||||
value = field?[CodingKeys.value.rawValue] as? String
|
||||
short = field?[CodingKeys.short.rawValue] as? Bool
|
||||
}
|
||||
|
||||
public init(title: String?, value: String?, short: Bool? = nil) {
|
||||
@@ -40,9 +46,27 @@ public struct AttachmentField {
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var field = [String: Any]()
|
||||
field["title"] = title
|
||||
field["value"] = value
|
||||
field["short"] = short
|
||||
field[CodingKeys.title.rawValue] = title
|
||||
field[CodingKeys.value.rawValue] = value
|
||||
field[CodingKeys.short.rawValue] = short
|
||||
return field
|
||||
}
|
||||
}
|
||||
|
||||
extension AttachmentField: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
title = try values.decodeIfPresent(String.self, forKey: .title)
|
||||
value = try values.decodeIfPresent(String.self, forKey: .value)
|
||||
short = try values.decodeIfPresent(Bool.self, forKey: .short)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encode(value, forKey: .value)
|
||||
try container.encode(short, forKey: .short)
|
||||
}
|
||||
}
|
||||
|
||||
extension AttachmentField.CodingKeys: CodingKey { }
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
/// Defined by https://api.slack.com/reference/messaging/composition-objects#text
|
||||
public struct TextComposition {
|
||||
/// The type of block. Can be one of plainText or markdown.
|
||||
public let type: BlockType
|
||||
public let text: String
|
||||
public let emoji: Bool?
|
||||
public let verbatim: Bool?
|
||||
|
||||
public init(type: BlockType,
|
||||
text: String,
|
||||
emoji: Bool? = nil,
|
||||
verbatim: Bool? = nil) {
|
||||
self.type = type
|
||||
self.text = text
|
||||
self.emoji = emoji
|
||||
self.verbatim = verbatim
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var composition = [String: Any]()
|
||||
composition["type"] = type.rawValue
|
||||
composition["text"] = text
|
||||
composition["emoji"] = emoji
|
||||
composition["verbatim"] = verbatim
|
||||
return composition
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/composition-objects#option
|
||||
public struct OptionComposition {
|
||||
public let text: TextComposition
|
||||
public let value: String
|
||||
public let url: String?
|
||||
|
||||
public init(text: TextComposition,
|
||||
value: String,
|
||||
url: String? = nil) {
|
||||
self.text = text
|
||||
self.value = value
|
||||
self.url = url
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var composition = [String: Any]()
|
||||
composition["text"] = text.dictionary
|
||||
composition["value"] = value
|
||||
composition["url"] = url
|
||||
return composition
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/composition-objects#option-group
|
||||
public struct OptionGroupComposition {
|
||||
public let label: TextComposition
|
||||
public let options: [OptionComposition]
|
||||
|
||||
public init(label: TextComposition,
|
||||
options: [OptionComposition]) {
|
||||
self.label = label
|
||||
self.options = options
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var composition = [String: Any]()
|
||||
composition["label"] = label.dictionary
|
||||
composition["options"] = options.map { $0.dictionary }
|
||||
return composition
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/composition-objects#confirm
|
||||
public struct ConfirmComposition {
|
||||
public let title: TextComposition
|
||||
public let text: TextComposition
|
||||
public let confirm: TextComposition
|
||||
public let deny: TextComposition
|
||||
|
||||
public init(title: TextComposition,
|
||||
text: TextComposition,
|
||||
confirm: TextComposition,
|
||||
deny: TextComposition) {
|
||||
self.title = title
|
||||
self.text = text
|
||||
self.confirm = confirm
|
||||
self.deny = deny
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var composition = [String: Any]()
|
||||
composition["title"] = title.dictionary
|
||||
composition["text"] = text.dictionary
|
||||
composition["confirm"] = confirm.dictionary
|
||||
composition["deny"] = deny.dictionary
|
||||
return composition
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,354 @@
|
||||
public protocol BlockElement {
|
||||
var dictionary: [String: Any] { get }
|
||||
}
|
||||
|
||||
public protocol SectionElement: BlockElement {}
|
||||
|
||||
public protocol ActionsElement: BlockElement {}
|
||||
|
||||
public protocol ContextElement: BlockElement {}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#image
|
||||
public struct ImageElement: BlockElement, SectionElement, ContextElement {
|
||||
/// Type will always be image.
|
||||
public let type: BlockType
|
||||
public let imageURL: String
|
||||
public let altText: String
|
||||
|
||||
public init(type: BlockType = .image,
|
||||
imageURL: String,
|
||||
altText: String) {
|
||||
self.type = type
|
||||
self.imageURL = imageURL
|
||||
self.altText = altText
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["image_url"] = imageURL
|
||||
element["alt_text"] = altText
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom type to better support Context PlainText
|
||||
public struct PlainTextElement: BlockElement, ContextElement {
|
||||
/// Type will always be image.
|
||||
public let type: BlockType
|
||||
public let text: String
|
||||
|
||||
public init(type: BlockType = .plainText,
|
||||
text: String) {
|
||||
self.type = type
|
||||
self.text = text
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["text"] = text
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Custom type to better support Context Markdown Text
|
||||
public struct MarkdownTextElement: BlockElement, ContextElement {
|
||||
/// Type will always be image.
|
||||
public let type: BlockType
|
||||
public let text: String
|
||||
|
||||
public init(type: BlockType = .markdown,
|
||||
text: String) {
|
||||
self.type = type
|
||||
self.text = text
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["text"] = text
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
public enum ButtonElementStyle: String, CaseIterable {
|
||||
case `default`
|
||||
case primary
|
||||
case danger
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#button
|
||||
public struct ButtonElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be button.
|
||||
public let type: BlockType
|
||||
public let text: TextComposition
|
||||
public let actionId: String
|
||||
public let url: String?
|
||||
public let value: String?
|
||||
public let style: ButtonElementStyle?
|
||||
|
||||
public init(type: BlockType = .button,
|
||||
text: TextComposition,
|
||||
actionId: String,
|
||||
url: String? = nil,
|
||||
value: String? = nil,
|
||||
style: ButtonElementStyle? = nil) {
|
||||
self.type = type
|
||||
self.text = text
|
||||
self.actionId = actionId
|
||||
self.url = url
|
||||
self.value = value
|
||||
self.style = style
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["text"] = text.dictionary
|
||||
element["action_id"] = actionId
|
||||
element["url"] = url
|
||||
element["value"] = value
|
||||
element["style"] = style
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#static-select
|
||||
public struct StaticSelectElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be static_select.
|
||||
public let type: BlockType
|
||||
public let placeholder: TextComposition
|
||||
public let actionId: String
|
||||
public let options: [OptionComposition]?
|
||||
public let optionGroups: [OptionGroupComposition]?
|
||||
public let initialOption: OptionComposition?
|
||||
public let initialOptionGroup: OptionGroupComposition?
|
||||
public let confirm: ConfirmComposition?
|
||||
|
||||
public init(type: BlockType = .staticSelect,
|
||||
placeholder: TextComposition,
|
||||
actionId: String,
|
||||
options: [OptionComposition]? = nil,
|
||||
optionGroups: [OptionGroupComposition]? = nil,
|
||||
initialOption: OptionComposition? = nil,
|
||||
initialOptionGroup: OptionGroupComposition? = nil,
|
||||
confirm: ConfirmComposition? = nil) {
|
||||
self.type = type
|
||||
self.placeholder = placeholder
|
||||
self.actionId = actionId
|
||||
self.options = options
|
||||
self.optionGroups = optionGroups
|
||||
self.initialOption = initialOption
|
||||
self.initialOptionGroup = initialOptionGroup
|
||||
self.confirm = confirm
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["placeholder"] = placeholder.dictionary
|
||||
element["action_id"] = actionId
|
||||
element["options"] = options?.map { $0.dictionary }
|
||||
element["option_groups"] = optionGroups?.map { $0.dictionary }
|
||||
element["initial_option"] = initialOption?.dictionary ?? initialOptionGroup?.dictionary
|
||||
element["confirm"] = confirm?.dictionary
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#external-select
|
||||
public struct ExternalSelectElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be externalSelect.
|
||||
public let type: BlockType
|
||||
public let placeholder: TextComposition
|
||||
public let actionId: String
|
||||
public let initialOption: OptionComposition?
|
||||
public let initialOptionGroup: OptionGroupComposition?
|
||||
public let minQueryLenght: Int?
|
||||
public let confirm: ConfirmComposition?
|
||||
|
||||
public init(type: BlockType = .externalSelect,
|
||||
placeholder: TextComposition,
|
||||
actionId: String,
|
||||
initialOption: OptionComposition? = nil,
|
||||
initialOptionGroup: OptionGroupComposition? = nil,
|
||||
minQueryLenght: Int?,
|
||||
confirm: ConfirmComposition? = nil) {
|
||||
self.type = type
|
||||
self.placeholder = placeholder
|
||||
self.actionId = actionId
|
||||
self.initialOption = initialOption
|
||||
self.initialOptionGroup = initialOptionGroup
|
||||
self.minQueryLenght = minQueryLenght
|
||||
self.confirm = confirm
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["placeholder"] = placeholder.dictionary
|
||||
element["action_id"] = actionId
|
||||
element["initial_option"] = initialOption?.dictionary ?? initialOptionGroup?.dictionary
|
||||
element["min_query_length"] = minQueryLenght
|
||||
element["confirm"] = confirm?.dictionary
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#user-select
|
||||
public struct UsersSelectElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be usersSelect.
|
||||
public let type: BlockType
|
||||
public let placeholder: TextComposition
|
||||
public let actionId: String
|
||||
public let initialUserId: String?
|
||||
public let confirm: ConfirmComposition?
|
||||
|
||||
public init(type: BlockType = .usersSelect,
|
||||
placeholder: TextComposition,
|
||||
actionId: String,
|
||||
initialUserId: String?,
|
||||
confirm: ConfirmComposition? = nil) {
|
||||
self.type = type
|
||||
self.placeholder = placeholder
|
||||
self.actionId = actionId
|
||||
self.initialUserId = initialUserId
|
||||
self.confirm = confirm
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["placeholder"] = placeholder.dictionary
|
||||
element["action_id"] = actionId
|
||||
element["initial_user"] = initialUserId
|
||||
element["confirm"] = confirm?.dictionary
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#converstation-select
|
||||
public struct ConverstationSelectElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be converstationSelect.
|
||||
public let type: BlockType
|
||||
public let placeholder: TextComposition
|
||||
public let actionId: String
|
||||
public let initialConverstationId: String?
|
||||
public let confirm: ConfirmComposition?
|
||||
|
||||
public init(type: BlockType = .converstationSelect,
|
||||
placeholder: TextComposition,
|
||||
actionId: String,
|
||||
initialConverstationId: String?,
|
||||
confirm: ConfirmComposition? = nil) {
|
||||
self.type = type
|
||||
self.placeholder = placeholder
|
||||
self.actionId = actionId
|
||||
self.initialConverstationId = initialConverstationId
|
||||
self.confirm = confirm
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["placeholder"] = placeholder.dictionary
|
||||
element["action_id"] = actionId
|
||||
element["initial_conversation"] = initialConverstationId
|
||||
element["confirm"] = confirm?.dictionary
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#channel-select
|
||||
public struct ChannelSelectElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be channelSelect.
|
||||
public let type: BlockType
|
||||
public let placeholder: TextComposition
|
||||
public let actionId: String
|
||||
public let initialChannelId: String?
|
||||
public let confirm: ConfirmComposition?
|
||||
|
||||
public init(type: BlockType = .channelSelect,
|
||||
placeholder: TextComposition,
|
||||
actionId: String,
|
||||
initialChannelId: String?,
|
||||
confirm: ConfirmComposition? = nil) {
|
||||
self.type = type
|
||||
self.placeholder = placeholder
|
||||
self.actionId = actionId
|
||||
self.initialChannelId = initialChannelId
|
||||
self.confirm = confirm
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["placeholder"] = placeholder.dictionary
|
||||
element["action_id"] = actionId
|
||||
element["initial_channel"] = initialChannelId
|
||||
element["confirm"] = confirm?.dictionary
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#overflow
|
||||
public struct OverflowElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be overflow.
|
||||
public let type: BlockType
|
||||
public let actionId: String
|
||||
public let options: [OptionComposition]
|
||||
public let confirm: ConfirmComposition?
|
||||
|
||||
public init(type: BlockType = .overflow,
|
||||
placeholder: TextComposition,
|
||||
actionId: String,
|
||||
options: [OptionComposition],
|
||||
confirm: ConfirmComposition? = nil) {
|
||||
self.type = type
|
||||
self.actionId = actionId
|
||||
self.options = options
|
||||
self.confirm = confirm
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["action_id"] = actionId
|
||||
element["options"] = options
|
||||
element["confirm"] = confirm?.dictionary
|
||||
return element
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/block-elements#overflow
|
||||
public struct DatePickerElement: BlockElement, SectionElement, ActionsElement {
|
||||
/// Type will always be datePicker.
|
||||
public let type: BlockType
|
||||
public let actionId: String
|
||||
public let placeholder: TextComposition?
|
||||
public let initialDate: String?
|
||||
public let confirm: ConfirmComposition?
|
||||
|
||||
public init(type: BlockType = .datePicker,
|
||||
actionId: String,
|
||||
placeholder: TextComposition?,
|
||||
initialDate: String? = nil,
|
||||
confirm: ConfirmComposition? = nil) {
|
||||
self.type = type
|
||||
self.actionId = actionId
|
||||
self.placeholder = placeholder
|
||||
self.initialDate = initialDate
|
||||
self.confirm = confirm
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var element = [String: Any]()
|
||||
element["type"] = type.rawValue
|
||||
element["action_id"] = actionId
|
||||
element["placeholder"] = placeholder?.dictionary
|
||||
element["initial_date"] = initialDate
|
||||
element["confirm"] = confirm?.dictionary
|
||||
return element
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
public protocol Block {
|
||||
var dictionary: [String: Any] { get }
|
||||
}
|
||||
|
||||
public enum BlockType: String, CaseIterable {
|
||||
case actions
|
||||
case button
|
||||
case context
|
||||
case datePicker = "datepicker"
|
||||
case divider
|
||||
case image
|
||||
case markdown = "mrkdwn"
|
||||
case overflow
|
||||
case plainText = "plain_text"
|
||||
case section
|
||||
|
||||
// Selects
|
||||
|
||||
case channelSelect = "channels_select"
|
||||
case converstationSelect = "conversations_select"
|
||||
case externalSelect = "external_select"
|
||||
case staticSelect = "static_select"
|
||||
case usersSelect = "users_select"
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/blocks#section
|
||||
public struct SectionBlock: Block {
|
||||
/// Type will always be section.
|
||||
public let type: BlockType
|
||||
public let text: TextComposition
|
||||
public let blockId: String?
|
||||
public let fields: [TextComposition]?
|
||||
public let accessory: SectionElement?
|
||||
|
||||
public init(type: BlockType = .section,
|
||||
text: TextComposition,
|
||||
blockId: String? = nil,
|
||||
fields: [TextComposition]? = nil,
|
||||
accessory: SectionElement? = nil) {
|
||||
self.type = type
|
||||
self.text = text
|
||||
self.blockId = blockId
|
||||
self.fields = fields
|
||||
self.accessory = accessory
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var block = [String: Any]()
|
||||
block["type"] = type.rawValue
|
||||
block["text"] = text.dictionary
|
||||
block["block_id"] = blockId
|
||||
block["fields"] = fields?.map { $0.dictionary }
|
||||
block["accessory"] = accessory?.dictionary
|
||||
return block
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/blocks#divider
|
||||
public struct DividerBlock: Block {
|
||||
/// Type will always be divider.
|
||||
public let type: BlockType
|
||||
public let blockId: String?
|
||||
|
||||
public init(type: BlockType = .divider,
|
||||
blockId: String? = nil) {
|
||||
self.type = type
|
||||
self.blockId = blockId
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var block = [String: Any]()
|
||||
block["type"] = type
|
||||
block["block_id"] = blockId
|
||||
return block
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/blocks#image
|
||||
public struct ImageBlock: Block {
|
||||
/// Type will always be image.
|
||||
public let type: BlockType
|
||||
public let imageURL: String
|
||||
public let altText: String
|
||||
public let title: String?
|
||||
public let blockId: String?
|
||||
|
||||
public init(type: BlockType = .image,
|
||||
imageURL: String,
|
||||
altText: String,
|
||||
title: String? = nil,
|
||||
blockId: String? = nil) {
|
||||
self.type = type
|
||||
self.imageURL = imageURL
|
||||
self.altText = altText
|
||||
self.title = title
|
||||
self.blockId = blockId
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var block = [String: Any]()
|
||||
block["type"] = type.rawValue
|
||||
block["image_url"] = imageURL
|
||||
block["alt_text"] = altText
|
||||
block["title"] = title
|
||||
block["block_id"] = blockId
|
||||
return block
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/blocks#actions
|
||||
public struct ActionsBlock: Block {
|
||||
/// Type will always be actions.
|
||||
public let type: BlockType
|
||||
public let elements: [ActionsElement]
|
||||
public let blockId: String?
|
||||
|
||||
public init(type: BlockType = .actions,
|
||||
elements: [ActionsElement],
|
||||
blockId: String? = nil) {
|
||||
self.type = type
|
||||
self.elements = elements
|
||||
self.blockId = blockId
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var block = [String: Any]()
|
||||
block["type"] = type.rawValue
|
||||
block["elements"] = elements.map { $0.dictionary }
|
||||
block["block_id"] = blockId
|
||||
return block
|
||||
}
|
||||
}
|
||||
|
||||
/// Defined by https://api.slack.com/reference/messaging/blocks#context
|
||||
public struct ContextBlock: Block {
|
||||
/// Type will always be actions.
|
||||
public let type: BlockType
|
||||
public let elements: [ContextElement]
|
||||
public let blockId: String?
|
||||
|
||||
public init(type: BlockType = .context,
|
||||
elements: [ContextElement],
|
||||
blockId: String? = nil) {
|
||||
self.type = type
|
||||
self.elements = elements
|
||||
self.blockId = blockId
|
||||
}
|
||||
|
||||
public var dictionary: [String: Any] {
|
||||
var block = [String: Any]()
|
||||
block["type"] = type.rawValue
|
||||
block["elements"] = elements.map { $0.dictionary }
|
||||
block["block_id"] = blockId
|
||||
return block
|
||||
}
|
||||
}
|
||||
@@ -22,11 +22,32 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
public struct Edited {
|
||||
fileprivate enum CodingKeys: String {
|
||||
case user
|
||||
case ts
|
||||
}
|
||||
|
||||
public let user: String?
|
||||
public let ts: String?
|
||||
|
||||
public init(edited: [String: Any]?) {
|
||||
user = edited?["user"] as? String
|
||||
ts = edited?["ts"] as? String
|
||||
user = edited?[CodingKeys.user.rawValue] as? String
|
||||
ts = edited?[CodingKeys.ts.rawValue] as? String
|
||||
}
|
||||
}
|
||||
|
||||
extension Edited: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
user = try values.decodeIfPresent(String.self, forKey: .user)
|
||||
ts = try values.decodeIfPresent(String.self, forKey: .ts)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(user, forKey: .user)
|
||||
try container.encode(ts, forKey: .ts)
|
||||
}
|
||||
}
|
||||
|
||||
extension Edited.CodingKeys: CodingKey { }
|
||||
|
||||
@@ -22,11 +22,32 @@
|
||||
// THE SOFTWARE.
|
||||
|
||||
public struct Reply {
|
||||
fileprivate enum CodingKeys: String {
|
||||
case user
|
||||
case ts
|
||||
}
|
||||
|
||||
public let user: String?
|
||||
public let ts: String?
|
||||
|
||||
|
||||
public init(reply: [String: Any]?) {
|
||||
user = reply?["user"] as? String
|
||||
ts = reply?["ts"] as? String
|
||||
user = reply?[CodingKeys.user.rawValue] as? String
|
||||
ts = reply?[CodingKeys.ts.rawValue] as? String
|
||||
}
|
||||
}
|
||||
|
||||
extension Reply: Codable {
|
||||
public init(from decoder: Decoder) throws {
|
||||
let values = try decoder.container(keyedBy: CodingKeys.self)
|
||||
user = try values.decodeIfPresent(String.self, forKey: .user)
|
||||
ts = try values.decodeIfPresent(String.self, forKey: .ts)
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(user, forKey: .user)
|
||||
try container.encode(ts, forKey: .ts)
|
||||
}
|
||||
}
|
||||
|
||||
extension Reply.CodingKeys: CodingKey { }
|
||||
|
||||
@@ -88,6 +88,7 @@ public enum Endpoint: String {
|
||||
case usersGetPresence = "users.getPresence"
|
||||
case usersInfo = "users.info"
|
||||
case usersList = "users.list"
|
||||
case usersLookupByEmail = "users.lookupByEmail"
|
||||
case usersProfileSet = "users.profile.set"
|
||||
case usersSetActive = "users.setActive"
|
||||
case usersSetPresence = "users.setPresence"
|
||||
|
||||
@@ -271,6 +271,7 @@ extension WebAPI {
|
||||
parse: ParseMode? = nil,
|
||||
linkNames: Bool? = nil,
|
||||
attachments: [Attachment?]? = nil,
|
||||
blocks: [Block]? = nil,
|
||||
unfurlLinks: Bool? = nil,
|
||||
unfurlMedia: Bool? = nil,
|
||||
iconURL: String? = nil,
|
||||
@@ -290,7 +291,8 @@ extension WebAPI {
|
||||
"username": username,
|
||||
"icon_url": iconURL,
|
||||
"icon_emoji": iconEmoji,
|
||||
"attachments": encodeAttachments(attachments)
|
||||
"attachments": encodeAttachments(attachments),
|
||||
"blocks": encodeBlocks(blocks)
|
||||
]
|
||||
networkInterface.request(.chatPostMessage, parameters: parameters, successClosure: {(response) in
|
||||
success?((ts: response["ts"] as? String, response["channel"] as? String))
|
||||
@@ -346,6 +348,7 @@ extension WebAPI {
|
||||
thread: String? = nil,
|
||||
asUser: Bool? = nil,
|
||||
attachments: [Attachment?]? = nil,
|
||||
blocks: [Block]? = nil,
|
||||
linkNames: Bool? = nil,
|
||||
parse: ParseMode? = nil,
|
||||
success: (((ts: String?, channel: String?)) -> Void)?,
|
||||
@@ -359,6 +362,7 @@ extension WebAPI {
|
||||
"thread_ts": thread,
|
||||
"as_user": asUser,
|
||||
"attachments": encodeAttachments(attachments),
|
||||
"blocks": encodeBlocks(blocks),
|
||||
"link_names": linkNames,
|
||||
"parse": parse?.rawValue,
|
||||
]
|
||||
@@ -1134,6 +1138,15 @@ extension WebAPI {
|
||||
failure?(error)
|
||||
}
|
||||
}
|
||||
|
||||
public func usersLookupByEmail(_ email: String, success: ((_ user: User) -> Void)?, failure: FailureClosure?) {
|
||||
let parameters: [String: Any] = ["token": token, "email": email]
|
||||
networkInterface.request(.usersLookupByEmail, parameters: parameters, successClosure: { response in
|
||||
success?(User(user: response["user"] as? [String: Any]))
|
||||
}) { error in
|
||||
failure?(error)
|
||||
}
|
||||
}
|
||||
|
||||
public func usersProfileSet(profile: User.Profile, success: SuccessClosure?, failure: FailureClosure?) {
|
||||
let profileValues = ([
|
||||
@@ -1241,6 +1254,19 @@ extension WebAPI {
|
||||
return nil
|
||||
}
|
||||
|
||||
fileprivate func encodeBlocks(_ blocks: [Block]?) -> String? {
|
||||
if let blocks = blocks {
|
||||
let blocksArray: [[String: Any]] = blocks.map { $0.dictionary }
|
||||
do {
|
||||
let data = try JSONSerialization.data(withJSONObject: blocksArray, options: [])
|
||||
return String(data: data, encoding: String.Encoding.utf8)
|
||||
} catch let error {
|
||||
print(error)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
fileprivate func enumerateDNDStatuses(_ statuses: [String: Any]) -> [String: DoNotDisturbStatus] {
|
||||
var retVal = [String: DoNotDisturbStatus]()
|
||||
for key in statuses.keys {
|
||||
|
||||
@@ -119,6 +119,13 @@
|
||||
26D4E6082212120900A67B67 /* SKRTMAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 26D4E5FE221211B900A67B67 /* SKRTMAPI.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
26D4E6092212120F00A67B67 /* SKServer.h in Headers */ = {isa = PBXBuildFile; fileRef = 26D4E600221211B900A67B67 /* SKServer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
26D4E60A2212121400A67B67 /* SKWebAPI.h in Headers */ = {isa = PBXBuildFile; fileRef = 26D4E601221211B900A67B67 /* SKWebAPI.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
A20D15EA22DE158000044CFC /* BlockComposition.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20D15E722DE158000044CFC /* BlockComposition.swift */; };
|
||||
A20D15EB22DE158000044CFC /* BlockElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20D15E822DE158000044CFC /* BlockElement.swift */; };
|
||||
A20D15EC22DE158000044CFC /* BlockLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A20D15E922DE158000044CFC /* BlockLayout.swift */; };
|
||||
9EA45FB922C01290006A6D36 /* action.json in Resources */ = {isa = PBXBuildFile; fileRef = 9EA45FB822C01290006A6D36 /* action.json */; };
|
||||
9EE6A7C322C2CDD6002BD111 /* edited.json in Resources */ = {isa = PBXBuildFile; fileRef = 9EE6A7C222C2CDD6002BD111 /* edited.json */; };
|
||||
9EEC459622BE63F800206AC3 /* reply.json in Resources */ = {isa = PBXBuildFile; fileRef = 9EEC459522BE63F800206AC3 /* reply.json */; };
|
||||
9EEC459822BE789600206AC3 /* attachmentfield.json in Resources */ = {isa = PBXBuildFile; fileRef = 9EEC459722BE789600206AC3 /* attachmentfield.json */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -311,6 +318,13 @@
|
||||
26D4E6292220731800A67B67 /* rtm.connect.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = rtm.connect.json; sourceTree = "<group>"; };
|
||||
26D4E62A2220731800A67B67 /* file.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = file.json; sourceTree = "<group>"; };
|
||||
26D4E6362220733F00A67B67 /* SKCoreTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SKCoreTests.swift; sourceTree = "<group>"; };
|
||||
A20D15E722DE158000044CFC /* BlockComposition.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockComposition.swift; sourceTree = "<group>"; };
|
||||
A20D15E822DE158000044CFC /* BlockElement.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockElement.swift; sourceTree = "<group>"; };
|
||||
A20D15E922DE158000044CFC /* BlockLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlockLayout.swift; sourceTree = "<group>"; };
|
||||
9EA45FB822C01290006A6D36 /* action.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = action.json; sourceTree = "<group>"; };
|
||||
9EE6A7C222C2CDD6002BD111 /* edited.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = edited.json; sourceTree = "<group>"; };
|
||||
9EEC459522BE63F800206AC3 /* reply.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = reply.json; sourceTree = "<group>"; };
|
||||
9EEC459722BE789600206AC3 /* attachmentfield.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = attachmentfield.json; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -494,6 +508,9 @@
|
||||
263B102221FE33A000AF9EF9 /* Action.swift */,
|
||||
263B100C21FE33A000AF9EF9 /* Attachment.swift */,
|
||||
263B100B21FE33A000AF9EF9 /* AttachmentField.swift */,
|
||||
A20D15E722DE158000044CFC /* BlockComposition.swift */,
|
||||
A20D15E822DE158000044CFC /* BlockElement.swift */,
|
||||
A20D15E922DE158000044CFC /* BlockLayout.swift */,
|
||||
263B101B21FE33A000AF9EF9 /* Bot.swift */,
|
||||
263B101521FE33A000AF9EF9 /* Channel.swift */,
|
||||
263B101021FE33A000AF9EF9 /* Comment.swift */,
|
||||
@@ -818,13 +835,17 @@
|
||||
26D4E618222072A700A67B67 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9EA45FB822C01290006A6D36 /* action.json */,
|
||||
9EEC459722BE789600206AC3 /* attachmentfield.json */,
|
||||
26D4E6282220731800A67B67 /* channel.json */,
|
||||
26D4E6222220731700A67B67 /* conversation.json */,
|
||||
9EE6A7C222C2CDD6002BD111 /* edited.json */,
|
||||
26D4E6262220731800A67B67 /* events.json */,
|
||||
26D4E62A2220731800A67B67 /* file.json */,
|
||||
26D4E6272220731800A67B67 /* group.json */,
|
||||
26D4E6232220731700A67B67 /* im.json */,
|
||||
26D4E6212220731700A67B67 /* mpim.json */,
|
||||
9EEC459522BE63F800206AC3 /* reply.json */,
|
||||
26D4E6292220731800A67B67 /* rtm.connect.json */,
|
||||
26D4E6202220731700A67B67 /* rtm.start.json */,
|
||||
26D4E6242220731800A67B67 /* user.json */,
|
||||
@@ -1185,6 +1206,7 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2601B6CD2223038A00F197AB /* channel.json in Resources */,
|
||||
9EA45FB922C01290006A6D36 /* action.json in Resources */,
|
||||
2601B710222F766D00F197AB /* member_left_channel.json in Resources */,
|
||||
2601B6CE2223038A00F197AB /* conversation.json in Resources */,
|
||||
2601B70F222F766D00F197AB /* member_joined_channel.json in Resources */,
|
||||
@@ -1195,8 +1217,11 @@
|
||||
2601B6D32223038A00F197AB /* mpim.json in Resources */,
|
||||
2601B6D42223038A00F197AB /* rtm.connect.json in Resources */,
|
||||
2601B6D52223038A00F197AB /* rtm.start.json in Resources */,
|
||||
9EEC459822BE789600206AC3 /* attachmentfield.json in Resources */,
|
||||
2601B6D62223038A00F197AB /* user.json in Resources */,
|
||||
9EEC459622BE63F800206AC3 /* reply.json in Resources */,
|
||||
2601B6D72223038A00F197AB /* usergroup.json in Resources */,
|
||||
9EE6A7C322C2CDD6002BD111 /* edited.json in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1262,6 +1287,8 @@
|
||||
263B103D21FE33A000AF9EF9 /* Action.swift in Sources */,
|
||||
263B102D21FE33A000AF9EF9 /* Item.swift in Sources */,
|
||||
263B103521FE33A000AF9EF9 /* Extensions.swift in Sources */,
|
||||
A20D15EA22DE158000044CFC /* BlockComposition.swift in Sources */,
|
||||
A20D15EB22DE158000044CFC /* BlockElement.swift in Sources */,
|
||||
263B103621FE33A000AF9EF9 /* Bot.swift in Sources */,
|
||||
263B102921FE33A000AF9EF9 /* Edited.swift in Sources */,
|
||||
263B102721FE33A000AF9EF9 /* Attachment.swift in Sources */,
|
||||
@@ -1269,6 +1296,7 @@
|
||||
263B103C21FE33A000AF9EF9 /* SlackError.swift in Sources */,
|
||||
263B103221FE33A000AF9EF9 /* Team.swift in Sources */,
|
||||
263B103321FE33A000AF9EF9 /* User.swift in Sources */,
|
||||
A20D15EC22DE158000044CFC /* BlockLayout.swift in Sources */,
|
||||
263B103021FE33A000AF9EF9 /* Channel.swift in Sources */,
|
||||
263B102621FE33A000AF9EF9 /* AttachmentField.swift in Sources */,
|
||||
263B102B21FE33A000AF9EF9 /* Comment.swift in Sources */,
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"name": "any name",
|
||||
"text": "any text",
|
||||
"type": "any type",
|
||||
"value": "any value",
|
||||
"url": "any url",
|
||||
"style": "primary",
|
||||
"confirm":
|
||||
{
|
||||
"title": "any title",
|
||||
"text": "any text",
|
||||
"ok_text": "any ok text",
|
||||
"dismiss_text": "any dismiss text"
|
||||
},
|
||||
"options":
|
||||
[
|
||||
{
|
||||
"text": "any text 1",
|
||||
"value": "any value 1"
|
||||
},
|
||||
{
|
||||
"text": "any text 2",
|
||||
"value": "any value 2"
|
||||
}
|
||||
],
|
||||
"data_source": "channels"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"title": "any title",
|
||||
"value": "any value",
|
||||
"short": false
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"user": "U0CJ5PC7L",
|
||||
"ts": "1448262357.000002"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"user": "U0CJ5PC7L",
|
||||
"ts": "1448262357.000002"
|
||||
}
|
||||
@@ -44,6 +44,10 @@ final class SKCoreTests: XCTestCase {
|
||||
static let user = try! Data(contentsOf: URL(fileURLWithPath: "\(rootPath)/user.json"))
|
||||
static let usergroup = try! Data(contentsOf: URL(fileURLWithPath: "\(rootPath)/usergroup.json"))
|
||||
static let events = try! Data(contentsOf: URL(fileURLWithPath: "\(rootPath)/events.json"))
|
||||
static let action = try! Data(contentsOf: URL(fileURLWithPath: "\(rootPath)/action.json"))
|
||||
static let attachmentfield = try! Data(contentsOf: URL(fileURLWithPath: "\(rootPath)/attachmentfield.json"))
|
||||
static let edited = try! Data(contentsOf: URL(fileURLWithPath: "\(rootPath)/edited.json"))
|
||||
static let reply = try! Data(contentsOf: URL(fileURLWithPath: "\(rootPath)/reply.json"))
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
@@ -55,7 +59,10 @@ final class SKCoreTests: XCTestCase {
|
||||
("TestMpim", testMpim),
|
||||
("testUser", testUser),
|
||||
("testUserGroup", testUserGroup),
|
||||
("testEvents", testEvents)
|
||||
("testEvents", testEvents),
|
||||
("testActionCodable", testActionCodable),
|
||||
("testAttachmentFieldCodable", testAttachmentFieldCodable),
|
||||
("testReplyCodable", testReplyCodable)
|
||||
]
|
||||
|
||||
func testEvents() {
|
||||
@@ -146,4 +153,87 @@ final class SKCoreTests: XCTestCase {
|
||||
let userGroup = UserGroup(userGroup: json)
|
||||
XCTAssertNotNil(userGroup)
|
||||
}
|
||||
|
||||
func testActionCodable() {
|
||||
let data = JSONData.action
|
||||
let decoder = JSONDecoder()
|
||||
let actionByDecoder = try? decoder.decode(Action.self, from: data)
|
||||
XCTAssertNotNil(actionByDecoder)
|
||||
XCTAssertNotNil(actionByDecoder!.name)
|
||||
XCTAssertNotNil(actionByDecoder!.text)
|
||||
XCTAssertNotNil(actionByDecoder!.type)
|
||||
XCTAssertNotNil(actionByDecoder!.value)
|
||||
XCTAssertNotNil(actionByDecoder!.url)
|
||||
XCTAssertNotNil(actionByDecoder!.style)
|
||||
XCTAssertNotNil(actionByDecoder!.confirm)
|
||||
XCTAssertNotNil(actionByDecoder!.options)
|
||||
XCTAssertNotNil(actionByDecoder!.dataSource)
|
||||
let encoder = JSONEncoder()
|
||||
let jsonData = try? encoder.encode(actionByDecoder!)
|
||||
XCTAssertNotNil(jsonData)
|
||||
let action = try? JSONSerialization.jsonObject(with: jsonData!, options: []) as? [String: Any]
|
||||
XCTAssertNotNil(action)
|
||||
let actionBySerialization = Action(action: action)
|
||||
XCTAssertEqual(actionBySerialization.name, actionByDecoder!.name)
|
||||
XCTAssertEqual(actionBySerialization.text, actionByDecoder!.text)
|
||||
XCTAssertEqual(actionBySerialization.type, actionByDecoder!.type)
|
||||
XCTAssertEqual(actionBySerialization.value, actionByDecoder!.value)
|
||||
XCTAssertEqual(actionBySerialization.url, actionByDecoder!.url)
|
||||
XCTAssertEqual(actionBySerialization.style, actionByDecoder!.style)
|
||||
XCTAssertEqual(actionBySerialization.options?.count, actionByDecoder!.options?.count)
|
||||
XCTAssertEqual(actionBySerialization.dataSource, actionByDecoder!.dataSource)
|
||||
}
|
||||
|
||||
func testAttachmentFieldCodable() {
|
||||
let data = JSONData.attachmentfield
|
||||
let decoder = JSONDecoder()
|
||||
let attachmentFieldByDecoder = try? decoder.decode(AttachmentField.self, from: data)
|
||||
XCTAssertNotNil(attachmentFieldByDecoder)
|
||||
XCTAssertNotNil(attachmentFieldByDecoder!.title)
|
||||
XCTAssertNotNil(attachmentFieldByDecoder!.value)
|
||||
XCTAssertNotNil(attachmentFieldByDecoder!.short)
|
||||
let encoder = JSONEncoder()
|
||||
let jsonData = try? encoder.encode(attachmentFieldByDecoder!)
|
||||
XCTAssertNotNil(jsonData)
|
||||
let field = try? JSONSerialization.jsonObject(with: jsonData!, options: []) as? [String: Any]
|
||||
XCTAssertNotNil(field)
|
||||
let attachmentFieldBySerialization = AttachmentField(field: field)
|
||||
XCTAssertEqual(attachmentFieldBySerialization.title, attachmentFieldByDecoder!.title)
|
||||
XCTAssertEqual(attachmentFieldBySerialization.value, attachmentFieldByDecoder!.value)
|
||||
XCTAssertEqual(attachmentFieldBySerialization.short, attachmentFieldByDecoder!.short)
|
||||
}
|
||||
|
||||
func testEditedCodable() {
|
||||
let data = JSONData.edited
|
||||
let decoder = JSONDecoder()
|
||||
let editedByDecoder = try? decoder.decode(Edited.self, from: data)
|
||||
XCTAssertNotNil(editedByDecoder)
|
||||
XCTAssertNotNil(editedByDecoder!.user)
|
||||
XCTAssertNotNil(editedByDecoder!.ts)
|
||||
let encoder = JSONEncoder()
|
||||
let jsonData = try? encoder.encode(editedByDecoder!)
|
||||
XCTAssertNotNil(jsonData)
|
||||
let edited = try? JSONSerialization.jsonObject(with: jsonData!, options: []) as? [String: Any]
|
||||
XCTAssertNotNil(edited)
|
||||
let editedBySerialization = Edited(edited: edited)
|
||||
XCTAssertEqual(editedBySerialization.user, editedByDecoder!.user)
|
||||
XCTAssertEqual(editedBySerialization.ts, editedByDecoder!.ts)
|
||||
}
|
||||
|
||||
func testReplyCodable() {
|
||||
let data = JSONData.reply
|
||||
let decoder = JSONDecoder()
|
||||
let replyByDecoder = try? decoder.decode(Reply.self, from: data)
|
||||
XCTAssertNotNil(replyByDecoder)
|
||||
XCTAssertNotNil(replyByDecoder!.user)
|
||||
XCTAssertNotNil(replyByDecoder!.ts)
|
||||
let encoder = JSONEncoder()
|
||||
let jsonData = try? encoder.encode(replyByDecoder!)
|
||||
XCTAssertNotNil(jsonData)
|
||||
let reply = try? JSONSerialization.jsonObject(with: jsonData!, options: []) as? [String: Any]
|
||||
XCTAssertNotNil(reply)
|
||||
let replyBySerialization = Reply(reply: reply)
|
||||
XCTAssertEqual(replyBySerialization.user, replyByDecoder!.user)
|
||||
XCTAssertEqual(replyBySerialization.ts, replyByDecoder!.ts)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@ extension SKCoreTests {
|
||||
("testMpim", testMpim),
|
||||
("testUser", testUser),
|
||||
("testUserGroup", testUserGroup),
|
||||
("testActionCodable", testActionCodable),
|
||||
("testAttachmentFieldCodable", testAttachmentFieldCodable),
|
||||
("testEditedCodable", testEditedCodable),
|
||||
("testReplyCodable", testReplyCodable),
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
+15
-15
@@ -10,7 +10,7 @@ jobs:
|
||||
# Builds
|
||||
- job: macOS
|
||||
pool:
|
||||
vmImage: 'macOS-10.13'
|
||||
vmImage: 'macOS-10.14'
|
||||
steps:
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
@@ -18,19 +18,19 @@ jobs:
|
||||
scheme: 'SlackKit'
|
||||
sdk: 'macosx'
|
||||
configuration: 'Release'
|
||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: '10'
|
||||
xcWorkspacePath: 'SlackKit.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: 'default'
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
actions: 'test'
|
||||
scheme: 'SlackKitTests'
|
||||
sdk: 'macosx'
|
||||
configuration: 'Debug'
|
||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: '10'
|
||||
xcWorkspacePath: 'SlackKit.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: 'default'
|
||||
- job: iOS
|
||||
pool:
|
||||
vmImage: 'macOS-10.13'
|
||||
vmImage: 'macOS-10.14'
|
||||
steps:
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
@@ -38,19 +38,19 @@ jobs:
|
||||
scheme: 'SlackKit'
|
||||
sdk: 'iphoneos'
|
||||
configuration: 'Release'
|
||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: '10'
|
||||
xcWorkspacePath: 'SlackKit.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: 'default'
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
actions: 'test'
|
||||
scheme: 'SlackKitTests'
|
||||
sdk: 'iphonesimulator'
|
||||
configuration: 'Debug'
|
||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: '10'
|
||||
xcWorkspacePath: 'SlackKit.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: 'default'
|
||||
- job: tvOS
|
||||
pool:
|
||||
vmImage: 'macOS-10.13'
|
||||
vmImage: 'macOS-10.14'
|
||||
steps:
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
@@ -58,16 +58,16 @@ jobs:
|
||||
scheme: 'SlackKit'
|
||||
sdk: 'appletvos'
|
||||
configuration: 'Release'
|
||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: '10'
|
||||
xcWorkspacePath: 'SlackKit.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: 'default'
|
||||
- task: Xcode@5
|
||||
inputs:
|
||||
actions: 'test'
|
||||
scheme: 'SlackKitTests'
|
||||
sdk: 'appletvsimulator'
|
||||
configuration: 'Debug'
|
||||
xcWorkspacePath: '**/*.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: '10'
|
||||
xcWorkspacePath: 'SlackKit.xcodeproj/project.xcworkspace'
|
||||
xcodeVersion: 'default'
|
||||
- job: Linux
|
||||
pool:
|
||||
vmImage: 'ubuntu-16.04'
|
||||
|
||||
Reference in New Issue
Block a user