From 39665cae48efdc3b4f595cc449bd497a2deeb1d1 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 28 Feb 2023 15:31:29 +0100 Subject: [PATCH 01/17] Implement VideoPress token fetch --- WordPressKit/MediaServiceRemote.h | 11 +++++++++ WordPressKit/MediaServiceRemoteREST.m | 33 +++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/WordPressKit/MediaServiceRemote.h b/WordPressKit/MediaServiceRemote.h index 50054ca0..4da14be6 100644 --- a/WordPressKit/MediaServiceRemote.h +++ b/WordPressKit/MediaServiceRemote.h @@ -69,5 +69,16 @@ success:(void (^)(NSURL *videoURL, NSURL *posterURL))success failure:(void (^)(NSError *))failure; +/** + Retrieves the VideoPress token for the request videoPressID. + The token is required to play private VideoPress videos. + + @param videoPressID the videoPressID to search for. + @param success a block to be executed if the the token is fetched successfully for the VideoPress video. + @param failure a block to be executed if the token can't be fetched for the VideoPress video. + */ +-(void)getVideoPressToken:(NSString *)videoPressID + success:(void (^)(NSString *token))success + failure:(void (^)(NSError *))failure; @end diff --git a/WordPressKit/MediaServiceRemoteREST.m b/WordPressKit/MediaServiceRemoteREST.m index c5e6f50e..235cd81d 100644 --- a/WordPressKit/MediaServiceRemoteREST.m +++ b/WordPressKit/MediaServiceRemoteREST.m @@ -344,6 +344,39 @@ -(void)getVideoURLFromVideoPressID:(NSString *)videoPressID }]; } +-(void)getVideoPressToken:(NSString *)videoPressID + success:(void (^)(NSString *token))success + failure:(void (^)(NSError *))failure +{ + + NSString *path = [NSString stringWithFormat:@"sites/%@/media/videopress-playback-jwt/%@", self.siteID, videoPressID]; + NSString *requestUrl = [self pathForEndpoint:path + withVersion:ServiceRemoteWordPressComRESTApiVersion_2_0]; + + [self.wordPressComRestApi POST:requestUrl + parameters:nil + success:^(id responseObject, NSHTTPURLResponse *httpResponse) { + NSDictionary *response = (NSDictionary *)responseObject; + NSString *token = [response stringForKey:@"metadata_token"]; + if (token) { + if (success) { + success(token); + } + } else { + if (failure) { + NSError *error = [NSError errorWithDomain:WordPressComRestApiErrorDomain + code:WordPressComRestApiErrorUnknown + userInfo:nil]; + failure(error); + } + } + } failure:^(NSError *error, NSHTTPURLResponse *response) { + if (failure) { + failure(error); + } + }]; +} + + (NSArray *)remoteMediaFromJSONArray:(NSArray *)jsonMedia { return [jsonMedia wp_map:^id(NSDictionary *json) { From 905d47b13ae7a05c1141cd31e0e4759f605cec3c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 28 Feb 2023 15:36:59 +0100 Subject: [PATCH 02/17] Bump podspec to 6.2.0-beta.1 --- WordPressKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressKit.podspec b/WordPressKit.podspec index 3c1528a2..a00a5277 100644 --- a/WordPressKit.podspec +++ b/WordPressKit.podspec @@ -2,7 +2,7 @@ Pod::Spec.new do |s| s.name = 'WordPressKit' - s.version = '6.1.0' + s.version = '6.2.0-beta.1' s.summary = 'WordPressKit offers a clean and simple WordPress.com and WordPress.org API.' s.description = <<-DESC From 25495507cdc015c8188c80c5afc9ffbef5a50eaf Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Tue, 28 Feb 2023 15:58:50 +0100 Subject: [PATCH 03/17] Implement `getVideoPressToken` in XML-RPC --- WordPressKit/MediaServiceRemoteXMLRPC.m | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/WordPressKit/MediaServiceRemoteXMLRPC.m b/WordPressKit/MediaServiceRemoteXMLRPC.m index 7558ba81..83c009c7 100644 --- a/WordPressKit/MediaServiceRemoteXMLRPC.m +++ b/WordPressKit/MediaServiceRemoteXMLRPC.m @@ -246,6 +246,19 @@ -(void)getVideoURLFromVideoPressID:(NSString *)videoPressID } } +-(void)getVideoPressToken:(NSString *)videoPressID + success:(void (^)(NSString *token))success + failure:(void (^)(NSError *))failure +{ + // The endpoint `wpcom/v2/sites//media/videopress-playback-jwt/` is not available in XML-RPC. + if (failure) { + NSError *error = [NSError errorWithDomain:NSURLErrorDomain + code:NSURLErrorUnsupportedURL + userInfo:nil]; + failure(error); + } +} + #pragma mark - Private methods - (NSArray *)remoteMediaFromXMLRPCArray:(NSArray *)xmlrpcArray From 24f79389c87a90734a8ba419f629db92780866c1 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 1 Mar 2023 18:14:14 +0100 Subject: [PATCH 04/17] Add RemoteVideoPressVideo --- WordPressKit.xcodeproj/project.pbxproj | 4 + WordPressKit/RemoteVideoPressVideo.swift | 111 +++++++++++++++++++++++ 2 files changed, 115 insertions(+) create mode 100644 WordPressKit/RemoteVideoPressVideo.swift diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index fd57471e..641facdb 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -19,6 +19,7 @@ 17CE77F420C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */; }; 17D936252475D8AB008B2205 /* RemoteHomepageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D936242475D8AB008B2205 /* RemoteHomepageType.swift */; }; 1A4F98672279A87D00D86E8E /* WPKit-Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */; settings = {ATTRIBUTES = (Private, ); }; }; + 1DAC3D2629AF4F250068FE13 /* RemoteVideoPressVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DAC3D2529AF4F250068FE13 /* RemoteVideoPressVideo.swift */; }; 240315B0A1D6C2B981572B5B /* Pods_WordPressKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED05C8FF3E61D93CE5BA527E /* Pods_WordPressKitTests.framework */; }; 24ADA24E24F9B32D001B5DAE /* FeatureFlagSerializationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24ADA24D24F9B32D001B5DAE /* FeatureFlagSerializationTest.swift */; }; 3236F77824AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3236F77724AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift */; }; @@ -677,6 +678,7 @@ 17CE77F320C701C8001DEA5A /* ReaderSiteSearchServiceRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderSiteSearchServiceRemoteTests.swift; sourceTree = ""; }; 17D936242475D8AB008B2205 /* RemoteHomepageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteHomepageType.swift; sourceTree = ""; }; 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WPKit-Swift.h"; sourceTree = ""; }; + 1DAC3D2529AF4F250068FE13 /* RemoteVideoPressVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteVideoPressVideo.swift; sourceTree = ""; }; 24ADA24D24F9B32D001B5DAE /* FeatureFlagSerializationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagSerializationTest.swift; 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 = ""; }; 3236F77724AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReaderTopicServiceRemote+Interests.swift"; sourceTree = ""; }; @@ -1992,6 +1994,7 @@ FEE4EF56272FDD4B003CDA3C /* RemoteCommentV2.swift */, FEF7419C28085D89002C4203 /* RemoteBloggingPrompt.swift */, FE20A6A3282A96C00025E975 /* RemoteBloggingPromptsSettings.swift */, + 1DAC3D2529AF4F250068FE13 /* RemoteVideoPressVideo.swift */, ); name = Models; sourceTree = ""; @@ -3107,6 +3110,7 @@ 74E2295C1F1E77290085F7F2 /* KeyringConnectionExternalUser.swift in Sources */, E1BD95151FD5A2B800CD5CE3 /* PluginDirectoryServiceRemote.swift in Sources */, 7430C9D71F1933210051B8E6 /* RemoteReaderCrossPostMeta.swift in Sources */, + 1DAC3D2629AF4F250068FE13 /* RemoteVideoPressVideo.swift in Sources */, FA68CD152993C6CD00FA4C29 /* BlazeServiceRemote.swift in Sources */, 4081976F221DDE9B00A298E4 /* StatsTopPostsTimeIntervalData.swift in Sources */, 9311A68B1F22625A00704AC9 /* TaxonomyServiceRemoteXMLRPC.m in Sources */, diff --git a/WordPressKit/RemoteVideoPressVideo.swift b/WordPressKit/RemoteVideoPressVideo.swift new file mode 100644 index 00000000..d65ab0f9 --- /dev/null +++ b/WordPressKit/RemoteVideoPressVideo.swift @@ -0,0 +1,111 @@ +import Foundation + +/// This enum matches the privacy setting constants defined in Jetpack: +/// https://github.com/Automattic/jetpack/blob/a2ccfb7978184e306211292a66ed49dcf38a517f/projects/packages/videopress/src/utility-functions.php#L13-L17 +@objc public enum VideoPressPrivacySetting: Int { + case isPublic = 0 + case isPrivate = 1 + case siteDefault = 2 +} + +@objcMembers public class RemoteVideoPressVideo: NSObject { + + /// The following properties match the response parameters from the `videos` endpoint: + /// https://developer.wordpress.com/docs/api/1.1/get/videos/%24guid/ + /// + /// However, it's missing the following parameters that could be added in the future if needed: + /// - files + /// - file_url_base + /// - upload_date + /// - files_status + /// - subtitles + public var id: String + public var title: String? + public var videoDescription: String? + public var width: NSNumber? + public var height: NSNumber? + public var duration: NSNumber? + public var displayEmbed: Bool? + public var allowDownload: Bool? + public var rating: String? + public var privacySetting: VideoPressPrivacySetting = .siteDefault + public var posterURL: URL? + public var originalURL: URL? + public var watermarkURL: URL? + public var bgColor: String? + public var blogId: NSNumber? + public var postId: NSNumber? + public var finished: Bool? + + public var token: String? + + public init(dictionary metadataDict: NSDictionary, id: String) { + self.id = id + + title = metadataDict.string(forKey: "title") + videoDescription = metadataDict.string(forKey: "descrption") + width = metadataDict.number(forKey: "width") + height = metadataDict.number(forKey: "height") + duration = metadataDict.number(forKey: "duration") + displayEmbed = metadataDict.object(forKey: "display_embed") as? Bool + allowDownload = metadataDict.object(forKey: "allow_download") as? Bool + rating = metadataDict.string(forKey: "rating") + if let privacySettingValue = metadataDict.number(forKey: "privacy_setting")?.intValue, let privacySettingEnum = VideoPressPrivacySetting.init(rawValue: privacySettingValue) { + privacySetting = privacySettingEnum + } + if let poster = metadataDict.string(forKey: "poster") { + posterURL = URL(string: poster) + } + if let original = metadataDict.string(forKey: "original") { + originalURL = URL(string: original) + } + if let watermark = metadataDict.string(forKey: "watermark") { + watermarkURL = URL(string: watermark) + } + bgColor = metadataDict.string(forKey: "bg_color") + blogId = metadataDict.number(forKey: "blog_id") + postId = metadataDict.number(forKey: "post_id") + finished = metadataDict.object(forKey: "finished") as? Bool + } + + /// Returns the URL that should be used to play the video. + /// + /// The URL used is the original but adding the token as a query parameter, which is required to play private videos. + public func getPlayURL() -> URL? { + guard var videoPlayURL = self.originalURL else { + return nil + } + if let token = self.token, var urlComponents = URLComponents(url: videoPlayURL, resolvingAgainstBaseURL: true) { + let metadataTokenParam = URLQueryItem(name: "metadata_token", value: token) + var queryItems: [URLQueryItem] = urlComponents.queryItems ?? [] + queryItems.append(metadataTokenParam) + urlComponents.queryItems = queryItems + videoPlayURL = urlComponents.url! + } + return videoPlayURL + } + + public func toDict() -> [String: Any] { + return [ + "id": self.id, + "title": self.title ?? "", + "description": self.videoDescription ?? "", + "width": self.width ?? 0, + "height": self.height ?? 0, + "duration": self.duration ?? 0, + "displayEmbed": self.displayEmbed!, + "allowDownload": self.allowDownload!, + "rating": self.rating ?? "", + "privacySetting": self.privacySetting, + "posterURL": self.posterURL?.absoluteString ?? "", + "originalURL": self.originalURL?.absoluteString ?? "", + "watermarkURL": self.watermarkURL?.absoluteString ?? "", + "bgColor": self.bgColor ?? "", + "blogId": self.blogId ?? -1, + "postId": self.postId ?? -1, + "finished": self.finished!, + "token": self.token ?? "", + "playURL": self.getPlayURL()?.absoluteString ?? self.originalURL?.absoluteString ?? "" + ] + } +} From d6c3ab72e92f127dd0f37401efbb570180c342ca Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 1 Mar 2023 18:14:52 +0100 Subject: [PATCH 05/17] Implement VideoPress metadata fetch --- WordPressKit/MediaServiceRemote.h | 17 +++++++++++ WordPressKit/MediaServiceRemoteREST.m | 40 +++++++++++++++++++++++++ WordPressKit/MediaServiceRemoteXMLRPC.m | 14 +++++++++ 3 files changed, 71 insertions(+) diff --git a/WordPressKit/MediaServiceRemote.h b/WordPressKit/MediaServiceRemote.h index 4da14be6..a6c94404 100644 --- a/WordPressKit/MediaServiceRemote.h +++ b/WordPressKit/MediaServiceRemote.h @@ -1,6 +1,7 @@ #import @class RemoteMedia; +@class RemoteVideoPressVideo; @protocol MediaServiceRemote @@ -58,6 +59,22 @@ withSuccess:(void (^)(NSInteger))success failure:(void (^)(NSError *))failure; +/** + * Retrieves the metadata of a VideoPress video. + * + * The metadata parameters can be found in the API reference: + * https://developer.wordpress.com/docs/api/1.1/get/videos/%24guid/ + * + * @param videoPressID ID of the video in VideoPress. + * @param isSitePrivate true if the site is private, this will be used to determine the fetch of the VideoPress token. + * @param success a block to be executed when the metadata is fetched successfully. + * @param failure a block to be executed when the metadata can't be fetched. + */ +-(void)getMetadataFromVideoPressID:(NSString *)videoPressID + isSitePrivate:(BOOL)isSitePrivate + success:(void (^)(RemoteVideoPressVideo *metadata))success + failure:(void (^)(NSError *))failure; + /** Retrieves the VideoPress URL for the request videoPressID diff --git a/WordPressKit/MediaServiceRemoteREST.m b/WordPressKit/MediaServiceRemoteREST.m index 235cd81d..c67d20b0 100644 --- a/WordPressKit/MediaServiceRemoteREST.m +++ b/WordPressKit/MediaServiceRemoteREST.m @@ -309,6 +309,46 @@ - (void)deleteMedia:(RemoteMedia *)media }]; } +-(void)getMetadataFromVideoPressID:(NSString *)videoPressID + isSitePrivate:(BOOL)isSitePrivate + success:(void (^)(RemoteVideoPressVideo *metadata))success + failure:(void (^)(NSError *))failure +{ + NSString *path = [NSString stringWithFormat:@"videos/%@", videoPressID]; + NSString *requestUrl = [self pathForEndpoint:path + withVersion:ServiceRemoteWordPressComRESTApiVersion_1_1]; + + [self.wordPressComRestApi GET:requestUrl + parameters:nil + success:^(id responseObject, NSHTTPURLResponse *httpResponse) { + NSDictionary *response = (NSDictionary *)responseObject; + RemoteVideoPressVideo *video = [[RemoteVideoPressVideo alloc] initWithDictionary:response id:videoPressID]; + + BOOL needsToken = video.privacySetting == VideoPressPrivacySettingIsPrivate || (video.privacySetting == VideoPressPrivacySettingSiteDefault && isSitePrivate); + if(needsToken) { + [self getVideoPressToken:videoPressID success:^(NSString *token) { + video.token = token; + if (success) { + success(video); + } + } failure:^(NSError * error) { + if (failure) { + failure(error); + } + }]; + } + else { + if (success) { + success(video); + } + } + } failure:^(NSError *error, NSHTTPURLResponse *response) { + if (failure) { + failure(error); + } + }]; +} + -(void)getVideoURLFromVideoPressID:(NSString *)videoPressID success:(void (^)(NSURL *videoURL, NSURL *posterURL))success failure:(void (^)(NSError *))failure diff --git a/WordPressKit/MediaServiceRemoteXMLRPC.m b/WordPressKit/MediaServiceRemoteXMLRPC.m index 83c009c7..2a986fbf 100644 --- a/WordPressKit/MediaServiceRemoteXMLRPC.m +++ b/WordPressKit/MediaServiceRemoteXMLRPC.m @@ -233,6 +233,20 @@ - (void)deleteMedia:(RemoteMedia *)media }]; } +-(void)getMetadataFromVideoPressID:(NSString *)videoPressID + isSitePrivate:(BOOL)includeToken + success:(void (^)(RemoteVideoPressVideo *video))success + failure:(void (^)(NSError *))failure +{ + // ⚠️ The endpoint used for fetching the metadata is not available in XML-RPC. + if (failure) { + NSError *error = [NSError errorWithDomain:NSURLErrorDomain + code:NSURLErrorUnsupportedURL + userInfo:nil]; + failure(error); + } +} + -(void)getVideoURLFromVideoPressID:(NSString *)videoPressID success:(void (^)(NSURL *videoURL, NSURL *posterURL))success failure:(void (^)(NSError *))failure From 7e2f6b6cffe539bb4cc592821f050d7b29931c90 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 1 Mar 2023 18:17:57 +0100 Subject: [PATCH 06/17] Remove `getVideoURLFromVideoPressID` in favor of `getMetadataFromVideoPressID` --- WordPressKit/MediaServiceRemote.h | 11 -------- WordPressKit/MediaServiceRemoteREST.m | 35 ------------------------- WordPressKit/MediaServiceRemoteXMLRPC.m | 13 --------- 3 files changed, 59 deletions(-) diff --git a/WordPressKit/MediaServiceRemote.h b/WordPressKit/MediaServiceRemote.h index a6c94404..dd5640e7 100644 --- a/WordPressKit/MediaServiceRemote.h +++ b/WordPressKit/MediaServiceRemote.h @@ -75,17 +75,6 @@ success:(void (^)(RemoteVideoPressVideo *metadata))success failure:(void (^)(NSError *))failure; -/** - Retrieves the VideoPress URL for the request videoPressID - - @param videoPressID the videoPressID to search for - @param success a block to be executed if the the video is found on VideoPress and the URL is valid - @param failure a block to be executed if the video is not found on VideoPress. - */ --(void)getVideoURLFromVideoPressID:(NSString *)videoPressID - success:(void (^)(NSURL *videoURL, NSURL *posterURL))success - failure:(void (^)(NSError *))failure; - /** Retrieves the VideoPress token for the request videoPressID. The token is required to play private VideoPress videos. diff --git a/WordPressKit/MediaServiceRemoteREST.m b/WordPressKit/MediaServiceRemoteREST.m index c67d20b0..f95cc962 100644 --- a/WordPressKit/MediaServiceRemoteREST.m +++ b/WordPressKit/MediaServiceRemoteREST.m @@ -349,41 +349,6 @@ -(void)getMetadataFromVideoPressID:(NSString *)videoPressID }]; } --(void)getVideoURLFromVideoPressID:(NSString *)videoPressID - success:(void (^)(NSURL *videoURL, NSURL *posterURL))success - failure:(void (^)(NSError *))failure -{ - NSString *path = [NSString stringWithFormat:@"videos/%@", videoPressID]; - NSString *requestUrl = [self pathForEndpoint:path - withVersion:ServiceRemoteWordPressComRESTApiVersion_1_1]; - - [self.wordPressComRestApi GET:requestUrl - parameters:nil - success:^(id responseObject, NSHTTPURLResponse *httpResponse) { - NSDictionary *response = (NSDictionary *)responseObject; - NSString *urlString = [response stringForKey:@"original"]; - NSString *posterURLString = [response stringForKey:@"poster"]; - NSURL *videoURL = [NSURL URLWithString:urlString]; - NSURL *posterURL = [NSURL URLWithString:posterURLString]; - if (videoURL) { - if (success) { - success(videoURL, posterURL); - } - } else { - if (failure) { - NSError *error = [NSError errorWithDomain:WordPressComRestApiErrorDomain - code:WordPressComRestApiErrorUnknown - userInfo:nil]; - failure(error); - } - } - } failure:^(NSError *error, NSHTTPURLResponse *response) { - if (failure) { - failure(error); - } - }]; -} - -(void)getVideoPressToken:(NSString *)videoPressID success:(void (^)(NSString *token))success failure:(void (^)(NSError *))failure diff --git a/WordPressKit/MediaServiceRemoteXMLRPC.m b/WordPressKit/MediaServiceRemoteXMLRPC.m index 2a986fbf..88df1774 100644 --- a/WordPressKit/MediaServiceRemoteXMLRPC.m +++ b/WordPressKit/MediaServiceRemoteXMLRPC.m @@ -247,19 +247,6 @@ -(void)getMetadataFromVideoPressID:(NSString *)videoPressID } } --(void)getVideoURLFromVideoPressID:(NSString *)videoPressID - success:(void (^)(NSURL *videoURL, NSURL *posterURL))success - failure:(void (^)(NSError *))failure -{ - //Sergio Estevao: 2017-04-12 this option doens't exist on XML-RPC so we will always fail the request - if (failure) { - NSError *error = [NSError errorWithDomain:NSURLErrorDomain - code:NSURLErrorUnsupportedURL - userInfo:nil]; - failure(error); - } -} - -(void)getVideoPressToken:(NSString *)videoPressID success:(void (^)(NSString *token))success failure:(void (^)(NSError *))failure From df662b7c45886c15b8aa689715e8dceb01a959e9 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 2 Mar 2023 14:59:50 +0100 Subject: [PATCH 07/17] Add tests for VideoPress metadata and token fetch --- WordPressKit.xcodeproj/project.pbxproj | 16 +++ .../MediaServiceRemoteRESTTests.swift | 130 ++++++++++++++++++ .../Mock Data/videopress-private-video.json | 19 +++ .../Mock Data/videopress-public-video.json | 19 +++ .../videopress-site-default-video.json | 19 +++ .../Mock Data/videopress-token.json | 3 + 6 files changed, 206 insertions(+) create mode 100644 WordPressKitTests/Mock Data/videopress-private-video.json create mode 100644 WordPressKitTests/Mock Data/videopress-public-video.json create mode 100644 WordPressKitTests/Mock Data/videopress-site-default-video.json create mode 100644 WordPressKitTests/Mock Data/videopress-token.json diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index 641facdb..e1a0bd87 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -20,6 +20,10 @@ 17D936252475D8AB008B2205 /* RemoteHomepageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D936242475D8AB008B2205 /* RemoteHomepageType.swift */; }; 1A4F98672279A87D00D86E8E /* WPKit-Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1DAC3D2629AF4F250068FE13 /* RemoteVideoPressVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DAC3D2529AF4F250068FE13 /* RemoteVideoPressVideo.swift */; }; + 1DF972BA29B0DF8C007A72BC /* videopress-token.json in Resources */ = {isa = PBXBuildFile; fileRef = 1DF972B729B0DF8C007A72BC /* videopress-token.json */; }; + 1DF972BF29B107E7007A72BC /* videopress-private-video.json in Resources */ = {isa = PBXBuildFile; fileRef = 1DF972BC29B107E7007A72BC /* videopress-private-video.json */; }; + 1DF972C029B107E7007A72BC /* videopress-public-video.json in Resources */ = {isa = PBXBuildFile; fileRef = 1DF972BD29B107E7007A72BC /* videopress-public-video.json */; }; + 1DF972C129B107E7007A72BC /* videopress-site-default-video.json in Resources */ = {isa = PBXBuildFile; fileRef = 1DF972BE29B107E7007A72BC /* videopress-site-default-video.json */; }; 240315B0A1D6C2B981572B5B /* Pods_WordPressKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = ED05C8FF3E61D93CE5BA527E /* Pods_WordPressKitTests.framework */; }; 24ADA24E24F9B32D001B5DAE /* FeatureFlagSerializationTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24ADA24D24F9B32D001B5DAE /* FeatureFlagSerializationTest.swift */; }; 3236F77824AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3236F77724AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift */; }; @@ -679,6 +683,10 @@ 17D936242475D8AB008B2205 /* RemoteHomepageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteHomepageType.swift; sourceTree = ""; }; 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WPKit-Swift.h"; sourceTree = ""; }; 1DAC3D2529AF4F250068FE13 /* RemoteVideoPressVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteVideoPressVideo.swift; sourceTree = ""; }; + 1DF972B729B0DF8C007A72BC /* videopress-token.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "videopress-token.json"; sourceTree = ""; }; + 1DF972BC29B107E7007A72BC /* videopress-private-video.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "videopress-private-video.json"; sourceTree = ""; }; + 1DF972BD29B107E7007A72BC /* videopress-public-video.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "videopress-public-video.json"; sourceTree = ""; }; + 1DF972BE29B107E7007A72BC /* videopress-site-default-video.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "videopress-site-default-video.json"; sourceTree = ""; }; 24ADA24D24F9B32D001B5DAE /* FeatureFlagSerializationTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeatureFlagSerializationTest.swift; 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 = ""; }; 3236F77724AE34B40088E8F3 /* ReaderTopicServiceRemote+Interests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ReaderTopicServiceRemote+Interests.swift"; sourceTree = ""; }; @@ -2253,6 +2261,10 @@ E1787DAF200E564B004CB3AF /* timezones.json */, 436D5642211B7F9B00CEAA33 /* validate-domain-contact-information-response-fail.json */, 436D5644211B801000CEAA33 /* validate-domain-contact-information-response-success.json */, + 1DF972BC29B107E7007A72BC /* videopress-private-video.json */, + 1DF972BD29B107E7007A72BC /* videopress-public-video.json */, + 1DF972BE29B107E7007A72BC /* videopress-site-default-video.json */, + 1DF972B729B0DF8C007A72BC /* videopress-token.json */, FFE247A920C891E5002DF3A2 /* WordPressComAuthenticateWithIDToken2FANeededSuccess.json */, FFE247AB20C891E5002DF3A2 /* WordPressComAuthenticateWithIDTokenBearerTokenSuccess.json */, FFE247AE20C891E6002DF3A2 /* WordPressComAuthenticateWithIDTokenExistingUserNeedsConnection.json */, @@ -2698,6 +2710,7 @@ 74C473B51EF320CC009918F2 /* site-delete-bad-json-failure.json in Resources */, 740B23E21F17FB4200067A2A /* xmlrpc-metaweblog-editpost-change-format-failure.xml in Resources */, FFE247BD20C9C88B002DF3A2 /* empty.json in Resources */, + 1DF972BA29B0DF8C007A72BC /* videopress-token.json in Resources */, BA8EA71324A056C300D5CC9F /* plugin-service-remote-featured-malformed.json in Resources */, 436D5643211B7F9B00CEAA33 /* validate-domain-contact-information-response-fail.json in Resources */, AB49D09725D1AC0A0084905B /* post-likes-success.json in Resources */, @@ -2790,6 +2803,7 @@ 740B23EE1F17FB7E00067A2A /* xmlrpc-malformed-request-xml-error.xml in Resources */, 826016F91F9FAF6300533B6C /* activity-log-success-3.json in Resources */, FE20A6A8282BC83A0025E975 /* blogging-prompts-settings-update-with-response.json in Resources */, + 1DF972BF29B107E7007A72BC /* videopress-private-video.json in Resources */, 8B749E8A25AF819700023F03 /* jetpack-capabilities-malformed.json in Resources */, 93BD27551EE73442002BB00B /* auth-send-login-email-invalid-client-failure.json in Resources */, 826016FD1F9FAF6300533B6C /* activity-log-bad-json-failure.json in Resources */, @@ -2853,6 +2867,7 @@ 93BD27631EE73442002BB00B /* me-sites-visibility-bad-json-failure.json in Resources */, 17BF9A7220C7E18200BF57D2 /* reader-site-search-success-hasmore.json in Resources */, 9AB6D64B21872A0D0008F274 /* post-revisions-success.json in Resources */, + 1DF972C029B107E7007A72BC /* videopress-public-video.json in Resources */, C92EFF6925E7403F00E0308D /* common-starter-site-designs-success.json in Resources */, 7433BC131EFC45C7002D9E92 /* site-plans-bad-json-failure.json in Resources */, 74B335DE1F06F5A50053A184 /* WordPressComRestApiFailInvalidJSON.json in Resources */, @@ -2887,6 +2902,7 @@ 9A881750223C01E400A3AB20 /* jetpack-service-error-install-failure.json in Resources */, 74FC6F431F191C1D00112505 /* notifications-load-all.json in Resources */, 74C473C11EF32C74009918F2 /* site-export-missing-status-failure.json in Resources */, + 1DF972C129B107E7007A72BC /* videopress-site-default-video.json in Resources */, 828A2400201B671F004F6859 /* activity-restore-success.json in Resources */, 826016FC1F9FAF6300533B6C /* activity-log-success-2.json in Resources */, C738CAF928622BB1001BE107 /* qrlogin-authenticate-failed-400.json in Resources */, diff --git a/WordPressKitTests/MediaServiceRemoteRESTTests.swift b/WordPressKitTests/MediaServiceRemoteRESTTests.swift index f96283fb..c38a0dd8 100644 --- a/WordPressKitTests/MediaServiceRemoteRESTTests.swift +++ b/WordPressKitTests/MediaServiceRemoteRESTTests.swift @@ -1,4 +1,5 @@ import XCTest +import OHHTTPStubs @testable import WordPressKit class MediaServiceRemoteRESTTests: XCTestCase { @@ -12,6 +13,11 @@ class MediaServiceRemoteRESTTests: XCTestCase { mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: mockRemoteApi, siteID: NSNumber(value: siteID)) } + override func tearDown() { + super.tearDown() + HTTPStubs.removeAllStubs() + } + func mockRemoteMedia() -> RemoteMedia { let remoteMedia = RemoteMedia() @@ -23,6 +29,30 @@ class MediaServiceRemoteRESTTests: XCTestCase { return remoteMedia } + func mockVideoPressMetadataResponse(mockData: String) { + stub(condition: { request in + guard let url = request.url?.absoluteString else { + return false + } + return url.contains("/videos/") + }) { _ in + let stubPath = OHPathForFile(mockData, type(of: self)) + return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + } + } + + func mockVideoPressTokenResponse() { + stub(condition: { request in + guard let url = request.url?.absoluteString else { + return false + } + return url.contains("/media/videopress-playback-jwt/") + }) { _ in + let stubPath = OHPathForFile("videopress-token.json", type(of: self)) + return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + } + } + func testGetMediaWithIDPath() { let id = 1 @@ -347,4 +377,104 @@ class MediaServiceRemoteRESTTests: XCTestCase { XCTAssertEqual(remoteMediaArray[1].height?.intValue, height) XCTAssertEqual(remoteMediaArray[1].width?.intValue, width) } + + func testGetMetadataFromVideoPressIDPath() { + + let id = "AbCDeF" + let expectedPath = mediaServiceRemote.path(forEndpoint: "videos/\(id)", withVersion: ._1_1) + mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: false, success: nil, failure: nil) + XCTAssertTrue(mockRemoteApi.getMethodCalled, "Wrong method, expected GET got \(mockRemoteApi.methodCalled())") + XCTAssertEqual(mockRemoteApi.URLStringPassedIn, expectedPath, "Wrong path") + } + + func testGetMetadataFromPublicVideoPressIDPath() { + let id = "AbCDeF" + // Mock VideoPress metadata response. + mockVideoPressMetadataResponse(mockData: "videopress-public-video.json") + let expect = self.expectation(description: "VideoPress metadata is fetched for a public video in a private site") + let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: true, success: { metadata in + expect.fulfill() + XCTAssertNotNil(metadata) + XCTAssertEqual(metadata?.id, id) + XCTAssertNil(metadata?.token) + }, failure: { _ in + expect.fulfill() + XCTFail("This call should be successfull") + }) + self.waitForExpectations(timeout: 2, handler: nil) + } + + func testGetMetadataFromPrivateVideoPressIDPath() { + let id = "AbCDeF" + let token = "videopress-token" + // Mock VideoPress metadata response. + mockVideoPressMetadataResponse(mockData: "videopress-private-video.json") + // Mock VideoPress token response. + mockVideoPressTokenResponse() + let expect = self.expectation(description: "VideoPress metadata is fetched for a private video in public site") + let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: false, success: { metadata in + expect.fulfill() + XCTAssertNotNil(metadata) + XCTAssertEqual(metadata?.id, id) + XCTAssertEqual(metadata?.token, token) + }, failure: { _ in + expect.fulfill() + XCTFail("This call should be successfull") + }) + self.waitForExpectations(timeout: 2, handler: nil) + } + + func testGetMetadataFromSiteDefaultVideoPressIDPath() { + let id = "AbCDeF" + let token = "videopress-token" + // Mock VideoPress metadata response. + mockVideoPressMetadataResponse(mockData: "videopress-site-default-video.json") + // Mock VideoPress token response. + mockVideoPressTokenResponse() + let expect = self.expectation(description: "VideoPress metadata is fetched for a site default video in a private site") + let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: true, success: { metadata in + expect.fulfill() + XCTAssertNotNil(metadata) + XCTAssertEqual(metadata?.id, id) + XCTAssertEqual(metadata?.token, token) + }, failure: { _ in + expect.fulfill() + XCTFail("This call should be successfull") + }) + self.waitForExpectations(timeout: 2, handler: nil) + } + + func testGetVideoPressToken() { + let id = "AbCDeF" + let token = "videopress-token" + + // Mock VideoPress token response. + stub(condition: { request in + guard let url = request.url?.absoluteString else { + return false + } + return url.contains("/media/videopress-playback-jwt/") + }) { _ in + let stubPath = OHPathForFile("videopress-token.json", type(of: self)) + return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) + } + + let expect = self.expectation(description: "VideoPress token is fetched for a video") + let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + mediaServiceRemote.getVideoPressToken(id, success: { result in + expect.fulfill() + XCTAssertEqual(result, token) + }, failure: { _ in + expect.fulfill() + XCTFail("This call should be successfull") + }) + self.waitForExpectations(timeout: 2, handler: nil) + } } diff --git a/WordPressKitTests/Mock Data/videopress-private-video.json b/WordPressKitTests/Mock Data/videopress-private-video.json new file mode 100644 index 00000000..f0ad2bcf --- /dev/null +++ b/WordPressKitTests/Mock Data/videopress-private-video.json @@ -0,0 +1,19 @@ +{ + "guid": "OO4thna8", + "title": "VideoPress demo", + "description": "", + "width": 1280, + "height": 720, + "duration": 143700, + "display_embed": true, + "allow_download": false, + "rating": "G", + "poster": "https://videos.files.wordpress.com/OO4thna8/videopress2-web2_hd.original.jpg", + "original": "https://videos.files.wordpress.com/OO4thna8/videopress2-web2.mov", + "watermark": "https://wptv.files.wordpress.com/2010/07/wptv.png", + "bg_color": "", + "blog_id": 9999, + "post_id": 1913, + "privacy_setting": 1, + "finished": true +} diff --git a/WordPressKitTests/Mock Data/videopress-public-video.json b/WordPressKitTests/Mock Data/videopress-public-video.json new file mode 100644 index 00000000..361a507f --- /dev/null +++ b/WordPressKitTests/Mock Data/videopress-public-video.json @@ -0,0 +1,19 @@ +{ + "guid": "OO4thna8", + "title": "VideoPress demo", + "description": "", + "width": 1280, + "height": 720, + "duration": 143700, + "display_embed": true, + "allow_download": false, + "rating": "G", + "poster": "https://videos.files.wordpress.com/OO4thna8/videopress2-web2_hd.original.jpg", + "original": "https://videos.files.wordpress.com/OO4thna8/videopress2-web2.mov", + "watermark": "https://wptv.files.wordpress.com/2010/07/wptv.png", + "bg_color": "", + "blog_id": 9999, + "post_id": 1913, + "privacy_setting": 0, + "finished": true +} diff --git a/WordPressKitTests/Mock Data/videopress-site-default-video.json b/WordPressKitTests/Mock Data/videopress-site-default-video.json new file mode 100644 index 00000000..7a3ac06c --- /dev/null +++ b/WordPressKitTests/Mock Data/videopress-site-default-video.json @@ -0,0 +1,19 @@ +{ + "guid": "OO4thna8", + "title": "VideoPress demo", + "description": "", + "width": 1280, + "height": 720, + "duration": 143700, + "display_embed": true, + "allow_download": false, + "rating": "G", + "poster": "https://videos.files.wordpress.com/OO4thna8/videopress2-web2_hd.original.jpg", + "original": "https://videos.files.wordpress.com/OO4thna8/videopress2-web2.mov", + "watermark": "https://wptv.files.wordpress.com/2010/07/wptv.png", + "bg_color": "", + "blog_id": 9999, + "post_id": 1913, + "privacy_setting": 2, + "finished": true +} diff --git a/WordPressKitTests/Mock Data/videopress-token.json b/WordPressKitTests/Mock Data/videopress-token.json new file mode 100644 index 00000000..39600bc2 --- /dev/null +++ b/WordPressKitTests/Mock Data/videopress-token.json @@ -0,0 +1,3 @@ +{ + "metadata_token": "videopress-token" +} \ No newline at end of file From 37f6aa270ab1202b1bad7fcc198bb9b1025189ed Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 2 Mar 2023 18:51:09 +0100 Subject: [PATCH 08/17] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5023925e..e453706b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,7 +34,7 @@ _None._ ### Breaking Changes -_None._ +- Refactor the logic to fetch metadata of VideoPress videos [#581] ### New Features From b080c68a1a18fb40ab5ca03f52e095584a016b88 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Fri, 3 Mar 2023 11:58:25 +0100 Subject: [PATCH 09/17] Make RemoteVideoPressVideo encodable --- WordPressKit/RemoteVideoPressVideo.swift | 84 ++++++++++++++---------- 1 file changed, 50 insertions(+), 34 deletions(-) diff --git a/WordPressKit/RemoteVideoPressVideo.swift b/WordPressKit/RemoteVideoPressVideo.swift index d65ab0f9..162e6ba3 100644 --- a/WordPressKit/RemoteVideoPressVideo.swift +++ b/WordPressKit/RemoteVideoPressVideo.swift @@ -2,13 +2,13 @@ import Foundation /// This enum matches the privacy setting constants defined in Jetpack: /// https://github.com/Automattic/jetpack/blob/a2ccfb7978184e306211292a66ed49dcf38a517f/projects/packages/videopress/src/utility-functions.php#L13-L17 -@objc public enum VideoPressPrivacySetting: Int { +@objc public enum VideoPressPrivacySetting: Int, Encodable { case isPublic = 0 case isPrivate = 1 case siteDefault = 2 } -@objcMembers public class RemoteVideoPressVideo: NSObject { +@objcMembers public class RemoteVideoPressVideo: NSObject, Encodable { /// The following properties match the response parameters from the `videos` endpoint: /// https://developer.wordpress.com/docs/api/1.1/get/videos/%24guid/ @@ -22,9 +22,9 @@ import Foundation public var id: String public var title: String? public var videoDescription: String? - public var width: NSNumber? - public var height: NSNumber? - public var duration: NSNumber? + public var width: Int? + public var height: Int? + public var duration: Int? public var displayEmbed: Bool? public var allowDownload: Bool? public var rating: String? @@ -33,20 +33,24 @@ import Foundation public var originalURL: URL? public var watermarkURL: URL? public var bgColor: String? - public var blogId: NSNumber? - public var postId: NSNumber? + public var blogId: Int? + public var postId: Int? public var finished: Bool? public var token: String? + enum CodingKeys: String, CodingKey { + case id, title, description, width, height, duration, displayEmbed, allowDownload, rating, privacySetting, posterURL, originalURL, watermarkURL, bgColor, blogId, postId, finished, token, playURL + } + public init(dictionary metadataDict: NSDictionary, id: String) { self.id = id title = metadataDict.string(forKey: "title") videoDescription = metadataDict.string(forKey: "descrption") - width = metadataDict.number(forKey: "width") - height = metadataDict.number(forKey: "height") - duration = metadataDict.number(forKey: "duration") + width = metadataDict.number(forKey: "width")?.intValue + height = metadataDict.number(forKey: "height")?.intValue + duration = metadataDict.number(forKey: "duration")?.intValue displayEmbed = metadataDict.object(forKey: "display_embed") as? Bool allowDownload = metadataDict.object(forKey: "allow_download") as? Bool rating = metadataDict.string(forKey: "rating") @@ -63,8 +67,8 @@ import Foundation watermarkURL = URL(string: watermark) } bgColor = metadataDict.string(forKey: "bg_color") - blogId = metadataDict.number(forKey: "blog_id") - postId = metadataDict.number(forKey: "post_id") + blogId = metadataDict.number(forKey: "blog_id")?.intValue + postId = metadataDict.number(forKey: "post_id")?.intValue finished = metadataDict.object(forKey: "finished") as? Bool } @@ -85,27 +89,39 @@ import Foundation return videoPlayURL } - public func toDict() -> [String: Any] { - return [ - "id": self.id, - "title": self.title ?? "", - "description": self.videoDescription ?? "", - "width": self.width ?? 0, - "height": self.height ?? 0, - "duration": self.duration ?? 0, - "displayEmbed": self.displayEmbed!, - "allowDownload": self.allowDownload!, - "rating": self.rating ?? "", - "privacySetting": self.privacySetting, - "posterURL": self.posterURL?.absoluteString ?? "", - "originalURL": self.originalURL?.absoluteString ?? "", - "watermarkURL": self.watermarkURL?.absoluteString ?? "", - "bgColor": self.bgColor ?? "", - "blogId": self.blogId ?? -1, - "postId": self.postId ?? -1, - "finished": self.finished!, - "token": self.token ?? "", - "playURL": self.getPlayURL()?.absoluteString ?? self.originalURL?.absoluteString ?? "" - ] + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(id, forKey: .id) + try container.encode(title, forKey: .title) + try container.encode(videoDescription, forKey: .description) + try container.encode(width, forKey: .width) + try container.encode(height, forKey: .height) + try container.encode(duration, forKey: .duration) + try container.encode(displayEmbed, forKey: .displayEmbed) + try container.encode(allowDownload, forKey: .allowDownload) + try container.encode(rating, forKey: .rating) + try container.encode(privacySetting, forKey: .privacySetting) + try container.encode(posterURL, forKey: .posterURL) + try container.encode(originalURL, forKey: .originalURL) + try container.encode(watermarkURL, forKey: .watermarkURL) + try container.encode(bgColor, forKey: .bgColor) + try container.encode(blogId, forKey: .blogId) + try container.encode(postId, forKey: .postId) + try container.encode(finished, forKey: .finished) + try container.encode(token, forKey: .token) + let playURL = getPlayURL()?.absoluteString ?? self.originalURL?.absoluteString ?? "" + try container.encode(playURL, forKey: .playURL) + } + + public func asDictionary() -> [String: Any] { + guard + let data = try? JSONEncoder().encode(self), + let dictionary = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] + else { + assertionFailure("Encoding of RemoteVideoPressVideo failed") + return [String: Any]() + } + return dictionary } } From 0047e08f55fd82d891ed3b457f61edb40a9be2c0 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 8 Mar 2023 13:48:35 +0100 Subject: [PATCH 10/17] Replace `getPlayURL` with a generic version for getting an URL with the token --- WordPressKit/RemoteVideoPressVideo.swift | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/WordPressKit/RemoteVideoPressVideo.swift b/WordPressKit/RemoteVideoPressVideo.swift index 162e6ba3..971abfa2 100644 --- a/WordPressKit/RemoteVideoPressVideo.swift +++ b/WordPressKit/RemoteVideoPressVideo.swift @@ -40,7 +40,7 @@ import Foundation public var token: String? enum CodingKeys: String, CodingKey { - case id, title, description, width, height, duration, displayEmbed, allowDownload, rating, privacySetting, posterURL, originalURL, watermarkURL, bgColor, blogId, postId, finished, token, playURL + case id, title, description, width, height, duration, displayEmbed, allowDownload, rating, privacySetting, posterURL, originalURL, watermarkURL, bgColor, blogId, postId, finished, token } public init(dictionary metadataDict: NSDictionary, id: String) { @@ -72,21 +72,24 @@ import Foundation finished = metadataDict.object(forKey: "finished") as? Bool } - /// Returns the URL that should be used to play the video. + /// Returns the specified URL adding the token as a query parameter, which is required to play private videos. + /// - Parameters: + /// - url /// - /// The URL used is the original but adding the token as a query parameter, which is required to play private videos. - public func getPlayURL() -> URL? { - guard var videoPlayURL = self.originalURL else { + /// - Returns: The specified URL with the token as a query parameter. + @objc(getURLWithToken:) + public func getURLWithToken(url: URL?) -> URL? { + guard var urlWithToken = url else { return nil } - if let token = self.token, var urlComponents = URLComponents(url: videoPlayURL, resolvingAgainstBaseURL: true) { + if let token = self.token, var urlComponents = URLComponents(url: urlWithToken, resolvingAgainstBaseURL: true) { let metadataTokenParam = URLQueryItem(name: "metadata_token", value: token) var queryItems: [URLQueryItem] = urlComponents.queryItems ?? [] queryItems.append(metadataTokenParam) urlComponents.queryItems = queryItems - videoPlayURL = urlComponents.url! + urlWithToken = urlComponents.url! } - return videoPlayURL + return urlWithToken } public func encode(to encoder: Encoder) throws { @@ -110,8 +113,6 @@ import Foundation try container.encode(postId, forKey: .postId) try container.encode(finished, forKey: .finished) try container.encode(token, forKey: .token) - let playURL = getPlayURL()?.absoluteString ?? self.originalURL?.absoluteString ?? "" - try container.encode(playURL, forKey: .playURL) } public func asDictionary() -> [String: Any] { From 53708b60b65d1d39b7e71080ac1bee6f776187bd Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Wed, 8 Mar 2023 14:28:57 +0100 Subject: [PATCH 11/17] Update documentation of `getURLWithToken` --- WordPressKit/RemoteVideoPressVideo.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressKit/RemoteVideoPressVideo.swift b/WordPressKit/RemoteVideoPressVideo.swift index 971abfa2..3bd02e83 100644 --- a/WordPressKit/RemoteVideoPressVideo.swift +++ b/WordPressKit/RemoteVideoPressVideo.swift @@ -74,7 +74,7 @@ import Foundation /// Returns the specified URL adding the token as a query parameter, which is required to play private videos. /// - Parameters: - /// - url + /// - url: URL to include the token. /// /// - Returns: The specified URL with the token as a query parameter. @objc(getURLWithToken:) From 45d442c62411c17f71261ca0f1e4b09acfa698c8 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 9 Mar 2023 11:42:30 +0100 Subject: [PATCH 12/17] Use test helpers to simplify `MediaServiceRemoteREST` tests --- .../MediaServiceRemoteRESTTests.swift | 72 +++---------------- 1 file changed, 11 insertions(+), 61 deletions(-) diff --git a/WordPressKitTests/MediaServiceRemoteRESTTests.swift b/WordPressKitTests/MediaServiceRemoteRESTTests.swift index c38a0dd8..ec891176 100644 --- a/WordPressKitTests/MediaServiceRemoteRESTTests.swift +++ b/WordPressKitTests/MediaServiceRemoteRESTTests.swift @@ -1,8 +1,7 @@ import XCTest -import OHHTTPStubs @testable import WordPressKit -class MediaServiceRemoteRESTTests: XCTestCase { +class MediaServiceRemoteRESTTests: RemoteTestCase, RESTTestable { let mockRemoteApi = MockWordPressComRestApi() var mediaServiceRemote: MediaServiceRemoteREST! @@ -13,11 +12,6 @@ class MediaServiceRemoteRESTTests: XCTestCase { mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: mockRemoteApi, siteID: NSNumber(value: siteID)) } - override func tearDown() { - super.tearDown() - HTTPStubs.removeAllStubs() - } - func mockRemoteMedia() -> RemoteMedia { let remoteMedia = RemoteMedia() @@ -29,30 +23,6 @@ class MediaServiceRemoteRESTTests: XCTestCase { return remoteMedia } - func mockVideoPressMetadataResponse(mockData: String) { - stub(condition: { request in - guard let url = request.url?.absoluteString else { - return false - } - return url.contains("/videos/") - }) { _ in - let stubPath = OHPathForFile(mockData, type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) - } - } - - func mockVideoPressTokenResponse() { - stub(condition: { request in - guard let url = request.url?.absoluteString else { - return false - } - return url.contains("/media/videopress-playback-jwt/") - }) { _ in - let stubPath = OHPathForFile("videopress-token.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) - } - } - func testGetMediaWithIDPath() { let id = 1 @@ -379,7 +349,6 @@ class MediaServiceRemoteRESTTests: XCTestCase { } func testGetMetadataFromVideoPressIDPath() { - let id = "AbCDeF" let expectedPath = mediaServiceRemote.path(forEndpoint: "videos/\(id)", withVersion: ._1_1) mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: false, success: nil, failure: nil) @@ -390,10 +359,9 @@ class MediaServiceRemoteRESTTests: XCTestCase { func testGetMetadataFromPublicVideoPressIDPath() { let id = "AbCDeF" // Mock VideoPress metadata response. - mockVideoPressMetadataResponse(mockData: "videopress-public-video.json") + stubRemoteResponse("/videos", filename: "videopress-public-video.json", contentType: .ApplicationHTML) let expect = self.expectation(description: "VideoPress metadata is fetched for a public video in a private site") - let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) - let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: getRestApi(), siteID: NSNumber(value: siteID)) mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: true, success: { metadata in expect.fulfill() XCTAssertNotNil(metadata) @@ -409,13 +377,10 @@ class MediaServiceRemoteRESTTests: XCTestCase { func testGetMetadataFromPrivateVideoPressIDPath() { let id = "AbCDeF" let token = "videopress-token" - // Mock VideoPress metadata response. - mockVideoPressMetadataResponse(mockData: "videopress-private-video.json") - // Mock VideoPress token response. - mockVideoPressTokenResponse() + stubRemoteResponse("/videos", filename: "videopress-private-video.json", contentType: .ApplicationHTML) + stubRemoteResponse("/media/videopress-playback-jwt", filename: "videopress-token.json", contentType: .ApplicationHTML) let expect = self.expectation(description: "VideoPress metadata is fetched for a private video in public site") - let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) - let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: getRestApi(), siteID: NSNumber(value: siteID)) mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: false, success: { metadata in expect.fulfill() XCTAssertNotNil(metadata) @@ -431,13 +396,10 @@ class MediaServiceRemoteRESTTests: XCTestCase { func testGetMetadataFromSiteDefaultVideoPressIDPath() { let id = "AbCDeF" let token = "videopress-token" - // Mock VideoPress metadata response. - mockVideoPressMetadataResponse(mockData: "videopress-site-default-video.json") - // Mock VideoPress token response. - mockVideoPressTokenResponse() + stubRemoteResponse("/videos", filename: "videopress-site-default-video.json", contentType: .ApplicationHTML) + stubRemoteResponse("/media/videopress-playback-jwt", filename: "videopress-token.json", contentType: .ApplicationHTML) let expect = self.expectation(description: "VideoPress metadata is fetched for a site default video in a private site") - let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) - let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: getRestApi(), siteID: NSNumber(value: siteID)) mediaServiceRemote.getMetadataFromVideoPressID(id, isSitePrivate: true, success: { metadata in expect.fulfill() XCTAssertNotNil(metadata) @@ -453,21 +415,9 @@ class MediaServiceRemoteRESTTests: XCTestCase { func testGetVideoPressToken() { let id = "AbCDeF" let token = "videopress-token" - - // Mock VideoPress token response. - stub(condition: { request in - guard let url = request.url?.absoluteString else { - return false - } - return url.contains("/media/videopress-playback-jwt/") - }) { _ in - let stubPath = OHPathForFile("videopress-token.json", type(of: self)) - return fixture(filePath: stubPath!, headers: ["Content-Type" as NSObject: "application/json" as AnyObject]) - } - + stubRemoteResponse("/media/videopress-playback-jwt", filename: "videopress-token.json", contentType: .ApplicationHTML) let expect = self.expectation(description: "VideoPress token is fetched for a video") - let api = WordPressComRestApi(oAuthToken: nil, userAgent: nil) - let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: api, siteID: NSNumber(value: siteID)) + let mediaServiceRemote = MediaServiceRemoteREST(wordPressComRestApi: getRestApi(), siteID: NSNumber(value: siteID)) mediaServiceRemote.getVideoPressToken(id, success: { result in expect.fulfill() XCTAssertEqual(result, token) From 91e88a5945374ae5fb4cfd0cc063bfcf4860649c Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 9 Mar 2023 12:36:19 +0100 Subject: [PATCH 13/17] Add tests for RemoteVideoPressVideo model --- WordPressKit.xcodeproj/project.pbxproj | 4 + .../Models/RemoteVideoPressVideoTests.swift | 84 +++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 WordPressKitTests/Models/RemoteVideoPressVideoTests.swift diff --git a/WordPressKit.xcodeproj/project.pbxproj b/WordPressKit.xcodeproj/project.pbxproj index e1a0bd87..790ceb6d 100644 --- a/WordPressKit.xcodeproj/project.pbxproj +++ b/WordPressKit.xcodeproj/project.pbxproj @@ -20,6 +20,7 @@ 17D936252475D8AB008B2205 /* RemoteHomepageType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17D936242475D8AB008B2205 /* RemoteHomepageType.swift */; }; 1A4F98672279A87D00D86E8E /* WPKit-Swift.h in Headers */ = {isa = PBXBuildFile; fileRef = 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */; settings = {ATTRIBUTES = (Private, ); }; }; 1DAC3D2629AF4F250068FE13 /* RemoteVideoPressVideo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DAC3D2529AF4F250068FE13 /* RemoteVideoPressVideo.swift */; }; + 1DC837C229B9F04F009DCD4B /* RemoteVideoPressVideoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DC837C129B9F04F009DCD4B /* RemoteVideoPressVideoTests.swift */; }; 1DF972BA29B0DF8C007A72BC /* videopress-token.json in Resources */ = {isa = PBXBuildFile; fileRef = 1DF972B729B0DF8C007A72BC /* videopress-token.json */; }; 1DF972BF29B107E7007A72BC /* videopress-private-video.json in Resources */ = {isa = PBXBuildFile; fileRef = 1DF972BC29B107E7007A72BC /* videopress-private-video.json */; }; 1DF972C029B107E7007A72BC /* videopress-public-video.json in Resources */ = {isa = PBXBuildFile; fileRef = 1DF972BD29B107E7007A72BC /* videopress-public-video.json */; }; @@ -683,6 +684,7 @@ 17D936242475D8AB008B2205 /* RemoteHomepageType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteHomepageType.swift; sourceTree = ""; }; 1A4F98662279A87D00D86E8E /* WPKit-Swift.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WPKit-Swift.h"; sourceTree = ""; }; 1DAC3D2529AF4F250068FE13 /* RemoteVideoPressVideo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteVideoPressVideo.swift; sourceTree = ""; }; + 1DC837C129B9F04F009DCD4B /* RemoteVideoPressVideoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteVideoPressVideoTests.swift; sourceTree = ""; }; 1DF972B729B0DF8C007A72BC /* videopress-token.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "videopress-token.json"; sourceTree = ""; }; 1DF972BC29B107E7007A72BC /* videopress-private-video.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "videopress-private-video.json"; sourceTree = ""; }; 1DF972BD29B107E7007A72BC /* videopress-public-video.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "videopress-public-video.json"; sourceTree = ""; }; @@ -2470,6 +2472,7 @@ children = ( F3FF8A1B279C86E000E5C90F /* Stats */, F3FF8A20279C8EE200E5C90F /* RemotePersonTests.swift */, + 1DC837C129B9F04F009DCD4B /* RemoteVideoPressVideoTests.swift */, ); path = Models; sourceTree = ""; @@ -3293,6 +3296,7 @@ BAFA775624ADAB3C000F0D3A /* MockPluginDirectoryEntryProvider.swift in Sources */, 9AB6D64A218727D60008F274 /* PostServiceRemoteRESTRevisionsTest.swift in Sources */, 7430C9BD1F192C0F0051B8E6 /* ReaderPostServiceRemoteTests.m in Sources */, + 1DC837C229B9F04F009DCD4B /* RemoteVideoPressVideoTests.swift in Sources */, FAD1345125909DEA00A8FEB1 /* JetpackBackupServiceRemoteTests.swift in Sources */, 8B2F4BE924ABC9DC0056C08A /* ReaderPostServiceRemote+CardsTests.swift in Sources */, 40F9880C221ACEEE00B7B369 /* StatsRemoteV2Tests.swift in Sources */, diff --git a/WordPressKitTests/Models/RemoteVideoPressVideoTests.swift b/WordPressKitTests/Models/RemoteVideoPressVideoTests.swift new file mode 100644 index 00000000..aae53fa0 --- /dev/null +++ b/WordPressKitTests/Models/RemoteVideoPressVideoTests.swift @@ -0,0 +1,84 @@ +import XCTest +@testable import WordPressKit + +class RemoteVideoPressVideoTests: XCTestCase { + + func mockVideoPressMetadata(_ id: String) -> NSDictionary { + return [ + "title": "VideoPress demo", + "description": "asd", + "width": 1280, + "height": 720, + "duration": 143700, + "display_embed": true, + "allow_download": false, + "rating": "G", + "poster": "https://videos.files.wordpress.com/\(id)/videopress2-web2_hd.original.jpg", + "original": "https://videos.files.wordpress.com/\(id)/videopress2-web2.mov", + "watermark": "https://wptv.files.wordpress.com/2010/07/wptv.png", + "bg_color": "", + "blog_id": 5089392, + "post_id": 1913, + "finished": true + ] + } + + func testInit() { + let id = "AbCdE" + let metadata = mockVideoPressMetadata(id) + let video = RemoteVideoPressVideo(dictionary: metadata, id: id) + XCTAssertEqual(video.title, metadata["title"] as? String) + XCTAssertEqual(video.videoDescription, metadata["description"] as? String) + XCTAssertEqual(video.width, metadata["width"] as? Int) + XCTAssertEqual(video.height, metadata["height"] as? Int) + XCTAssertEqual(video.duration, metadata["duration"] as? Int) + XCTAssertEqual(video.displayEmbed, metadata["display_embed"] as? Bool) + XCTAssertEqual(video.allowDownload, metadata["allow_download"] as? Bool) + XCTAssertEqual(video.rating, metadata["rating"] as? String) + XCTAssertEqual(video.posterURL, URL(string: metadata["poster"] as! String)) + XCTAssertEqual(video.originalURL, URL(string: metadata["original"] as! String)) + XCTAssertEqual(video.watermarkURL, URL(string: metadata["watermark"] as! String)) + XCTAssertEqual(video.bgColor, metadata["bg_color"] as? String) + XCTAssertEqual(video.blogId, metadata["blog_id"] as? Int) + XCTAssertEqual(video.postId, metadata["post_id"] as? Int) + XCTAssertEqual(video.finished, metadata["finished"] as? Bool) + } + + func testGetURLWithToken() { + let id = "AbCdE" + let metadata = mockVideoPressMetadata(id) + let token = "videopress-token" + let video = RemoteVideoPressVideo(dictionary: metadata, id: id) + let originalURL = video.originalURL!.absoluteString + + video.token = token + XCTAssertEqual(video.getURLWithToken(url: video.originalURL), URL(string: "\(originalURL)?metadata_token=\(token)")) + + video.token = nil + XCTAssertEqual(video.getURLWithToken(url: video.originalURL), video.originalURL) + + XCTAssertNil(video.getURLWithToken(url: nil)) + } + + func testAsDictionary() throws { + let id = "AbCdE" + let metadata = mockVideoPressMetadata(id) + let video = RemoteVideoPressVideo(dictionary: metadata, id: id) + let dict = video.asDictionary() + XCTAssertEqual(video.title, dict["title"] as? String) + XCTAssertEqual(video.videoDescription, dict["description"] as? String) + XCTAssertEqual(video.width, dict["width"] as? Int) + XCTAssertEqual(video.height, dict["height"] as? Int) + XCTAssertEqual(video.duration, dict["duration"] as? Int) + XCTAssertEqual(video.displayEmbed, dict["displayEmbed"] as? Bool) + XCTAssertEqual(video.allowDownload, dict["allowDownload"] as? Bool) + XCTAssertEqual(video.rating, dict["rating"] as? String) + XCTAssertEqual(video.posterURL?.absoluteString, dict["posterURL"] as? String) + XCTAssertEqual(video.originalURL?.absoluteString, dict["originalURL"] as? String) + XCTAssertEqual(video.watermarkURL?.absoluteString, dict["watermarkURL"] as? String) + XCTAssertEqual(video.bgColor, dict["bgColor"] as? String) + XCTAssertEqual(video.blogId, dict["blogId"] as? Int) + XCTAssertEqual(video.postId, dict["postId"] as? Int) + XCTAssertEqual(video.finished, dict["finished"] as? Bool) + } +} From 4ccbf10b82f5f7dc5e63d51c5cacafaf8198821b Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Thu, 9 Mar 2023 12:36:46 +0100 Subject: [PATCH 14/17] Fix description property name in RemoteVideoPressVideo model --- WordPressKit/RemoteVideoPressVideo.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressKit/RemoteVideoPressVideo.swift b/WordPressKit/RemoteVideoPressVideo.swift index 3bd02e83..db70c444 100644 --- a/WordPressKit/RemoteVideoPressVideo.swift +++ b/WordPressKit/RemoteVideoPressVideo.swift @@ -47,7 +47,7 @@ import Foundation self.id = id title = metadataDict.string(forKey: "title") - videoDescription = metadataDict.string(forKey: "descrption") + videoDescription = metadataDict.string(forKey: "description") width = metadataDict.number(forKey: "width")?.intValue height = metadataDict.number(forKey: "height")?.intValue duration = metadataDict.number(forKey: "duration")?.intValue From d77b9479c0c1a9e13493229f03ad67aa12ba540a Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 13 Mar 2023 11:53:40 +0100 Subject: [PATCH 15/17] Update `getURLWithToken` to return `nil` when no token --- WordPressKit/RemoteVideoPressVideo.swift | 17 ++++++----------- .../Models/RemoteVideoPressVideoTests.swift | 8 +++----- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/WordPressKit/RemoteVideoPressVideo.swift b/WordPressKit/RemoteVideoPressVideo.swift index db70c444..4ecebf68 100644 --- a/WordPressKit/RemoteVideoPressVideo.swift +++ b/WordPressKit/RemoteVideoPressVideo.swift @@ -76,20 +76,15 @@ import Foundation /// - Parameters: /// - url: URL to include the token. /// - /// - Returns: The specified URL with the token as a query parameter. + /// - Returns: The specified URL with the token as a query parameter. It will return `nil` if the token is not present. @objc(getURLWithToken:) - public func getURLWithToken(url: URL?) -> URL? { - guard var urlWithToken = url else { + public func getURLWithToken(url: URL) -> URL? { + guard let token, var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: true) else { return nil } - if let token = self.token, var urlComponents = URLComponents(url: urlWithToken, resolvingAgainstBaseURL: true) { - let metadataTokenParam = URLQueryItem(name: "metadata_token", value: token) - var queryItems: [URLQueryItem] = urlComponents.queryItems ?? [] - queryItems.append(metadataTokenParam) - urlComponents.queryItems = queryItems - urlWithToken = urlComponents.url! - } - return urlWithToken + let metadataTokenParam = URLQueryItem(name: "metadata_token", value: token) + urlComponents.queryItems = (urlComponents.queryItems ?? []) + [metadataTokenParam] + return urlComponents.url } public func encode(to encoder: Encoder) throws { diff --git a/WordPressKitTests/Models/RemoteVideoPressVideoTests.swift b/WordPressKitTests/Models/RemoteVideoPressVideoTests.swift index aae53fa0..229bb8bc 100644 --- a/WordPressKitTests/Models/RemoteVideoPressVideoTests.swift +++ b/WordPressKitTests/Models/RemoteVideoPressVideoTests.swift @@ -49,15 +49,13 @@ class RemoteVideoPressVideoTests: XCTestCase { let metadata = mockVideoPressMetadata(id) let token = "videopress-token" let video = RemoteVideoPressVideo(dictionary: metadata, id: id) - let originalURL = video.originalURL!.absoluteString + let originalURL = video.originalURL! video.token = token - XCTAssertEqual(video.getURLWithToken(url: video.originalURL), URL(string: "\(originalURL)?metadata_token=\(token)")) + XCTAssertEqual(video.getURLWithToken(url: originalURL), URL(string: "\(originalURL.absoluteString)?metadata_token=\(token)")) video.token = nil - XCTAssertEqual(video.getURLWithToken(url: video.originalURL), video.originalURL) - - XCTAssertNil(video.getURLWithToken(url: nil)) + XCTAssertNil(video.getURLWithToken(url: originalURL)) } func testAsDictionary() throws { From b901dc8f0ae251f2698a1bfa2e7c2a1c4599e6c1 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 13 Mar 2023 11:58:59 +0100 Subject: [PATCH 16/17] Allow auto syntheses of the `Encodable` protocol in `RemoteVideoPressVideo` --- WordPressKit/RemoteVideoPressVideo.swift | 25 +----------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/WordPressKit/RemoteVideoPressVideo.swift b/WordPressKit/RemoteVideoPressVideo.swift index 4ecebf68..7613136a 100644 --- a/WordPressKit/RemoteVideoPressVideo.swift +++ b/WordPressKit/RemoteVideoPressVideo.swift @@ -40,7 +40,7 @@ import Foundation public var token: String? enum CodingKeys: String, CodingKey { - case id, title, description, width, height, duration, displayEmbed, allowDownload, rating, privacySetting, posterURL, originalURL, watermarkURL, bgColor, blogId, postId, finished, token + case id, title, videoDescription = "description", width, height, duration, displayEmbed, allowDownload, rating, privacySetting, posterURL, originalURL, watermarkURL, bgColor, blogId, postId, finished, token } public init(dictionary metadataDict: NSDictionary, id: String) { @@ -87,29 +87,6 @@ import Foundation return urlComponents.url } - public func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - - try container.encode(id, forKey: .id) - try container.encode(title, forKey: .title) - try container.encode(videoDescription, forKey: .description) - try container.encode(width, forKey: .width) - try container.encode(height, forKey: .height) - try container.encode(duration, forKey: .duration) - try container.encode(displayEmbed, forKey: .displayEmbed) - try container.encode(allowDownload, forKey: .allowDownload) - try container.encode(rating, forKey: .rating) - try container.encode(privacySetting, forKey: .privacySetting) - try container.encode(posterURL, forKey: .posterURL) - try container.encode(originalURL, forKey: .originalURL) - try container.encode(watermarkURL, forKey: .watermarkURL) - try container.encode(bgColor, forKey: .bgColor) - try container.encode(blogId, forKey: .blogId) - try container.encode(postId, forKey: .postId) - try container.encode(finished, forKey: .finished) - try container.encode(token, forKey: .token) - } - public func asDictionary() -> [String: Any] { guard let data = try? JSONEncoder().encode(self), From 7946aa5616737c2fac6be14d843419a2f01afba8 Mon Sep 17 00:00:00 2001 From: Carlos Garcia Date: Mon, 13 Mar 2023 12:00:08 +0100 Subject: [PATCH 17/17] Bump major version in podspec due to breaking change --- WordPressKit.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WordPressKit.podspec b/WordPressKit.podspec index f1a902fd..1de625f6 100644 --- a/WordPressKit.podspec +++ b/WordPressKit.podspec @@ -2,7 +2,7 @@ Pod::Spec.new do |s| s.name = 'WordPressKit' - s.version = '6.3.0-beta.1' + s.version = '7.0.0-beta.1' s.summary = 'WordPressKit offers a clean and simple WordPress.com and WordPress.org API.' s.description = <<-DESC