Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Comments: Fetch site comments from core V2 endpoint #463

Merged
merged 8 commits into from
Nov 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion WordPressKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Pod::Spec.new do |s|
s.name = 'WordPressKit'
s.version = '4.42.3'
s.version = '4.43.0-beta.1'

s.summary = 'WordPressKit offers a clean and simple WordPress.com and WordPress.org API.'
s.description = <<-DESC
Expand Down
24 changes: 24 additions & 0 deletions WordPressKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,12 @@
FAD1344B259094C300A8FEB1 /* JetpackBackup.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD1344A259094C300A8FEB1 /* JetpackBackup.swift */; };
FAD1345125909DEA00A8FEB1 /* JetpackBackupServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD1345025909DEA00A8FEB1 /* JetpackBackupServiceRemoteTests.swift */; };
FEB7A88F271873BD00A8CF85 /* reader-post-comments-update-notification-success.json in Resources */ = {isa = PBXBuildFile; fileRef = FEB7A88E271873BD00A8CF85 /* reader-post-comments-update-notification-success.json */; };
FEE4EF57272FDD4B003CDA3C /* RemoteCommentV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE4EF56272FDD4B003CDA3C /* RemoteCommentV2.swift */; };
FEE4EF59272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE4EF58272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift */; };
FEE4EF5B27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEE4EF5A27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift */; };
FEE4EF5D2730315C003CDA3C /* empty-array.json in Resources */ = {isa = PBXBuildFile; fileRef = FEE4EF5C2730315C003CDA3C /* empty-array.json */; };
FEE4EF5F2730334D003CDA3C /* comments-v2-view-context-success.json in Resources */ = {isa = PBXBuildFile; fileRef = FEE4EF5E2730334D003CDA3C /* comments-v2-view-context-success.json */; };
FEE4EF6127303361003CDA3C /* comments-v2-edit-context-success.json in Resources */ = {isa = PBXBuildFile; fileRef = FEE4EF6027303361003CDA3C /* comments-v2-edit-context-success.json */; };
FEFFD99126C1347D00F34231 /* ShareAppContentServiceRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFFD99026C1347D00F34231 /* ShareAppContentServiceRemote.swift */; };
FEFFD99326C141A800F34231 /* RemoteShareAppContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFFD99226C141A800F34231 /* RemoteShareAppContent.swift */; };
FEFFD99726C158F400F34231 /* share-app-content-success.json in Resources */ = {isa = PBXBuildFile; fileRef = FEFFD99626C158F400F34231 /* share-app-content-success.json */; };
Expand Down Expand Up @@ -1223,6 +1229,12 @@
FAD1344A259094C300A8FEB1 /* JetpackBackup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackBackup.swift; sourceTree = "<group>"; };
FAD1345025909DEA00A8FEB1 /* JetpackBackupServiceRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JetpackBackupServiceRemoteTests.swift; sourceTree = "<group>"; };
FEB7A88E271873BD00A8CF85 /* reader-post-comments-update-notification-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "reader-post-comments-update-notification-success.json"; sourceTree = "<group>"; };
FEE4EF56272FDD4B003CDA3C /* RemoteCommentV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteCommentV2.swift; sourceTree = "<group>"; };
FEE4EF58272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommentServiceRemoteREST+ApiV2.swift"; sourceTree = "<group>"; };
FEE4EF5A27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CommentServiceRemoteREST+APIv2Tests.swift"; sourceTree = "<group>"; };
FEE4EF5C2730315C003CDA3C /* empty-array.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "empty-array.json"; sourceTree = "<group>"; };
FEE4EF5E2730334D003CDA3C /* comments-v2-view-context-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "comments-v2-view-context-success.json"; sourceTree = "<group>"; };
FEE4EF6027303361003CDA3C /* comments-v2-edit-context-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "comments-v2-edit-context-success.json"; sourceTree = "<group>"; };
FEFFD99026C1347D00F34231 /* ShareAppContentServiceRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAppContentServiceRemote.swift; sourceTree = "<group>"; };
FEFFD99226C141A800F34231 /* RemoteShareAppContent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteShareAppContent.swift; sourceTree = "<group>"; };
FEFFD99626C158F400F34231 /* share-app-content-success.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "share-app-content-success.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1725,6 +1737,7 @@
74BA04ED1F06DC0A00ED5CD8 /* CommentServiceRemote.h */,
74BA04EE1F06DC0A00ED5CD8 /* CommentServiceRemoteREST.h */,
74BA04EF1F06DC0A00ED5CD8 /* CommentServiceRemoteREST.m */,
FEE4EF58272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift */,
74BA04F01F06DC0A00ED5CD8 /* CommentServiceRemoteXMLRPC.h */,
74BA04F11F06DC0A00ED5CD8 /* CommentServiceRemoteXMLRPC.m */,
74585B8D1F0D51A100E7E667 /* DomainsServiceRemote.swift */,
Expand Down Expand Up @@ -1898,6 +1911,7 @@
4624222C2548BA0F002B8A12 /* RemoteSiteDesign.swift */,
464BAB0A262F6736006AEED5 /* RemoteBlockEditorSettings.swift */,
FEFFD99226C141A800F34231 /* RemoteShareAppContent.swift */,
FEE4EF56272FDD4B003CDA3C /* RemoteCommentV2.swift */,
);
name = Models;
sourceTree = "<group>";
Expand Down Expand Up @@ -1944,6 +1958,8 @@
FA79F1952591809C00D235A9 /* backup-get-backup-status-in-progress-success.json */,
FA79F1862591730D00D235A9 /* backup-prepare-backup-success.json */,
ABD95B8425DD6DA200735BEE /* comment-likes-success.json */,
FEE4EF5E2730334D003CDA3C /* comments-v2-view-context-success.json */,
FEE4EF6027303361003CDA3C /* comments-v2-edit-context-success.json */,
C92EFF7225E7444400E0308D /* common-starter-site-designs-empty-designs.json */,
C92EFF6C25E741E900E0308D /* common-starter-site-designs-malformed.json */,
C92EFF6825E7403F00E0308D /* common-starter-site-designs-success.json */,
Expand All @@ -1952,6 +1968,7 @@
74585B9E1F0D6E7500E7E667 /* domain-service-bad-json.json */,
74585BA01F0D6F5300E7E667 /* domain-service-empty.json */,
FFE247BC20C9C88B002DF3A2 /* empty.json */,
FEE4EF5C2730315C003CDA3C /* empty-array.json */,
930999531F16598A00F006A1 /* get-multiple-themes-v1.2.json */,
930999541F16598A00F006A1 /* get-purchased-themes-v1.1.json */,
930999551F16598A00F006A1 /* get-single-theme-v1.1.json */,
Expand Down Expand Up @@ -2245,6 +2262,7 @@
ABD95B7E25DD6C4B00735BEE /* CommentServiceRemoteRESTLikesTests.swift */,
98F884D326BC6445009ADF57 /* CommentServiceRemoteRESTTests.swift */,
9817D9D326BC8AF000ECBD8C /* CommentServiceRemoteXMLRPCTests.swift */,
FEE4EF5A27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift */,
);
name = Comment;
sourceTree = "<group>";
Expand Down Expand Up @@ -2695,6 +2713,7 @@
7403A3001EF06FEB00DED7DC /* me-settings-success.json in Resources */,
439A44DE2107CF6F00795ED7 /* site-plans-v3-bad-json-failure.json in Resources */,
74B335EA1F06F76B0053A184 /* xmlrpc-response-getpost.xml in Resources */,
FEE4EF6127303361003CDA3C /* comments-v2-edit-context-success.json in Resources */,
740B23E91F17FB4200067A2A /* xmlrpc-wp-getpost-invalid-id-failure.xml in Resources */,
930999561F16598A00F006A1 /* get-multiple-themes-v1.2.json in Resources */,
17BF9A7620C7E18200BF57D2 /* reader-site-search-success-no-data.json in Resources */,
Expand All @@ -2716,6 +2735,7 @@
BA4BFC582498C04D005C83E7 /* plugin-update-jetpack-already-updated.json in Resources */,
740B23E51F17FB4200067A2A /* xmlrpc-metaweblog-newpost-bad-xml-failure.xml in Resources */,
74C473C91EF335B8009918F2 /* site-active-purchases-empty-response.json in Resources */,
FEE4EF5D2730315C003CDA3C /* empty-array.json in Resources */,
93A4232326CBC845004CCA31 /* me-settings-close-account-success.json in Resources */,
740B23EA1F17FB4200067A2A /* xmlrpc-wp-getpost-success.xml in Resources */,
829BA4331FACF187003ADEEA /* activity-rewind-status-restore-queued.json in Resources */,
Expand Down Expand Up @@ -2756,6 +2776,7 @@
74D67F381F15C3740010C5ED /* site-viewers-delete-auth-failure.json in Resources */,
826016FA1F9FAF6300533B6C /* activity-log-success-1.json in Resources */,
9A881752223C01E400A3AB20 /* jetpack-service-error-login-failure.json in Resources */,
FEE4EF5F2730334D003CDA3C /* comments-v2-view-context-success.json in Resources */,
740B23E41F17FB4200067A2A /* xmlrpc-metaweblog-editpost-success.xml in Resources */,
7434E1DE1F17C3C900C40DDB /* site-users-update-role-unknown-user-failure.json in Resources */,
4081977B221F153B00A298E4 /* stats-visits-week.json in Resources */,
Expand Down Expand Up @@ -2972,6 +2993,7 @@
74B5F0E71EF8699C00B411E7 /* RemotePostType.m in Sources */,
93188D1F1F2262BF0028ED4D /* RemotePostTag.m in Sources */,
74D67F081F15BEB70010C5ED /* RemotePerson.swift in Sources */,
FEE4EF59272FF78C003CDA3C /* CommentServiceRemoteREST+ApiV2.swift in Sources */,
984E34EB22EF7209005C3F92 /* StatsFileDownloadsTimeIntervalData.swift in Sources */,
465F88A2263B325C00F4C950 /* ChecksumUtil.swift in Sources */,
40AB1ADA200FED25009B533D /* PluginDirectoryFeedPage.swift in Sources */,
Expand Down Expand Up @@ -3002,6 +3024,7 @@
B5A4822B20AC6C0B009D95F6 /* CocoaLumberjack.swift in Sources */,
B5A4822E20AC6C1A009D95F6 /* WPKitLogging.m in Sources */,
7430C9A61F1927180051B8E6 /* ReaderSiteServiceRemote.m in Sources */,
FEE4EF57272FDD4B003CDA3C /* RemoteCommentV2.swift in Sources */,
7430C9B21F1927C50051B8E6 /* RemoteReaderPost.m in Sources */,
74A44DCD1F13C533006CD8F4 /* PushAuthenticationServiceRemote.swift in Sources */,
742362DF1F1025B400BD0A7F /* RemoteMenu.m in Sources */,
Expand Down Expand Up @@ -3077,6 +3100,7 @@
74B335D81F06F1CA0053A184 /* MockWordPressComRestApi.swift in Sources */,
32AF21E3236DEB3C001C6502 /* PostServiceRemoteRESTAutosaveTests.swift in Sources */,
3236F79A24AE406D0088E8F3 /* ReaderTopicServiceRemote+InterestsTests.swift in Sources */,
FEE4EF5B27302317003CDA3C /* CommentServiceRemoteREST+APIv2Tests.swift in Sources */,
74B5F0DE1EF82A9600B411E7 /* BlogServiceRemoteRESTTests.m in Sources */,
ABD95B7F25DD6C4B00735BEE /* CommentServiceRemoteRESTLikesTests.swift in Sources */,
8B749E8225AF7DDA00023F03 /* JetpackCapabilitiesServiceRemoteTests.swift in Sources */,
Expand Down
68 changes: 68 additions & 0 deletions WordPressKit/CommentServiceRemoteREST+ApiV2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
public extension CommentServiceRemoteREST {
/// Lists the available keys for the request parameter.
enum RequestKeys: String {
/// The parent comment's ID. In API v2, supplying this parameter filters the list to only contain
/// the child/reply comments of the specified ID.
case parent

/// The dotcom user ID of the comment author. In API v2, supplying this parameter filters the list
/// to only contain comments authored by the specified ID.
case author

/// Valid values are `view`, `edit`, or `embed`. When not specified, the default context is `view`.
case context
}

/// Retrieves a list of comments in a site with the specified siteID.
/// - Parameters:
/// - siteID: The ID of the site that contains the specified comment.
/// - parameters: Additional request parameters. Optional.
/// - success: A closure that will be called when the request succeeds.
/// - failure: A closure that will be called when the request fails.
func getCommentsV2(for siteID: Int,
parameters: [RequestKeys: AnyHashable]? = nil,
success: @escaping ([RemoteCommentV2]) -> Void,
failure: @escaping (Error) -> Void) {
let path = coreV2Path(for: "sites/\(siteID)/comments")
let requestParameters: [String: AnyHashable] = {
guard let someParameters = parameters else {
return [:]
}

return someParameters.reduce([String: AnyHashable]()) { result, pair in
var result = result
result[pair.key.rawValue] = pair.value
return result
}
}()

wordPressComRestApi.GET(path, parameters: requestParameters as [String: AnyObject]) { result, _ in
switch result {
case .success(let responseObject):
do {
let data = try JSONSerialization.data(withJSONObject: responseObject, options: [])
let comments = try JSONDecoder().decode([RemoteCommentV2].self, from: data)
success(comments)
} catch {
failure(error)
}

case .failure(let error):
failure(error)
}
}
}

}

// MARK: - Private Helpers

private extension CommentServiceRemoteREST {
struct Constants {
static let coreV2String = "wp/v2"
}

func coreV2Path(for endpoint: String) -> String {
return "\(Constants.coreV2String)/\(endpoint)"
}
}
95 changes: 95 additions & 0 deletions WordPressKit/RemoteCommentV2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/// Captures the JSON structure for Comments returned from API v2 endpoint.
public struct RemoteCommentV2 {
public var commentID: Int
public var postID: Int
public var parentID: Int = 0
public var authorID: Int
public var authorName: String?
public var authorEmail: String? // only available in edit context
public var authorURL: String?
public var authorIP: String? // only available in edit context
public var authorUserAgent: String? // only available in edit context
public var authorAvatarURL: String?
public var date: Date
public var content: String
public var rawContent: String? // only available in edit context
public var link: String
public var status: String
public var type: String
}

// MARK: - Decodable

extension RemoteCommentV2: Decodable {
enum CodingKeys: String, CodingKey {
case id
case post
case parent
case author
case authorName = "author_name"
case authorEmail = "author_email"
case authorURL = "author_url"
case authorIP = "author_ip"
case authorUserAgent = "author_user_agent"
case date = "date_gmt" // take the gmt version, as the other `date` parameter doesn't provide timezone information.
case content
case authorAvatarURLs = "author_avatar_urls"
case link
case status
case type
}

enum ContentCodingKeys: String, CodingKey {
case rendered
case raw
}

enum AuthorAvatarCodingKeys: String, CodingKey {
case size96 = "96" // this is the default size for avatar URL in API v1.1.
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

self.commentID = try container.decode(Int.self, forKey: .id)
self.postID = try container.decode(Int.self, forKey: .post)
self.parentID = try container.decode(Int.self, forKey: .parent)
self.authorID = try container.decode(Int.self, forKey: .author)
self.authorName = try container.decode(String.self, forKey: .authorName)
self.authorEmail = try container.decodeIfPresent(String.self, forKey: .authorEmail)
self.authorURL = try container.decode(String.self, forKey: .authorURL)
self.authorIP = try container.decodeIfPresent(String.self, forKey: .authorIP)
self.authorUserAgent = try container.decodeIfPresent(String.self, forKey: .authorUserAgent)
self.link = try container.decode(String.self, forKey: .link)
self.type = try container.decode(String.self, forKey: .type)

// since `date_gmt` is already in GMT timezone, manually add the timezone string to make the rfc3339 formatter happy (or it will throw otherwise).
guard let dateString = try? container.decode(String.self, forKey: .date),
let date = NSDate(wordPressComJSONString: dateString + "+00:00") as Date? else {
throw DecodingError.dataCorruptedError(forKey: .date, in: container, debugDescription: "Date parsing failed")
}
self.date = date

let contentContainer = try container.nestedContainer(keyedBy: ContentCodingKeys.self, forKey: .content)
self.rawContent = try contentContainer.decodeIfPresent(String.self, forKey: .raw)
self.content = try contentContainer.decode(String.self, forKey: .rendered)

let remoteStatus = try container.decode(String.self, forKey: .status)
self.status = Self.status(from: remoteStatus)

let avatarContainer = try container.nestedContainer(keyedBy: AuthorAvatarCodingKeys.self, forKey: .authorAvatarURLs)
self.authorAvatarURL = try avatarContainer.decode(String.self, forKey: .size96)
}

/// Maintain parity with the client-side comment statuses. Refer to `CommentServiceRemoteREST:statusWithRemoteStatus`.
private static func status(from remoteStatus: String) -> String {
switch remoteStatus {
case "unapproved":
return "hold"
case "approved":
return "approve"
default:
return remoteStatus
}
}
}
Loading