Skip to content

Create /media endpoint #410

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

Merged
merged 8 commits into from
Dec 2, 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
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion native/kotlin/api/kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -115,13 +115,13 @@ val generateUniFFIBindingsTask = tasks.register<Exec>("generateUniFFIBindings")
inputs.dir("$cargoProjectRoot/$rustModuleName/")
}


tasks.named("compileKotlin").configure {
dependsOn(generateUniFFIBindingsTask)
}
tasks.named("processIntegrationTestResources").configure {
dependsOn(rootProject.tasks.named("copyDesktopJniLibs"))
dependsOn(rootProject.tasks.named("copyTestCredentials"))
dependsOn(rootProject.tasks.named("copyTestMedia"))
}

project.afterEvaluate {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package rs.wordpress.api.kotlin

import okhttp3.OkHttpClient
import okhttp3.Request
import uniffi.wp_api.UserId
import uniffi.wp_api.WpErrorCode

Expand All @@ -26,3 +28,9 @@ fun <T> WpRequestResult<T>.wpErrorCode(): WpErrorCode {
assert(this is WpRequestResult.WpError)
return (this as WpRequestResult.WpError).errorCode
}

fun restoreTestServer() {
OkHttpClient().newCall(
Request.Builder().url("http://localhost:4000/restore?db=true&plugins=true").build()
).execute()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ package rs.wordpress.api.kotlin
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Assertions.assertNull
import org.junit.jupiter.api.Test
import uniffi.wp_api.MediaCreateParams
import uniffi.wp_api.MediaListParams
import uniffi.wp_api.SparseMediaFieldWithEditContext
import uniffi.wp_api.wpAuthenticationFromUsernameAndPassword
import kotlin.test.assertEquals
import kotlin.test.assertNotNull

private const val MEDIA_ID_611: Long = 611
Expand Down Expand Up @@ -63,4 +65,18 @@ class MediaEndpointTest {
assertNotNull(sparseMedia)
assertNull(sparseMedia.slug)
}

@Test
fun testCreateMediaRequest() = runTest {
val title = "Testing media upload from Kotlin"
val response = client.request { requestBuilder ->
requestBuilder.media().create(
params = MediaCreateParams(title = title),
"test_media.jpg",
"image/jpg"
)
}.assertSuccessAndRetrieveData().data
assertEquals(title, response.title.rendered)
restoreTestServer()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ constructor(
statusCode = exception.statusCode,
reason = exception.reason
)
is WpApiException.MediaFileNotFound -> WpRequestResult.MediaFileNotFound(
filePath = exception.filePath
)
is WpApiException.ResponseParsingException -> WpRequestResult.ResponseParsingError(
reason = exception.reason,
response = exception.response,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@ package rs.wordpress.api.kotlin
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import uniffi.wp_api.MediaUploadRequest
import uniffi.wp_api.MediaUploadRequestExecutionException
import uniffi.wp_api.RequestExecutor
import uniffi.wp_api.WpNetworkHeaderMap
import uniffi.wp_api.WpNetworkRequest
import uniffi.wp_api.WpNetworkResponse
import java.io.File

class WpRequestExecutor(
private val okHttpClient: OkHttpClient = OkHttpClient(),
Expand All @@ -29,6 +35,45 @@ class WpRequestExecutor(
}
}

okHttpClient.newCall(requestBuilder.build()).execute().use { response ->
return@withContext WpNetworkResponse(
body = response.body?.bytes() ?: ByteArray(0),
statusCode = response.code.toUShort(),
headerMap = WpNetworkHeaderMap.fromMultiMap(response.headers.toMultimap())
)
}
}

override suspend fun uploadMedia(mediaUploadRequest: MediaUploadRequest): WpNetworkResponse =
withContext(dispatcher) {
val requestBuilder = Request.Builder().url(mediaUploadRequest.url())
val multipartBodyBuilder = MultipartBody.Builder()
.setType(MultipartBody.FORM)
mediaUploadRequest.mediaParams().forEach { (k, v) ->
multipartBodyBuilder.addFormDataPart(k, v)
}
val filePath = mediaUploadRequest.filePath()
val file =
WpRequestExecutor::class.java.classLoader?.getResource(filePath)?.file?.let {
File(
it
)
} ?: throw MediaUploadRequestExecutionException.MediaFileNotFound(filePath)
multipartBodyBuilder.addFormDataPart(
name = "file",
filename = file.name,
body = file.asRequestBody(mediaUploadRequest.fileContentType().toMediaType())
)
requestBuilder.method(
method = mediaUploadRequest.method().toString(),
body = multipartBodyBuilder.build()
)
mediaUploadRequest.headerMap().toMap().forEach { (key, values) ->
values.forEach { value ->
requestBuilder.addHeader(key, value)
}
}

okHttpClient.newCall(requestBuilder.build()).execute().use { response ->
return@withContext WpNetworkResponse(
body = response.body?.bytes() ?: ByteArray(0),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ sealed class WpRequestResult<T> {
val reason: String,
) : WpRequestResult<T>()

class MediaFileNotFound<T>(
val filePath: String
) : WpRequestResult<T>()

class SiteUrlParsingError<T>(
val reason: String,
) : WpRequestResult<T>()
Expand Down
11 changes: 11 additions & 0 deletions native/kotlin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,21 @@ fun setupJniAndBindings() {
into(jniLibsPath)
}

tasks.register<Delete>("deleteTestResources") {
delete = setOf(generatedTestResourcesPath)
}

tasks.register<Copy>("copyTestCredentials") {
dependsOn(tasks.named("deleteTestResources"))
from("$cargoProjectRoot/test_credentials.json")
into(generatedTestResourcesPath)
}

tasks.register<Copy>("copyTestMedia") {
dependsOn(tasks.named("deleteTestResources"))
from("$cargoProjectRoot/test_media.jpg")
into(generatedTestResourcesPath)
}
}

fun getNativeLibraryExtension(): String {
Expand Down
1 change: 1 addition & 0 deletions native/swift/Sources/wordpress-api/Exports.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ public typealias PostsRequestListWithEmbedContextResponse = WordPressAPIInternal

// MARK: - Media
public typealias SparseMedia = WordPressAPIInternal.SparseMedia
public typealias MediaUploadRequest = WordPressAPIInternal.MediaUploadRequest
public typealias MediaWithEditContext = WordPressAPIInternal.MediaWithEditContext
public typealias MediaWithViewContext = WordPressAPIInternal.MediaWithViewContext
public typealias MediaWithEmbedContext = WordPressAPIInternal.MediaWithEmbedContext
Expand Down
6 changes: 5 additions & 1 deletion native/swift/Sources/wordpress-api/SafeRequestExecutor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ extension SafeRequestExecutor {

}

extension URLSession: RequestExecutor {}
extension URLSession: RequestExecutor {
public func uploadMedia(mediaUploadRequest: MediaUploadRequest) async throws -> WpNetworkResponse {
try WpNetworkResponse(body: Data(), statusCode: 500, headerMap: .fromMap(hashMap: [:]))
}
}

extension URLSession: SafeRequestExecutor {

Expand Down
4 changes: 4 additions & 0 deletions native/swift/Tests/wordpress-api/Support/HTTPStubs.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ final class HTTPStubs: SafeRequestExecutor {
}
}

func uploadMedia(mediaUploadRequest: MediaUploadRequest) async throws -> WpNetworkResponse {
try WpNetworkResponse(body: Data(), statusCode: 500, headerMap: .fromMap(hashMap: [:]))
}

private func stub(for request: WpNetworkRequest) -> WpNetworkResponse? {
stubs.first { stub in stub.condition(request) }?
.response
Expand Down
Binary file added test_media.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 34 additions & 0 deletions wp_api/src/api_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub enum WpApiError {
status_code: Option<u16>,
reason: String,
},
#[error("Media file not found at file path: {}", file_path)]
MediaFileNotFound { file_path: String },
#[error("Error while parsing. \nReason: {}\nResponse: {}", reason, response)]
ResponseParsingError { reason: String, response: String },
#[error("Error while parsing site url: {}", reason)]
Expand Down Expand Up @@ -320,6 +322,21 @@ pub enum RequestExecutionError {
},
}

#[derive(Debug, PartialEq, Eq, thiserror::Error, uniffi::Error)]
pub enum MediaUploadRequestExecutionError {
#[error(
"Request execution failed!\nStatus Code: '{:?}'.\nResponse: '{}'",
status_code,
reason
)]
RequestExecutionFailed {
status_code: Option<u16>,
reason: String,
},
#[error("Media file not found at file path: {}", file_path)]
MediaFileNotFound { file_path: String },
}

impl From<RequestExecutionError> for WpApiError {
fn from(value: RequestExecutionError) -> Self {
match value {
Expand All @@ -333,3 +350,20 @@ impl From<RequestExecutionError> for WpApiError {
}
}
}

impl From<MediaUploadRequestExecutionError> for WpApiError {
fn from(value: MediaUploadRequestExecutionError) -> Self {
match value {
MediaUploadRequestExecutionError::RequestExecutionFailed {
status_code,
reason,
} => Self::RequestExecutionFailed {
status_code,
reason,
},
MediaUploadRequestExecutionError::MediaFileNotFound { file_path } => {
Self::MediaFileNotFound { file_path }
}
}
}
}
5 changes: 4 additions & 1 deletion wp_api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#![allow(dead_code, unused_variables)]

pub use api_client::{WpApiClient, WpApiRequestBuilder};
pub use api_error::{ParsedRequestError, RequestExecutionError, WpApiError, WpError, WpErrorCode};
pub use api_error::{
MediaUploadRequestExecutionError, ParsedRequestError, RequestExecutionError, WpApiError,
WpError, WpErrorCode,
};
pub use parsed_url::{ParseUrlError, ParsedUrl};
use plugins::*;
use serde::{Deserialize, Serialize};
Expand Down
Loading
Loading