diff --git a/WordPressKit.podspec b/WordPressKit.podspec index db1af666..70cedba3 100644 --- a/WordPressKit.podspec +++ b/WordPressKit.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "WordPressKit" - s.version = "4.5.3-beta.1" + s.version = "4.5.3-beta.2" s.summary = "WordPressKit offers a clean and simple WordPress.com and WordPress.org API." s.description = <<-DESC diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index 427bb712..74106bb9 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -17,6 +17,9 @@ 17CE77F420C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */; }; 1A4F98672279A87D00D86E8E /* WPKit-Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */; settings = {ATTRIBUTES = (Private, ); }; }; 240315B0A1D6C2B981572B5B /* Pods_WordPressKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED05C8FF3E61D93CE5BA527E /* Pods_WordPressKitTests.framework */; }; + 32A29A1F236BE4CC009488C2 /* post-autosave-mapping-success.json in Resources */ = {isa = PBXBuildFile; fileRef = 32A29A1E236BE4CC009488C2 /* post-autosave-mapping-success.json */; }; + 32AF21E3236DEB3C001C6502 /* PostServiceRemoteRESTAutosaveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32AF21E2236DEB3C001C6502 /* PostServiceRemoteRESTAutosaveTests.swift */; }; + 32E1DD23236AA09A008914B0 /* RemotePostAutosave.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32E1DD22236AA09A008914B0 /* RemotePostAutosave.swift */; }; 40247DFA2120D8E100AE1C3C /* AutomatedTransferService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */; }; 40247DFC2120E69600AE1C3C /* AutomatedTransferStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */; }; 404057C5221B30400060250C /* StatsSearchTermTimeIntervalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */; }; @@ -488,6 +491,9 @@ 17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSiteSearchServiceRemoteTests.swift; sourceTree = ""; }; 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WPKit-Swift.h"; sourceTree = ""; }; 264F5C834541BBF2018F4964 /* Pods-WordPressKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-WordPressKitTests/Pods-WordPressKitTests.debug.xcconfig"; sourceTree = ""; }; + 32A29A1E236BE4CC009488C2 /* post-autosave-mapping-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "post-autosave-mapping-success.json"; sourceTree = ""; }; + 32AF21E2236DEB3C001C6502 /* PostServiceRemoteRESTAutosaveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostServiceRemoteRESTAutosaveTests.swift; sourceTree = ""; }; + 32E1DD22236AA09A008914B0 /* RemotePostAutosave.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemotePostAutosave.swift; sourceTree = ""; }; 40247DF92120D8E100AE1C3C /* AutomatedTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferService.swift; sourceTree = ""; }; 40247DFB2120E69600AE1C3C /* AutomatedTransferStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutomatedTransferStatus.swift; sourceTree = ""; }; 404057C4221B30400060250C /* StatsSearchTermTimeIntervalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatsSearchTermTimeIntervalData.swift; sourceTree = ""; }; @@ -1041,9 +1047,10 @@ 740B23D41F17F6D200067A2A /* Post */ = { isa = PBXGroup; children = ( + 32AF21E2236DEB3C001C6502 /* PostServiceRemoteRESTAutosaveTests.swift */, + 9AB6D649218727D60008F274 /* PostServiceRemoteRESTRevisionsTest.swift */, 740B23D01F17F6BB00067A2A /* PostServiceRemoteRESTTests.m */, 740B23D11F17F6BB00067A2A /* PostServiceRemoteXMLRPCTests.swift */, - 9AB6D649218727D60008F274 /* PostServiceRemoteRESTRevisionsTest.swift */, ); name = Post; sourceTree = ""; @@ -1455,6 +1462,7 @@ E1D6B555200E46F200325669 /* WPTimeZone.swift */, E632D7771F6E047400297F6D /* SocialLogin2FANonceInfo.swift */, 17CE77F020C6EB41001DEA5A /* ReaderFeed.swift */, + 32E1DD22236AA09A008914B0 /* RemotePostAutosave.swift */, ); name = Models; sourceTree = ""; @@ -1571,6 +1579,7 @@ 40E4698C2017D2E30030DB5F /* plugin-directory-new.json */, 40E4698A2017C2840030DB5F /* plugin-directory-popular.json */, E1E89C651FD6B291006E7A33 /* plugin-directory-rename-xml-rpc.json */, + 32A29A1E236BE4CC009488C2 /* post-autosave-mapping-success.json */, 9AB6D648218722BB0008F274 /* post-revisions-success.json */, 9AB6D64D218731380008F274 /* post-revisions-mapping-success.json */, 9AB6D64C218730130008F274 /* post-revisions-failure.json */, @@ -1932,6 +1941,8 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32A29A1F236BE4CC009488C2 /* post-autosave-mapping-success.json in Resources */, + 93AC8EC61ED32FD000900F5A /* stats-v1.1-alltime.json in Resources */, 7403A2FC1EF06FEB00DED7DC /* me-settings-change-lastname-success.json in Resources */, 40F88F601F85723500AE3FAF /* auth-send-verification-email-already-verified-failure.json in Resources */, 74D67F211F15C3240010C5ED /* people-validate-invitation-success.json in Resources */, @@ -2209,6 +2220,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 32E1DD23236AA09A008914B0 /* RemotePostAutosave.swift in Sources */, + 9368C7B41EC630270092CE8E /* StatsItemAction.m in Sources */, 7430C9B41F1927C50051B8E6 /* RemoteReaderSite.m in Sources */, 74585B8E1F0D51A100E7E667 /* DomainsServiceRemote.swift in Sources */, 7430C9A41F1927180051B8E6 /* ReaderPostServiceRemote.m in Sources */, @@ -2385,6 +2398,7 @@ E13EE14C1F332C4400C15787 /* PluginServiceRemoteTests.swift in Sources */, 736C971021E80D48007A4200 /* SiteVerticalsPromptResponseDecodingTests.swift in Sources */, 74B335D81F06F1CA0053A184 /* MockWordPressComRestApi.swift in Sources */, + 32AF21E3236DEB3C001C6502 /* PostServiceRemoteRESTAutosaveTests.swift in Sources */, 74B5F0DE1EF82A9600B411E7 /* BlogServiceRemoteRESTTests.m in Sources */, 74E2294B1F1E73340085F7F2 /* SharingServiceRemoteTests.m in Sources */, 73B3DAD621FBB20D00B2CF18 /* WordPressComRestApiTests+Locale.swift in Sources */, diff --git a/WordPressKit/PostServiceRemoteOptions.h b/WordPressKit/PostServiceRemoteOptions.h index 3c33a82a..d568b204 100644 --- a/WordPressKit/PostServiceRemoteOptions.h +++ b/WordPressKit/PostServiceRemoteOptions.h @@ -72,4 +72,9 @@ typedef NS_ENUM(NSUInteger, PostServiceResultsOrdering) { */ - (NSString *)search; +/** + The metadata to include in the returned results. + */ +- (NSString *)meta; + @end diff --git a/WordPressKit/PostServiceRemoteREST.m b/WordPressKit/PostServiceRemoteREST.m index f4b50e48..2e8bf1e6 100644 --- a/WordPressKit/PostServiceRemoteREST.m +++ b/WordPressKit/PostServiceRemoteREST.m @@ -15,6 +15,7 @@ static NSString * const RemoteOptionKeyStatus = @"status"; static NSString * const RemoteOptionKeySearch = @"search"; static NSString * const RemoteOptionKeyAuthor = @"author"; +static NSString * const RemoteOptionKeyMeta = @"meta"; static NSString * const RemoteOptionValueOrderAscending = @"ASC"; static NSString * const RemoteOptionValueOrderDescending = @"DESC"; @@ -365,6 +366,9 @@ - (NSDictionary *)dictionaryWithRemoteOptions:(id )opt if (options.search.length > 0) { [remoteParams setObject:options.search forKey:RemoteOptionKeySearch]; } + if (options.meta.length > 0) { + [remoteParams setObject:options.meta forKey:RemoteOptionKeyMeta]; + } return remoteParams.count ? [NSDictionary dictionaryWithDictionary:remoteParams] : nil; } @@ -432,7 +436,21 @@ - (RemotePost *)remotePostFromJSONDictionary:(NSDictionary *)jsonPost { post.tags = [self tagNamesFromJSONDictionary:jsonPost[@"tags"]]; post.revisions = [jsonPost arrayForKey:@"revisions"]; - + + NSDictionary *autosaveAttributes = jsonPost[@"meta"][@"data"][@"autosave"]; + if ([autosaveAttributes wp_isValidObject]) { + RemotePostAutosave *autosave = [[RemotePostAutosave alloc] init]; + autosave.title = autosaveAttributes[@"title"]; + autosave.content = autosaveAttributes[@"content"]; + autosave.excerpt = autosaveAttributes[@"excerpt"]; + autosave.modifiedDate = [NSDate dateWithWordPressComJSONString:autosaveAttributes[@"modified"]]; + autosave.identifier = autosaveAttributes[@"ID"]; + autosave.authorID = autosaveAttributes[@"author_ID"]; + autosave.postID = autosaveAttributes[@"post_ID"]; + autosave.previewURL = autosaveAttributes[@"preview_URL"]; + post.autosave = autosave; + } + // Pick an image to use for display if (post.postThumbnailPath) { post.pathForDisplayImage = post.postThumbnailPath; diff --git a/WordPressKit/RemotePost.h b/WordPressKit/RemotePost.h index 98c37e0f..d4fa0ac2 100644 --- a/WordPressKit/RemotePost.h +++ b/WordPressKit/RemotePost.h @@ -1,4 +1,5 @@ #import +@class RemotePostAutosave; extern NSString * const PostStatusDraft; extern NSString * const PostStatusPending; @@ -35,6 +36,13 @@ extern NSString * const PostStatusDeleted; @property (nonatomic, strong) NSString *type; @property (nonatomic, strong) NSString *format; +/** +* A snapshot of the post at the last autosave. +* +* This is nullable. +*/ +@property (nonatomic, strong) RemotePostAutosave *autosave; + @property (nonatomic, strong) NSNumber *commentCount; @property (nonatomic, strong) NSNumber *likeCount; diff --git a/WordPressKit/RemotePostAutosave.swift b/WordPressKit/RemotePostAutosave.swift new file mode 100644 index 00000000..fcc7c382 --- /dev/null +++ b/WordPressKit/RemotePostAutosave.swift @@ -0,0 +1,15 @@ +import Foundation + +/// Encapsulates the autosave attributes of a post. +@objc +@objcMembers +public class RemotePostAutosave: NSObject { + public var title: String? + public var excerpt: String? + public var content: String? + public var modifiedDate: Date? + public var identifier: NSNumber? + public var authorID: String? + public var postID: NSNumber? + public var previewURL: String? +} diff --git a/WordPressKitTests/Mock Data/post-autosave-mapping-success.json b/WordPressKitTests/Mock Data/post-autosave-mapping-success.json new file mode 100644 index 00000000..648320ab --- /dev/null +++ b/WordPressKitTests/Mock Data/post-autosave-mapping-success.json @@ -0,0 +1,38 @@ +{ + "found": 3, + "posts": [ + { + "modified": "2019-10-28T02:05:52+00:00", + "title": "Hola, mundo!", + "status": "publish", + "meta": { + "links": { + "self": "https://public-api.wordpress.com/rest/v1.1/sites/168297153/posts/102", + "help": "https://public-api.wordpress.com/rest/v1.1/sites/168297153/posts/102/help", + "site": "https://public-api.wordpress.com/rest/v1.1/sites/168297153", + "replies": "https://public-api.wordpress.com/rest/v1.1/sites/168297153/posts/102/replies/", + "likes": "https://public-api.wordpress.com/rest/v1.1/sites/168297153/posts/102/likes/", + "autosave": "https://public-api.wordpress.com/rest/v1.1/sites/168297153/posts/102/autosave" + }, + "data": { + "autosave": { + "ID": 100, + "author_ID": "12345678", + "post_ID": 102, + "title": "Hello, world!", + "content": "\n

Uno.

\n", + "excerpt": "abc", + "preview_URL": "https://hello.wordpress.com/2019/10/28/hello-world/?preview=true&preview_nonce=07346f4e5d", + "modified": "2019-10-28T02:06:39+00:00" + } + } + } + } + ], + "meta": { + "links": { + "counts": "https://public-api.wordpress.com/rest/v1.1/sites/168297153/post-counts/post" + }, + "wpcom": true + } +} diff --git a/WordPressKitTests/PostServiceRemoteRESTAutosaveTests.swift b/WordPressKitTests/PostServiceRemoteRESTAutosaveTests.swift new file mode 100644 index 00000000..bcd8acda --- /dev/null +++ b/WordPressKitTests/PostServiceRemoteRESTAutosaveTests.swift @@ -0,0 +1,54 @@ +import Foundation +import XCTest + +@testable import WordPressKit + + +class PostServiceRemoteRESTAutosaveTests: RemoteTestCase, RESTTestable { + private let performPostsAutosaveSuccessFilename = "post-autosave-mapping-success.json" + private let siteId = 0 + private var remote: PostServiceRemoteREST! + private var postsEndpoint: String { + return "sites/\(siteId)/posts" + } + + override func setUp() { + super.setUp() + remote = PostServiceRemoteREST(wordPressComRestApi: getRestApi(), siteID: NSNumber(value: siteId)) + } + + override func tearDown() { + super.tearDown() + remote = nil + } + + + // MARK: Perform tests + + func testFetchPostsPerformsAutosaveMappingSuccessfully() { + let expect = expectation(description: "Fetch posts fetches autosaves successfully") + + stubRemoteResponse(postsEndpoint, filename: performPostsAutosaveSuccessFilename, contentType: .ApplicationJSON) + remote.getPostsOfType("", options: [:], success: { remotePosts in + + guard let remotePost = remotePosts?.first else { + XCTFail("Failed to retrieve mock post") + return + } + + XCTAssertEqual(remotePost.autosave.identifier?.intValue, 100) + XCTAssertEqual(remotePost.autosave.authorID, "12345678") + XCTAssertEqual(remotePost.autosave.postID, 102) + XCTAssertEqual(remotePost.autosave.title, "Hello, world!") + XCTAssertEqual(remotePost.autosave.content, "\n

Uno.

\n") + XCTAssertEqual(remotePost.autosave.excerpt, "abc") + XCTAssertEqual(remotePost.autosave.previewURL, "https://hello.wordpress.com/2019/10/28/hello-world/?preview=true&preview_nonce=07346f4e5d") + XCTAssertEqual(remotePost.autosave.modifiedDate, NSDate(wordPressComJSONString: "2019-10-28T02:06:39+00:00") as Date?) + expect.fulfill() + }, failure: { _ in + XCTFail("This callback shouldn't get called") + expect.fulfill() + }) + waitForExpectations(timeout: timeout, handler: nil) + } +}