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

[Media] Migrate WPCom media upload to /wp/v2 endpoints #14537

Merged
merged 8 commits into from
Nov 29, 2024
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
12 changes: 4 additions & 8 deletions Networking/Networking.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
020D07BC23D856BF00FD9580 /* MediaRemote.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020D07BB23D856BF00FD9580 /* MediaRemote.swift */; };
020D07BE23D8570800FD9580 /* MediaListMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020D07BD23D8570800FD9580 /* MediaListMapper.swift */; };
020D07C023D8587700FD9580 /* MediaRemoteTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020D07BF23D8587700FD9580 /* MediaRemoteTests.swift */; };
020D07C223D858BB00FD9580 /* media-upload.json in Resources */ = {isa = PBXBuildFile; fileRef = 020D07C123D858BB00FD9580 /* media-upload.json */; };
020D0C03291504DE00BB3DCE /* UnauthenticatedRequestTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020D0C02291504DE00BB3DCE /* UnauthenticatedRequestTests.swift */; };
020EF5E92A8BC957009D2169 /* ProductsTotalMapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 020EF5E82A8BC957009D2169 /* ProductsTotalMapper.swift */; };
020EF5ED2A8C8F4F009D2169 /* products-total.json in Resources */ = {isa = PBXBuildFile; fileRef = 020EF5EC2A8C8F4F009D2169 /* products-total.json */; };
Expand Down Expand Up @@ -95,7 +94,6 @@
02AED9DC2AA04716006DC460 /* KeyedDecodingContainer+WooTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AED9DB2AA04716006DC460 /* KeyedDecodingContainer+WooTests.swift */; };
02AF07EA27492DBC00B2D81E /* WordPressMedia.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02AF07E927492DBC00B2D81E /* WordPressMedia.swift */; };
02AF07EC27492FDD00B2D81E /* media-library-from-wordpress-site.json in Resources */ = {isa = PBXBuildFile; fileRef = 02AF07EB27492FDD00B2D81E /* media-library-from-wordpress-site.json */; };
02AF07EE27493AE700B2D81E /* media-upload-to-wordpress-site.json in Resources */ = {isa = PBXBuildFile; fileRef = 02AF07ED27493AE700B2D81E /* media-upload-to-wordpress-site.json */; };
02B41A90296BC85800FE3311 /* site-domains.json in Resources */ = {isa = PBXBuildFile; fileRef = 02B41A8F296BC85800FE3311 /* site-domains.json */; };
02B41A92296BEB3000FE3311 /* load-site-current-plan-success.json in Resources */ = {isa = PBXBuildFile; fileRef = 02B41A91296BEB3000FE3311 /* load-site-current-plan-success.json */; };
02B41A94296C04BC00FE3311 /* load-site-plans-no-current-plan.json in Resources */ = {isa = PBXBuildFile; fileRef = 02B41A93296C04BC00FE3311 /* load-site-plans-no-current-plan.json */; };
Expand Down Expand Up @@ -1249,6 +1247,7 @@
EECB7EE8286555180028C888 /* media-update-product-id.json in Resources */ = {isa = PBXBuildFile; fileRef = EECB7EE7286555180028C888 /* media-update-product-id.json */; };
EECDBEDE296845F400293C4E /* RESTRequestConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECDBEDD296845F400293C4E /* RESTRequestConvertible.swift */; };
EECDBEE029684AC500293C4E /* WordPressAPIVersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EECDBEDF29684AC500293C4E /* WordPressAPIVersionTests.swift */; };
EED25B1D2CF74B9800503657 /* media-upload.json in Resources */ = {isa = PBXBuildFile; fileRef = EED25B1C2CF74B9800503657 /* media-upload.json */; };
EEDADD282B7C6A5E00F7302B /* media.json in Resources */ = {isa = PBXBuildFile; fileRef = EEDADD272B7C6A5E00F7302B /* media.json */; };
EEE846A22A54745F005239B6 /* identify-language-success.json in Resources */ = {isa = PBXBuildFile; fileRef = EEE846A02A54745F005239B6 /* identify-language-success.json */; };
EEE846A32A54745F005239B6 /* identify-language-failure.json in Resources */ = {isa = PBXBuildFile; fileRef = EEE846A12A54745F005239B6 /* identify-language-failure.json */; };
Expand Down Expand Up @@ -1306,7 +1305,6 @@
020D07BB23D856BF00FD9580 /* MediaRemote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaRemote.swift; sourceTree = "<group>"; };
020D07BD23D8570800FD9580 /* MediaListMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaListMapper.swift; sourceTree = "<group>"; };
020D07BF23D8587700FD9580 /* MediaRemoteTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaRemoteTests.swift; sourceTree = "<group>"; };
020D07C123D858BB00FD9580 /* media-upload.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "media-upload.json"; sourceTree = "<group>"; };
020D0C02291504DE00BB3DCE /* UnauthenticatedRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnauthenticatedRequestTests.swift; sourceTree = "<group>"; };
020EF5E82A8BC957009D2169 /* ProductsTotalMapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProductsTotalMapper.swift; sourceTree = "<group>"; };
020EF5EC2A8C8F4F009D2169 /* products-total.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "products-total.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1382,7 +1380,6 @@
02AED9DB2AA04716006DC460 /* KeyedDecodingContainer+WooTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "KeyedDecodingContainer+WooTests.swift"; sourceTree = "<group>"; };
02AF07E927492DBC00B2D81E /* WordPressMedia.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordPressMedia.swift; sourceTree = "<group>"; };
02AF07EB27492FDD00B2D81E /* media-library-from-wordpress-site.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "media-library-from-wordpress-site.json"; sourceTree = "<group>"; };
02AF07ED27493AE700B2D81E /* media-upload-to-wordpress-site.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "media-upload-to-wordpress-site.json"; sourceTree = "<group>"; };
02B41A8F296BC85800FE3311 /* site-domains.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "site-domains.json"; sourceTree = "<group>"; };
02B41A91296BEB3000FE3311 /* load-site-current-plan-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "load-site-current-plan-success.json"; sourceTree = "<group>"; };
02B41A93296C04BC00FE3311 /* load-site-plans-no-current-plan.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "load-site-plans-no-current-plan.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2437,6 +2434,7 @@
EECB7EE7286555180028C888 /* media-update-product-id.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "media-update-product-id.json"; sourceTree = "<group>"; };
EECDBEDD296845F400293C4E /* RESTRequestConvertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RESTRequestConvertible.swift; sourceTree = "<group>"; };
EECDBEDF29684AC500293C4E /* WordPressAPIVersionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordPressAPIVersionTests.swift; sourceTree = "<group>"; };
EED25B1C2CF74B9800503657 /* media-upload.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "media-upload.json"; sourceTree = "<group>"; };
EEDADD272B7C6A5E00F7302B /* media.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = media.json; sourceTree = "<group>"; };
EEE846A02A54745F005239B6 /* identify-language-success.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "identify-language-success.json"; sourceTree = "<group>"; };
EEE846A12A54745F005239B6 /* identify-language-failure.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "identify-language-failure.json"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3086,6 +3084,7 @@
B559EBA820A0B5B100836CD4 /* Responses */ = {
isa = PBXGroup;
children = (
EED25B1C2CF74B9800503657 /* media-upload.json */,
EE6C6B6F2C6A190500632BDA /* systemStatus-inconsistent-page-id-data-type.json */,
DEB3878E2C2D71A10025256E /* gla-campaign-list-with-data-envelope.json */,
DEB3878D2C2D71A10025256E /* gla-campaign-list-without-data-envelope.json */,
Expand Down Expand Up @@ -3268,8 +3267,6 @@
02F096C12406691100C0C1D5 /* media-library.json */,
02AF07EB27492FDD00B2D81E /* media-library-from-wordpress-site.json */,
EECB7EE7286555180028C888 /* media-update-product-id.json */,
020D07C123D858BB00FD9580 /* media-upload.json */,
02AF07ED27493AE700B2D81E /* media-upload-to-wordpress-site.json */,
B58D10C92114D22E00107ED4 /* new-order-note.json */,
022902D322E2436400059692 /* no_stats_permission_error.json */,
B5A24178217F98F600595DEF /* notifications-load-all.json */,
Expand Down Expand Up @@ -4285,7 +4282,6 @@
EE57C137297F98DB00BC31E7 /* product-variations-load-all-without-data.json in Resources */,
09885C8027C3FFD200910A62 /* product-variations-bulk-update.json in Resources */,
31054734262E36AB00C5C02B /* wcpay-payment-intent-error.json in Resources */,
02AF07EE27493AE700B2D81E /* media-upload-to-wordpress-site.json in Resources */,
CCE5F38F29EFFE3800087332 /* subscription-list.json in Resources */,
DE4F2A4029750EF400B0701C /* inbox-note-without-data.json in Resources */,
45ED4F12239E8C57004F1BE3 /* taxes-classes.json in Resources */,
Expand Down Expand Up @@ -4622,6 +4618,7 @@
02E7FFCF25621C7900C53030 /* shipping-label-print.json in Resources */,
CE12AE9529F29F4F0056DD17 /* order-with-subscription-renewal.json in Resources */,
CEE9188129F7DC23004B23FF /* order-with-gift-cards.json in Resources */,
EED25B1D2CF74B9800503657 /* media-upload.json in Resources */,
74ABA1CB213F19FE00FFAD30 /* top-performers-week.json in Resources */,
B9CA4F332AB213DF00285AB9 /* tax.json in Resources */,
456930AD2652AF00009ED69D /* shipping-label-carriers-and-rates-success.json in Resources */,
Expand Down Expand Up @@ -4696,7 +4693,6 @@
0359EA2127AAE58C0048DE2D /* wcpay-charge-card-present.json in Resources */,
451274A625276C82009911FF /* product-variation.json in Resources */,
CC80E3F92948C8BC00D5FF45 /* site-visits-quarter.json in Resources */,
020D07C223D858BB00FD9580 /* media-upload.json in Resources */,
CE71E23B2A4C666A00DB5376 /* reports-products-alt.json in Resources */,
31A451D127863A2E00FE81AA /* stripe-account-rejected-other.json in Resources */,
DEF13C5029629EEA0024A02B /* user-complete-without-data.json in Resources */,
Expand Down
59 changes: 7 additions & 52 deletions Networking/Networking/Remote/MediaRemote.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,10 @@ public protocol MediaRemoteProtocol {
pageNumber: Int,
pageSize: Int,
completion: @escaping (Result<[WordPressMedia], Error>) -> Void)
func uploadMedia(for siteID: Int64,
func uploadMedia(siteID: Int64,
productID: Int64,
mediaItems: [UploadableMedia],
completion: @escaping (Result<[Media], Error>) -> Void)
func uploadMediaToWordPressSite(siteID: Int64,
productID: Int64,
mediaItem: UploadableMedia,
completion: @escaping (Result<WordPressMedia, Error>) -> Void)
mediaItem: UploadableMedia,
completion: @escaping (Result<WordPressMedia, Error>) -> Void)
func updateProductID(siteID: Int64,
productID: Int64,
mediaID: Int64,
Expand Down Expand Up @@ -145,47 +141,6 @@ public class MediaRemote: Remote, MediaRemoteProtocol {
}
}

/// Uploads an array of media in the local file system.
/// API reference: https://developer.wordpress.com/docs/api/1.1/post/sites/%24site/media/new/
///
/// - Parameters:
/// - siteID: Site for which we'll upload the media to.
/// - productID: Product for which the media items are first added to.
/// - context: Display or edit. Scope under which the request is made;
/// determines fields present in response. Default is Display.
/// - mediaItems: An array of uploadable media items.
/// - completion: Closure to be executed upon completion.
///
public func uploadMedia(for siteID: Int64,
productID: Int64,
mediaItems: [UploadableMedia],
completion: @escaping (Result<[Media], Error>) -> Void) {

let formParameters: [String: String] = [Int](0..<mediaItems.count).reduce(into: [:]) { (parentIDsByKey, index) in
parentIDsByKey["attrs[\(index)][parent_id]"] = "\(productID)"
parentIDsByKey["attrs[\(index)][\(ParameterKey.altText)]"] = mediaItems[index].altText
}

let path = "sites/\(siteID)/media/new"
let request = DotcomRequest(wordpressApiVersion: .mark1_1,
method: .post,
path: path)
let mapper = MediaListMapper()

enqueueMultipartFormDataUpload(request, mapper: mapper, multipartFormData: { multipartFormData in
formParameters.forEach { (key, value) in
multipartFormData.append(Data(value.utf8), withName: key)
}

mediaItems.forEach { mediaItem in
multipartFormData.append(mediaItem.localURL,
withName: "media[]",
fileName: mediaItem.filename,
mimeType: mediaItem.mimeType)
}
}, completion: completion)
}

/// Uploads a media item in the local file system to the WordPress site via WordPress site API.
/// The API does not support multiple media items unlike the WPCOM version in `uploadMedia`.
/// API reference: https://developer.wordpress.org/rest-api/reference/media/#create-a-media-item
Expand All @@ -195,10 +150,10 @@ public class MediaRemote: Remote, MediaRemoteProtocol {
/// - productID: Product for which the media items are first added to.
/// - mediaItem: The media item to upload.
/// - completion: Closure to be executed upon completion.
public func uploadMediaToWordPressSite(siteID: Int64,
productID: Int64,
mediaItem: UploadableMedia,
completion: @escaping (Result<WordPressMedia, Error>) -> Void) {
public func uploadMedia(siteID: Int64,
productID: Int64,
mediaItem: UploadableMedia,
completion: @escaping (Result<WordPressMedia, Error>) -> Void) {
let formParameters: [String: String] = [
ParameterKey.wordPressMediaPostID: "\(productID)",
ParameterKey.fieldsWordPressSite: ParameterValue.wordPressMediaFields,
Expand Down
69 changes: 8 additions & 61 deletions Networking/NetworkingTests/Remote/MediaRemoteTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ final class MediaRemoteTests: XCTestCase {
let remote = MediaRemote(network: network)

// When
remote.uploadMedia(for: self.sampleSiteID, productID: sampleProductID, mediaItems: [], completion: { _ in })
remote.uploadMedia(siteID: sampleSiteID, productID: sampleProductID, mediaItem: .fake(), completion: { _ in })

// Then
let request = try XCTUnwrap(network.requestsForResponseData.last as? DotcomRequest)
Expand All @@ -222,69 +222,16 @@ final class MediaRemoteTests: XCTestCase {
/// Verifies that `uploadMedia` properly parses the `media-upload` sample response.
///
func test_uploadMedia_properly_returns_parsed_media() throws {
// Given
let remote = MediaRemote(network: network)
let path = "sites/\(sampleSiteID)/media/new"
network.simulateResponse(requestUrlSuffix: path, filename: "media-upload")

// When
let result = waitFor { promise in
remote.uploadMedia(for: self.sampleSiteID,
productID: self.sampleProductID,
mediaItems: []) { result in
promise(result)
}
}

// Then
let mediaItems = try XCTUnwrap(result.get())
XCTAssertEqual(mediaItems.count, 1)
}

/// Verifies that `uploadMedia` properly relays Networking Layer errors.
///
func test_uploadMedia_properly_relays_networking_errors() {
// Given
let remote = MediaRemote(network: network)

// When
let result = waitFor { promise in
remote.uploadMedia(for: self.sampleSiteID,
productID: self.sampleProductID,
mediaItems: []) { result in
promise(result)
}
}

// Then
XCTAssertTrue(result.isFailure)
}

func test_uploadMediaToWordPressSite_does_not_send_data_in_request_body() throws {
// Given
let remote = MediaRemote(network: network)

// When
remote.uploadMediaToWordPressSite(siteID: sampleSiteID, productID: sampleProductID, mediaItem: .fake(), completion: { _ in })

// Then
let request = try XCTUnwrap(network.requestsForResponseData.last as? DotcomRequest)
XCTAssertNil(try request.asURLRequest().httpBody)
}

/// Verifies that `uploadMediaToWordPressSite` properly parses the `media-upload-to-wordpress-site` sample response.
///
func test_uploadMediaToWordPressSite_properly_returns_parsed_media() throws {
// Given
let remote = MediaRemote(network: network)
let path = "sites/\(sampleSiteID)/media"
network.simulateResponse(requestUrlSuffix: path, filename: "media-upload-to-wordpress-site")
network.simulateResponse(requestUrlSuffix: path, filename: "media-upload")

// When
let result = waitFor { promise in
remote.uploadMediaToWordPressSite(siteID: self.sampleSiteID,
productID: self.sampleProductID,
mediaItem: .fake()) { result in
remote.uploadMedia(siteID: self.sampleSiteID,
productID: self.sampleProductID,
mediaItem: .fake()) { result in
promise(result)
}
}
Expand Down Expand Up @@ -316,9 +263,9 @@ final class MediaRemoteTests: XCTestCase {

// When
let result = waitFor { promise in
remote.uploadMediaToWordPressSite(siteID: self.sampleSiteID,
productID: self.sampleProductID,
mediaItem: .fake()) { result in
remote.uploadMedia(siteID: self.sampleSiteID,
productID: self.sampleProductID,
mediaItem: .fake()) { result in
promise(result)
}
}
Expand Down
Loading