From 51b5b35804ada0d2815bb4ca0687605836cc8da9 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 13:42:37 -0500 Subject: [PATCH 01/18] Remove Rust integration that was used for testing from Android project --- .../kotlin/wordpress/rs/LibraryTest.kt | 12 ------------ .../lib/src/main/kotlin/wordpress/rs/Library.kt | 15 --------------- .../src/test/kotlin/wordpress/rs/LibraryTest.kt | 12 ------------ 3 files changed, 39 deletions(-) diff --git a/native/android/lib/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt b/native/android/lib/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt index 458b8e6ad..c6172ef6d 100644 --- a/native/android/lib/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt +++ b/native/android/lib/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt @@ -3,22 +3,10 @@ */ package wordpress.rs -import org.junit.Assert.assertEquals import org.junit.Before -import org.junit.Test class LibraryTest { @Before fun setup() { } - - @Test - fun testAddCustom() { - assertEquals(Library().addCustomFromRust(2, 4), 6) - } - - @Test - fun testCombineStrings() { - assertEquals(Library().combineStringsFromRust("this", "that"), "this-that") - } } diff --git a/native/android/lib/src/main/kotlin/wordpress/rs/Library.kt b/native/android/lib/src/main/kotlin/wordpress/rs/Library.kt index f01bff11e..6ecaba924 100644 --- a/native/android/lib/src/main/kotlin/wordpress/rs/Library.kt +++ b/native/android/lib/src/main/kotlin/wordpress/rs/Library.kt @@ -3,20 +3,5 @@ */ package wordpress.rs -import uniffi.wordpress_api.addCustom -import uniffi.wordpress_api.combineStrings -import uniffi.wordpress_api.panicFromRust - class Library { - fun addCustomFromRust(a: Int, b: Int): Int { - return addCustom(a, b) - } - - fun combineStringsFromRust(a: String, b: String): String { - return combineStrings(a, b) - } - - fun crashFromRust() { - return panicFromRust(); - } } diff --git a/native/android/lib/src/test/kotlin/wordpress/rs/LibraryTest.kt b/native/android/lib/src/test/kotlin/wordpress/rs/LibraryTest.kt index 2fe1dd1ac..c6172ef6d 100644 --- a/native/android/lib/src/test/kotlin/wordpress/rs/LibraryTest.kt +++ b/native/android/lib/src/test/kotlin/wordpress/rs/LibraryTest.kt @@ -3,22 +3,10 @@ */ package wordpress.rs -import org.junit.Assert.assertEquals import org.junit.Before -import org.junit.Test class LibraryTest { @Before fun setup() { } - - @Test - fun testAddCustom() { - assertEquals(Library().addCustomFromRust(2, 4), 6) - } - - @Test(expected = uniffi.wordpress_api.InternalException::class) - fun testCrashFromRust() { - Library().crashFromRust() - } } From e12c844eb1b2bc6615d1c54a0db409f3cb1789e6 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 13:44:32 -0500 Subject: [PATCH 02/18] Rename lib Android module as wp_api --- native/android/settings.gradle | 2 +- native/android/{lib => wp_api}/build.gradle | 0 .../src/androidTest/kotlin/wordpress/rs/LibraryTest.kt | 0 .../{lib => wp_api}/src/main/kotlin/wordpress/rs/Library.kt | 0 .../{lib => wp_api}/src/test/kotlin/wordpress/rs/LibraryTest.kt | 0 native/android/{lib => wp_api}/uniffi.gradle | 0 6 files changed, 1 insertion(+), 1 deletion(-) rename native/android/{lib => wp_api}/build.gradle (100%) rename native/android/{lib => wp_api}/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt (100%) rename native/android/{lib => wp_api}/src/main/kotlin/wordpress/rs/Library.kt (100%) rename native/android/{lib => wp_api}/src/test/kotlin/wordpress/rs/LibraryTest.kt (100%) rename native/android/{lib => wp_api}/uniffi.gradle (100%) diff --git a/native/android/settings.gradle b/native/android/settings.gradle index 074edc917..830105c30 100644 --- a/native/android/settings.gradle +++ b/native/android/settings.gradle @@ -22,4 +22,4 @@ plugins { } rootProject.name = "wordpress-rs" -include("lib") +include("wp_api") diff --git a/native/android/lib/build.gradle b/native/android/wp_api/build.gradle similarity index 100% rename from native/android/lib/build.gradle rename to native/android/wp_api/build.gradle diff --git a/native/android/lib/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt similarity index 100% rename from native/android/lib/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt rename to native/android/wp_api/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt diff --git a/native/android/lib/src/main/kotlin/wordpress/rs/Library.kt b/native/android/wp_api/src/main/kotlin/wordpress/rs/Library.kt similarity index 100% rename from native/android/lib/src/main/kotlin/wordpress/rs/Library.kt rename to native/android/wp_api/src/main/kotlin/wordpress/rs/Library.kt diff --git a/native/android/lib/src/test/kotlin/wordpress/rs/LibraryTest.kt b/native/android/wp_api/src/test/kotlin/wordpress/rs/LibraryTest.kt similarity index 100% rename from native/android/lib/src/test/kotlin/wordpress/rs/LibraryTest.kt rename to native/android/wp_api/src/test/kotlin/wordpress/rs/LibraryTest.kt diff --git a/native/android/lib/uniffi.gradle b/native/android/wp_api/uniffi.gradle similarity index 100% rename from native/android/lib/uniffi.gradle rename to native/android/wp_api/uniffi.gradle From 8f938a79d63ba69b84bb9ca8efd5fa81836cdc92 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 13:51:48 -0500 Subject: [PATCH 03/18] Rename wordpress.rs packages to rs.wp_api --- native/android/wp_api/build.gradle | 2 +- .../kotlin/{wordpress/rs => rs/wp_api}/LibraryTest.kt | 2 +- .../src/main/kotlin/{wordpress/rs => rs/wp_api}/Library.kt | 2 +- .../src/test/kotlin/{wordpress/rs => rs/wp_api}/LibraryTest.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename native/android/wp_api/src/androidTest/kotlin/{wordpress/rs => rs/wp_api}/LibraryTest.kt (88%) rename native/android/wp_api/src/main/kotlin/{wordpress/rs => rs/wp_api}/Library.kt (81%) rename native/android/wp_api/src/test/kotlin/{wordpress/rs => rs/wp_api}/LibraryTest.kt (88%) diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index 4a9d62d79..f569eead7 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -14,7 +14,7 @@ java { } android { - namespace = "org.wordpress.rs" + namespace = "rs.wp_api" compileSdk = 33 diff --git a/native/android/wp_api/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt similarity index 88% rename from native/android/wp_api/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt rename to native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt index c6172ef6d..0eb9f7232 100644 --- a/native/android/wp_api/src/androidTest/kotlin/wordpress/rs/LibraryTest.kt +++ b/native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt @@ -1,7 +1,7 @@ /* * This Kotlin source file was generated by the Gradle 'init' task. */ -package wordpress.rs +package rs.wp_api import org.junit.Before diff --git a/native/android/wp_api/src/main/kotlin/wordpress/rs/Library.kt b/native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt similarity index 81% rename from native/android/wp_api/src/main/kotlin/wordpress/rs/Library.kt rename to native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt index 6ecaba924..e90a20aca 100644 --- a/native/android/wp_api/src/main/kotlin/wordpress/rs/Library.kt +++ b/native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt @@ -1,7 +1,7 @@ /* * This Kotlin source file was generated by the Gradle 'init' task. */ -package wordpress.rs +package rs.wp_api class Library { } diff --git a/native/android/wp_api/src/test/kotlin/wordpress/rs/LibraryTest.kt b/native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt similarity index 88% rename from native/android/wp_api/src/test/kotlin/wordpress/rs/LibraryTest.kt rename to native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt index c6172ef6d..0eb9f7232 100644 --- a/native/android/wp_api/src/test/kotlin/wordpress/rs/LibraryTest.kt +++ b/native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt @@ -1,7 +1,7 @@ /* * This Kotlin source file was generated by the Gradle 'init' task. */ -package wordpress.rs +package rs.wp_api import org.junit.Before From 5b05a7212d500b289d70b982e343894fcf84be28 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 14:07:56 -0500 Subject: [PATCH 04/18] Use dylib to generate Kotlin bindings --- native/android/wp_api/build.gradle | 2 +- native/android/wp_api/uniffi.gradle | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index f569eead7..bbf2f6bc5 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -90,7 +90,7 @@ tasks.matching { it.name.matches("test") }.configureEach { it.dependsOn("cargoBuild") } -ext.configureUniFFIBindgen("../../../wp_api/src/wp_api.udl") +ext.configureUniFFIBindgen("../../../target/release/libwp_api.dylib") def getNativeRustTarget() { // This seems to return 'darwin' for both Apple Silicon and Intel architectures diff --git a/native/android/wp_api/uniffi.gradle b/native/android/wp_api/uniffi.gradle index 50903777c..c18f52fb4 100644 --- a/native/android/wp_api/uniffi.gradle +++ b/native/android/wp_api/uniffi.gradle @@ -1,18 +1,18 @@ // Adopted from https://github.com/mozilla/application-services/blob/v120.0.1/publish.gradle#L206-L226 // // A convenience function for configuring a `uniffi-bindgen` task, -// with appropriate dependency info. This fill call `uniffi-bindgen` -// on the provided `.udl` file in order to generate Kotlin language +// with appropriate dependency info. This will call `uniffi-bindgen` +// for the provided `.dylib` file in order to generate Kotlin language // bindings and include them in the source set for the project. -ext.configureUniFFIBindgen = { udlFilePath -> +ext.configureUniFFIBindgen = { dylibFilePath -> android.libraryVariants.all { variant -> def uniffiGeneratedPath = "generated/source/uniffi/${variant.name}/java" def t = tasks.register("generate${variant.name.capitalize()}UniFFIBindings", Exec) { workingDir project.rootDir - commandLine 'cargo', 'run', '--release', '--bin', 'uniffi_bindgen', 'generate', "${project.projectDir}/${udlFilePath}", '--out-dir', "${buildDir}/${uniffiGeneratedPath}", '--language', 'kotlin' + commandLine 'cargo', 'run', '--release', '--bin', 'uniffi_bindgen', 'generate', '--library', "${project.projectDir}/${dylibFilePath}", '--out-dir', "${buildDir}/${uniffiGeneratedPath}", '--language', 'kotlin' outputs.dir "${buildDir}/${uniffiGeneratedPath}" // Re-generate if the interface definition changes. - inputs.file "${project.projectDir}/${udlFilePath}" + inputs.file "${project.projectDir}/${dylibFilePath}" // Re-generate if our uniffi-bindgen tooling changes. inputs.dir "${project.rootDir}/../../uniffi_bindgen/" // Re-generate if our uniffi-bindgen version changes. From 2583c63ac1fefcabd2c570c9c9b54acef730d557 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 14:23:46 -0500 Subject: [PATCH 05/18] Update darwin rust target for Android project --- native/android/wp_api/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index bbf2f6bc5..0d645394b 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -97,6 +97,7 @@ def getNativeRustTarget() { // We don't use Intel macs anymore, so we should default to 'darwin-aarch64' switch (com.sun.jna.Platform.RESOURCE_PREFIX) { case 'darwin': + return 'darwin' case 'darwin-aarch64': return 'darwin-aarch64' case 'darwin-x86-64': From 859706bb9fdf5bd6f32affb4125c04e857680674 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 14:24:04 -0500 Subject: [PATCH 06/18] Add an Android unit test to verify NetworkResponseStatus can be created --- .../rs/wp_api/NetworkResponseStatusTest.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt diff --git a/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt b/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt new file mode 100644 index 000000000..60764bd57 --- /dev/null +++ b/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt @@ -0,0 +1,27 @@ +package rs.wp_api + +import org.junit.Test +import uniffi.wp_api.NetworkResponseStatus + +class FakeNetworkResponseStatus: NetworkResponseStatus { + override fun asU16(): UShort = 200u + + override fun isSuccess(): Boolean = true + override fun isInformational(): Boolean = false + override fun isRedirection(): Boolean = false + override fun isClientError(): Boolean = false + override fun isServerError(): Boolean = false +} + +class NetworkResponseStatusTest { + @Test + fun networkResponseStatusCanBeOverridden() { + val fakeStatus = FakeNetworkResponseStatus() + assert(fakeStatus.asU16() == 200.toUShort()) + assert(fakeStatus.isSuccess()) + assert(!fakeStatus.isInformational()) + assert(!fakeStatus.isRedirection()) + assert(!fakeStatus.isClientError()) + assert(!fakeStatus.isServerError()) + } +} From 2d08b322539be137ec9b6e8083c4152350988103 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 14:28:43 -0500 Subject: [PATCH 07/18] Update wp_api package name from rs.wp_api to rs.wordpress.wp_api --- native/android/wp_api/build.gradle | 6 +++--- .../wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt | 2 +- native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt | 2 +- .../android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt | 2 +- .../src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index 0d645394b..1a313e364 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -14,7 +14,7 @@ java { } android { - namespace = "rs.wp_api" + namespace = "rs.wordpress.wp_api" compileSdk = 33 @@ -73,8 +73,8 @@ project.afterEvaluate { MavenPublication(MavenPublication) { from components.release - groupId "org.wordpress" - artifactId "rs" + groupId "rs.wordpress" + artifactId "wp_api" // version is set by 'publish-to-s3' plugin } } diff --git a/native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt index 0eb9f7232..09942ceed 100644 --- a/native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt +++ b/native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt @@ -1,7 +1,7 @@ /* * This Kotlin source file was generated by the Gradle 'init' task. */ -package rs.wp_api +package rs.wordpress.wp_api import org.junit.Before diff --git a/native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt b/native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt index e90a20aca..e8eaef93e 100644 --- a/native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt +++ b/native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt @@ -1,7 +1,7 @@ /* * This Kotlin source file was generated by the Gradle 'init' task. */ -package rs.wp_api +package rs.wordpress.wp_api class Library { } diff --git a/native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt b/native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt index 0eb9f7232..09942ceed 100644 --- a/native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt +++ b/native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt @@ -1,7 +1,7 @@ /* * This Kotlin source file was generated by the Gradle 'init' task. */ -package rs.wp_api +package rs.wordpress.wp_api import org.junit.Before diff --git a/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt b/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt index 60764bd57..1af83e33f 100644 --- a/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt +++ b/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt @@ -1,4 +1,4 @@ -package rs.wp_api +package rs.wordpress.wp_api import org.junit.Test import uniffi.wp_api.NetworkResponseStatus From 41599b8519a7dee28391047aa71ccd9ca9e3e859 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 14:36:20 -0500 Subject: [PATCH 08/18] Refactor configureUniFFIBindgen Gradle task input paths --- native/android/wp_api/build.gradle | 2 +- native/android/wp_api/uniffi.gradle | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index 1a313e364..bd01d33b9 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -90,7 +90,7 @@ tasks.matching { it.name.matches("test") }.configureEach { it.dependsOn("cargoBuild") } -ext.configureUniFFIBindgen("../../../target/release/libwp_api.dylib") +ext.configureUniFFIBindgen("libwp_api.dylib") def getNativeRustTarget() { // This seems to return 'darwin' for both Apple Silicon and Intel architectures diff --git a/native/android/wp_api/uniffi.gradle b/native/android/wp_api/uniffi.gradle index c18f52fb4..8d5b6699d 100644 --- a/native/android/wp_api/uniffi.gradle +++ b/native/android/wp_api/uniffi.gradle @@ -4,15 +4,16 @@ // with appropriate dependency info. This will call `uniffi-bindgen` // for the provided `.dylib` file in order to generate Kotlin language // bindings and include them in the source set for the project. -ext.configureUniFFIBindgen = { dylibFilePath -> +ext.configureUniFFIBindgen = { dylibFileName -> android.libraryVariants.all { variant -> - def uniffiGeneratedPath = "generated/source/uniffi/${variant.name}/java" + def uniffiGeneratedPath = "$buildDir/generated/source/uniffi/${variant.name}/java" + def dylibFilePath = "${project.projectDir}/../../../target/release/${dylibFileName}" def t = tasks.register("generate${variant.name.capitalize()}UniFFIBindings", Exec) { workingDir project.rootDir - commandLine 'cargo', 'run', '--release', '--bin', 'uniffi_bindgen', 'generate', '--library', "${project.projectDir}/${dylibFilePath}", '--out-dir', "${buildDir}/${uniffiGeneratedPath}", '--language', 'kotlin' - outputs.dir "${buildDir}/${uniffiGeneratedPath}" + commandLine 'cargo', 'run', '--release', '--bin', 'uniffi_bindgen', 'generate', '--library', dylibFilePath, '--out-dir', uniffiGeneratedPath, '--language', 'kotlin' + outputs.dir uniffiGeneratedPath // Re-generate if the interface definition changes. - inputs.file "${project.projectDir}/${dylibFilePath}" + inputs.file dylibFilePath // Re-generate if our uniffi-bindgen tooling changes. inputs.dir "${project.rootDir}/../../uniffi_bindgen/" // Re-generate if our uniffi-bindgen version changes. From c727f2e8221018b54e5be7cc7d8c6f58076c15b4 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 17 Jan 2024 14:48:16 -0500 Subject: [PATCH 09/18] Move common Gradle configurations to common.gradle --- .../{wp_api/uniffi.gradle => common.gradle} | 46 ++++++++++++++++++ native/android/wp_api/build.gradle | 48 ++----------------- 2 files changed, 50 insertions(+), 44 deletions(-) rename native/android/{wp_api/uniffi.gradle => common.gradle} (51%) diff --git a/native/android/wp_api/uniffi.gradle b/native/android/common.gradle similarity index 51% rename from native/android/wp_api/uniffi.gradle rename to native/android/common.gradle index 8d5b6699d..1fa35c365 100644 --- a/native/android/wp_api/uniffi.gradle +++ b/native/android/common.gradle @@ -22,3 +22,49 @@ ext.configureUniFFIBindgen = { dylibFileName -> variant.registerJavaGeneratingTask(t.get(), new File(buildDir, uniffiGeneratedPath)) } } + +ext.addJnaDependencies = { + dependencies { + implementation "net.java.dev.jna:jna:5.13.0@aar" + androidTestImplementation "net.java.dev.jna:jna:5.13.0@aar" + testImplementation "net.java.dev.jna:jna:5.13.0" + } +} + +ext.setupCargo = { moduleName, nativeRustTarget -> + cargo { + module = "../../../$moduleName/" + libname = moduleName + profile = "release" + targets = ["arm", "arm64", "x86", "x86_64", nativeRustTarget] + targetDirectory = '../../../target' + + exec { spec, toolchain -> + // https://doc.rust-lang.org/rustc/command-line-arguments.html#-g-include-debug-information + spec.environment("RUSTFLAGS", "-g") + } + } + tasks.matching { it.name.matches(/merge.*JniLibFolders/) }.configureEach { + it.inputs.dir(new File(buildDir, "rustJniLibs/android")) + it.dependsOn("cargoBuild") + } + + tasks.matching { it.name.matches("test") }.configureEach { + it.dependsOn("cargoBuild") + } +} + +ext.getNativeRustTarget = { resourcePrefix -> + switch (resourcePrefix) { + case 'darwin': + return 'darwin' + case 'darwin-aarch64': + return 'darwin-aarch64' + case 'darwin-x86-64': + return 'darwin-x86-64' + case 'linux-x86-64': + return 'linux-x86-64' + case 'win32-x86-64': + return 'win32-x86-64-gnu' + } +} diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index bd01d33b9..792473c6a 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -5,7 +5,7 @@ plugins { id("com.automattic.android.publish-to-s3") } -apply from: 'uniffi.gradle' +apply from: '../common.gradle' java { toolchain { @@ -43,29 +43,16 @@ repositories { } dependencies { - implementation "net.java.dev.jna:jna:5.13.0@aar" - androidTestImplementation "net.java.dev.jna:jna:5.13.0@aar" - androidTestImplementation "androidx.test:runner:1.4.0" androidTestImplementation "androidx.test:rules:1.4.0" androidTestImplementation "junit:junit:4.13.2" testImplementation "junit:junit:4.13.2" - testImplementation "net.java.dev.jna:jna:5.13.0" } -cargo { - module = "../../../wp_api/" - libname = "wp_api" - profile = "release" - targets = ["arm", "arm64", "x86", "x86_64", getNativeRustTarget()] - targetDirectory = '../../../target' - - exec { spec, toolchain -> - // https://doc.rust-lang.org/rustc/command-line-arguments.html#-g-include-debug-information - spec.environment("RUSTFLAGS", "-g") - } -} +ext.setupCargo("wp_api", ext.getNativeRustTarget(com.sun.jna.Platform.RESOURCE_PREFIX)) +ext.configureUniFFIBindgen("libwp_api.dylib") +ext.addJnaDependencies() project.afterEvaluate { publishing { @@ -81,30 +68,3 @@ project.afterEvaluate { } } -tasks.matching { it.name.matches(/merge.*JniLibFolders/) }.configureEach { - it.inputs.dir(new File(buildDir, "rustJniLibs/android")) - it.dependsOn("cargoBuild") -} - -tasks.matching { it.name.matches("test") }.configureEach { - it.dependsOn("cargoBuild") -} - -ext.configureUniFFIBindgen("libwp_api.dylib") - -def getNativeRustTarget() { - // This seems to return 'darwin' for both Apple Silicon and Intel architectures - // We don't use Intel macs anymore, so we should default to 'darwin-aarch64' - switch (com.sun.jna.Platform.RESOURCE_PREFIX) { - case 'darwin': - return 'darwin' - case 'darwin-aarch64': - return 'darwin-aarch64' - case 'darwin-x86-64': - return 'darwin-x86-64' - case 'linux-x86-64': - return 'linux-x86-64' - case 'win32-x86-64': - return 'win32-x86-64-gnu' - } -} From 28ef57f4578693a270afa530d0aeddee03ab5bf4 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Sat, 20 Jan 2024 21:58:04 -0500 Subject: [PATCH 10/18] Add wp_networking Gradle module --- native/android/settings.gradle | 2 +- native/android/wp_networking/build.gradle | 70 +++++++++++++++++++++++ 2 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 native/android/wp_networking/build.gradle diff --git a/native/android/settings.gradle b/native/android/settings.gradle index 830105c30..c1a38c65c 100644 --- a/native/android/settings.gradle +++ b/native/android/settings.gradle @@ -22,4 +22,4 @@ plugins { } rootProject.name = "wordpress-rs" -include("wp_api") +include("wp_api", "wp_networking") diff --git a/native/android/wp_networking/build.gradle b/native/android/wp_networking/build.gradle new file mode 100644 index 000000000..d72dab4d0 --- /dev/null +++ b/native/android/wp_networking/build.gradle @@ -0,0 +1,70 @@ +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") + id("org.mozilla.rust-android-gradle.rust-android") + id("com.automattic.android.publish-to-s3") +} + +apply from: '../common.gradle' + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + +android { + namespace = "rs.wordpress.wp_networking" + + compileSdk = 33 + + defaultConfig { + minSdk = 24 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + + ndk { + debugSymbolLevel 'FULL' + } + } + + sourceSets { + androidTest.jniLibs.srcDirs += "$buildDir/rustJniLibs/android" + } + + packagingOptions { + doNotStrip "**/*.so" + } +} + +repositories { + mavenCentral() + google() +} + +dependencies { + androidTestImplementation "androidx.test:runner:1.4.0" + androidTestImplementation "androidx.test:rules:1.4.0" + androidTestImplementation "junit:junit:4.13.2" + + testImplementation "junit:junit:4.13.2" +} + +ext.setupCargo("wp_networking", ext.getNativeRustTarget(com.sun.jna.Platform.RESOURCE_PREFIX)) +ext.configureUniFFIBindgen("libwp_networking.dylib") +ext.addJnaDependencies() + +project.afterEvaluate { + publishing { + publications { + MavenPublication(MavenPublication) { + from components.release + + groupId "rs.wordpress" + artifactId "wp_networking" + // version is set by 'publish-to-s3' plugin + } + } + } +} + From 60283709229edb2d6ac21d2d3bdda26bed56010e Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Sat, 20 Jan 2024 22:14:28 -0500 Subject: [PATCH 11/18] Remove reqwest as default networking implementation from wp_networking --- Cargo.lock | 2 +- wp_cli/Cargo.toml | 1 + wp_cli/src/main.rs | 53 ++++++++++++++++++++++++++--- wp_networking/Cargo.toml | 1 - wp_networking/src/lib.rs | 46 ------------------------- wp_networking/src/wp_networking.udl | 1 - 6 files changed, 50 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 301940508..0d63a2d95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1891,6 +1891,7 @@ dependencies = [ name = "wp_cli" version = "0.1.0" dependencies = [ + "reqwest", "wp_api", "wp_networking", ] @@ -1900,7 +1901,6 @@ name = "wp_networking" version = "0.1.0" dependencies = [ "http", - "reqwest", "serde_json", "uniffi", "wp_api", diff --git a/wp_cli/Cargo.toml b/wp_cli/Cargo.toml index 786ddedf6..9ae541d6a 100644 --- a/wp_cli/Cargo.toml +++ b/wp_cli/Cargo.toml @@ -5,5 +5,6 @@ edition = "2021" default-run = "wp_cli" [dependencies] +reqwest = { version = "0.11.22", features = ["blocking"] } wp_api = { path = "../wp_api" } wp_networking = { path = "../wp_networking" } diff --git a/wp_cli/src/main.rs b/wp_cli/src/main.rs index e5f9c0ab5..347a209b9 100644 --- a/wp_cli/src/main.rs +++ b/wp_cli/src/main.rs @@ -1,6 +1,7 @@ -use std::fs::read_to_string; +use std::{fs::read_to_string, sync::Arc}; -use wp_api::WPAuthentication; +use reqwest::{blocking::Client, header::HeaderMap}; +use wp_api::{WPAuthentication, WPNetworkRequest, WPNetworkResponse, WPNetworkingInterface}; fn main() { // A very naive approach just to get things working for now - this whole code will be deleted @@ -14,9 +15,13 @@ fn main() { auth_token: auth_base64_token.into(), }; - let post_list = wp_networking::wp_api(url.into(), authentication.clone()) - .list_posts(None) - .unwrap(); + let post_list = wp_networking::wp_api_with_custom_networking( + url.into(), + authentication.clone(), + Arc::new(WPNetworking::default()), + ) + .list_posts(None) + .unwrap(); println!("{:?}", post_list); // let post_list_with_custom_networking = wp_networking::wp_api_with_custom_networking( @@ -37,3 +42,41 @@ fn main() { // todo!() // } // } +// +// +struct WPNetworking { + client: Client, +} + +impl Default for WPNetworking { + fn default() -> Self { + Self { + client: Client::new(), + } + } +} + +impl WPNetworkingInterface for WPNetworking { + fn request(&self, request: WPNetworkRequest) -> wp_api::WPNetworkResponse { + let method = match request.method { + wp_api::RequestMethod::GET => reqwest::Method::GET, + wp_api::RequestMethod::POST => reqwest::Method::POST, + wp_api::RequestMethod::PUT => reqwest::Method::PUT, + wp_api::RequestMethod::DELETE => reqwest::Method::DELETE, + }; + + let request_headers: HeaderMap = (&request.header_map.unwrap()).try_into().unwrap(); + + // TODO: Error handling + let response = self + .client + .request(method, request.url) + .headers(request_headers) + .send() + .unwrap(); + WPNetworkResponse { + status: Arc::new(response.status()), + body: response.text().unwrap().as_bytes().to_vec(), + } + } +} diff --git a/wp_networking/Cargo.toml b/wp_networking/Cargo.toml index 8beb4aaf7..3a6234069 100644 --- a/wp_networking/Cargo.toml +++ b/wp_networking/Cargo.toml @@ -9,7 +9,6 @@ name = "wp_networking" [dependencies] http = { workspace = true } -reqwest = { version = "0.11.22", features = ["blocking", "json"] } serde_json = "1.0" uniffi = { workspace = true } wp_api = { path = "../wp_api" } diff --git a/wp_networking/src/lib.rs b/wp_networking/src/lib.rs index 45e5c58e2..6cb9d76d2 100644 --- a/wp_networking/src/lib.rs +++ b/wp_networking/src/lib.rs @@ -1,7 +1,6 @@ #![allow(dead_code, unused_variables)] use std::{collections::HashMap, sync::Arc}; -use reqwest::{blocking::Client, header::HeaderMap}; use wp_api::{ ClientErrorType, PageListParams, PageListResponse, PostCreateParams, PostCreateResponse, PostDeleteParams, PostDeleteResponse, PostListParams, PostListResponse, PostObject, @@ -21,14 +20,6 @@ pub fn panic_from_rust() { std::fs::read_to_string("doesnt_exist.txt").unwrap(); } -pub fn wp_api(site_url: String, authentication: WPAuthentication) -> Arc { - Arc::new(WPApi { - site_url, - authentication, - networking_interface: Arc::new(WPNetworking::default()), - }) -} - pub fn wp_api_with_custom_networking( site_url: String, authentication: WPAuthentication, @@ -41,43 +32,6 @@ pub fn wp_api_with_custom_networking( }) } -struct WPNetworking { - client: Client, -} - -impl Default for WPNetworking { - fn default() -> Self { - Self { - client: Client::new(), - } - } -} - -impl WPNetworkingInterface for WPNetworking { - fn request(&self, request: WPNetworkRequest) -> wp_api::WPNetworkResponse { - let method = match request.method { - wp_api::RequestMethod::GET => reqwest::Method::GET, - wp_api::RequestMethod::POST => reqwest::Method::POST, - wp_api::RequestMethod::PUT => reqwest::Method::PUT, - wp_api::RequestMethod::DELETE => reqwest::Method::DELETE, - }; - - let request_headers: HeaderMap = (&request.header_map.unwrap()).try_into().unwrap(); - - // TODO: Error handling - let response = self - .client - .request(method, request.url) - .headers(request_headers) - .send() - .unwrap(); - WPNetworkResponse { - status: Arc::new(response.status()), - body: response.text().unwrap().as_bytes().to_vec(), - } - } -} - struct WPApi { site_url: String, authentication: WPAuthentication, diff --git a/wp_networking/src/wp_networking.udl b/wp_networking/src/wp_networking.udl index 673b52e88..2a2ad4a21 100644 --- a/wp_networking/src/wp_networking.udl +++ b/wp_networking/src/wp_networking.udl @@ -16,6 +16,5 @@ namespace wp_networking { // TODO: We should have a type for the `site_url`, but it's not yet clear what that type should // look like or what it should contain, so we are using a placeholder string type for now. - WPApiInterface wp_api(string site_url, WPAuthentication authentication); WPApiInterface wp_api_with_custom_networking(string site_url, WPAuthentication authentication, WPNetworkingInterface networking_interface); }; From 11ce199617a60bb3e77948b34ed55b4221adf068 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Sat, 20 Jan 2024 22:47:30 -0500 Subject: [PATCH 12/18] Mockup of Android implementation of WPNetworkingInterface --- native/android/common.gradle | 2 ++ .../rs/{ => wordpress}/wp_api/LibraryTest.kt | 0 .../rs/{ => wordpress}/wp_api/Library.kt | 0 .../rs/{ => wordpress}/wp_api/LibraryTest.kt | 0 .../wp_api/NetworkResponseStatusTest.kt | 0 native/android/wp_networking/build.gradle | 2 ++ .../wp_networking/WPNetworkingTest.kt | 10 ++++++++++ .../wordpress/wp_networking/WPNetworking.kt | 20 +++++++++++++++++++ 8 files changed, 34 insertions(+) rename native/android/wp_api/src/androidTest/kotlin/rs/{ => wordpress}/wp_api/LibraryTest.kt (100%) rename native/android/wp_api/src/main/kotlin/rs/{ => wordpress}/wp_api/Library.kt (100%) rename native/android/wp_api/src/test/kotlin/rs/{ => wordpress}/wp_api/LibraryTest.kt (100%) rename native/android/wp_api/src/test/kotlin/rs/{ => wordpress}/wp_api/NetworkResponseStatusTest.kt (100%) create mode 100644 native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt create mode 100644 native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt diff --git a/native/android/common.gradle b/native/android/common.gradle index 1fa35c365..4eee37920 100644 --- a/native/android/common.gradle +++ b/native/android/common.gradle @@ -20,6 +20,8 @@ ext.configureUniFFIBindgen = { dylibFileName -> inputs.file "${project.rootDir}/../../Cargo.lock" } variant.registerJavaGeneratingTask(t.get(), new File(buildDir, uniffiGeneratedPath)) + def sourceSet = variant.sourceSets.find { it.name == variant.name } + sourceSet.java.srcDir new File(buildDir, "generated/source/uniffi/${variant.name}/java") } } diff --git a/native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt similarity index 100% rename from native/android/wp_api/src/androidTest/kotlin/rs/wp_api/LibraryTest.kt rename to native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt diff --git a/native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt b/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt similarity index 100% rename from native/android/wp_api/src/main/kotlin/rs/wp_api/Library.kt rename to native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt diff --git a/native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt b/native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/LibraryTest.kt similarity index 100% rename from native/android/wp_api/src/test/kotlin/rs/wp_api/LibraryTest.kt rename to native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/LibraryTest.kt diff --git a/native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt b/native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/NetworkResponseStatusTest.kt similarity index 100% rename from native/android/wp_api/src/test/kotlin/rs/wp_api/NetworkResponseStatusTest.kt rename to native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/NetworkResponseStatusTest.kt diff --git a/native/android/wp_networking/build.gradle b/native/android/wp_networking/build.gradle index d72dab4d0..a08bc6b76 100644 --- a/native/android/wp_networking/build.gradle +++ b/native/android/wp_networking/build.gradle @@ -43,6 +43,8 @@ repositories { } dependencies { + implementation project(':wp_api') + androidTestImplementation "androidx.test:runner:1.4.0" androidTestImplementation "androidx.test:rules:1.4.0" androidTestImplementation "junit:junit:4.13.2" diff --git a/native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt b/native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt new file mode 100644 index 000000000..30aef5c83 --- /dev/null +++ b/native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt @@ -0,0 +1,10 @@ +package rs.wordpress.wp_networking + +import org.junit.Test + +class WPNetworkingTest { + @Test + fun testSomething() { + wpApi().listPosts(null) + } +} diff --git a/native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt b/native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt new file mode 100644 index 000000000..49815b86f --- /dev/null +++ b/native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt @@ -0,0 +1,20 @@ +package rs.wordpress.wp_networking + +import uniffi.wp_api.WpApiInterface +import uniffi.wp_api.WpAuthentication +import uniffi.wp_api.WpNetworkRequest +import uniffi.wp_api.WpNetworkResponse +import uniffi.wp_api.WpNetworkingInterface +import uniffi.wp_networking.wpApiWithCustomNetworking + +fun wpApi(): WpApiInterface { + val authentication = WpAuthentication(authToken = "ZGVtbzo0alp6IGNid2UgOXdkVCBFcE1kIGpQVDQgTkZCRg==") + val siteUrl = "https://clever-coffee-nut.jurassic.ninja" + return wpApiWithCustomNetworking(siteUrl, authentication, WPNetworking()) +} + +class WPNetworking: WpNetworkingInterface { + override fun request(request: WpNetworkRequest): WpNetworkResponse { + TODO("Not yet implemented") + } +} From 0cb33257e1a18c2ea26eeb03ac17fb6ddae7a3f5 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Tue, 23 Jan 2024 13:58:47 -0500 Subject: [PATCH 13/18] A major pivot: Turning Rust wp_api crate to be a helper library This is a major change from the current design, as such this commit only covers the necessary changes to get the projects building. It doesn't include changes that should be made due to the design change. --- Cargo.lock | 264 ++---------------- Cargo.toml | 2 +- native/android/common.gradle | 8 +- native/android/settings.gradle | 2 +- native/android/wp_api/build.gradle | 2 +- .../wp_api/NetworkResponseStatusTest.kt | 27 -- native/android/wp_networking/build.gradle | 72 ----- .../wp_networking/WPNetworkingTest.kt | 10 - .../wordpress/wp_networking/WPNetworking.kt | 20 -- wp_api/src/lib.rs | 80 ------ wp_api/src/wp_api.udl | 52 ---- wp_cli/Cargo.toml | 1 - wp_cli/src/main.rs | 40 +-- wp_networking/Cargo.toml | 1 + wp_networking/build.rs | 3 - wp_networking/src/lib.rs | 151 +++++++--- wp_networking/src/wp_networking.udl | 20 -- 17 files changed, 157 insertions(+), 598 deletions(-) delete mode 100644 native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/NetworkResponseStatusTest.kt delete mode 100644 native/android/wp_networking/build.gradle delete mode 100644 native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt delete mode 100644 native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt delete mode 100644 wp_networking/build.rs delete mode 100644 wp_networking/src/wp_networking.udl diff --git a/Cargo.lock b/Cargo.lock index 0d63a2d95..8b312ac2d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,15 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - [[package]] name = "anstream" version = "0.6.4" @@ -417,19 +408,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows", -] - [[package]] name = "gimli" version = "0.28.1" @@ -444,9 +422,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "goblin" -version = "0.6.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d6b4de4a8eb6c46a8c77e1d3be942cb9a8bf073c22374578e5ba4b08ed0ff68" +checksum = "bb07a4ffed2093b118a525b1d8f5204ae274faed5604537caf7135d0f18d9887" dependencies = [ "log", "plain", @@ -626,29 +604,6 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "pin-utils", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "memchr" version = "2.6.4" @@ -725,16 +680,6 @@ dependencies = [ "minimal-lexical", ] -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", -] - [[package]] name = "num_cpus" version = "1.16.0" @@ -761,13 +706,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] -name = "oneshot" +name = "oneshot-uniffi" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4" -dependencies = [ - "loom", -] +checksum = "6c548d5c78976f6955d72d0ced18c48ca07030f7a1d4024529fedd7c1c01b29c" [[package]] name = "openssl" @@ -813,12 +755,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "paste" version = "1.0.14" @@ -882,50 +818,6 @@ dependencies = [ "bitflags 1.3.2", ] -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.3", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.8.2", -] - -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - [[package]] name = "reqwest" version = "0.11.22" @@ -983,12 +875,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - [[package]] name = "ryu" version = "1.0.15" @@ -1004,26 +890,20 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scroll" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" +checksum = "6ab8598aa408498679922eff7fa985c25d58a90771bd6be794434c5277eab1a6" dependencies = [ "scroll_derive", ] [[package]] name = "scroll_derive" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1db149f81d46d2deba7cd3c50772474707729550221e69588478ebf9ada425ae" +checksum = "7f81c2fde025af7e69b1d1420531c8a8811ca898919db177141a85313b1cb932" dependencies = [ "proc-macro2", "quote", @@ -1105,15 +985,6 @@ dependencies = [ "serde", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "siphasher" version = "0.3.11" @@ -1129,12 +1000,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "smallvec" -version = "1.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" - [[package]] name = "smawk" version = "0.3.2" @@ -1249,16 +1114,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thread_local" -version = "1.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] - [[package]] name = "tinyvec" version = "1.6.0" @@ -1336,21 +1191,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing-core" version = "0.1.32" @@ -1358,36 +1201,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -1440,13 +1253,13 @@ checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "uniffi" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "anyhow", "camino", "clap", - "uniffi_bindgen 0.25.2", + "uniffi_bindgen 0.25.3", "uniffi_build", "uniffi_core", "uniffi_macros", @@ -1461,8 +1274,8 @@ dependencies = [ [[package]] name = "uniffi_bindgen" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "anyhow", "askama", @@ -1485,18 +1298,18 @@ dependencies = [ [[package]] name = "uniffi_build" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "anyhow", "camino", - "uniffi_bindgen 0.25.2", + "uniffi_bindgen 0.25.3", ] [[package]] name = "uniffi_checksum_derive" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "quote", "syn", @@ -1504,23 +1317,23 @@ dependencies = [ [[package]] name = "uniffi_core" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "anyhow", "bytes", "camino", "log", "once_cell", - "oneshot", + "oneshot-uniffi", "paste", "static_assertions", ] [[package]] name = "uniffi_macros" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "bincode", "camino", @@ -1537,8 +1350,8 @@ dependencies = [ [[package]] name = "uniffi_meta" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "anyhow", "bytes", @@ -1548,8 +1361,8 @@ dependencies = [ [[package]] name = "uniffi_testing" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "anyhow", "camino", @@ -1560,8 +1373,8 @@ dependencies = [ [[package]] name = "uniffi_udl" -version = "0.25.2" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +version = "0.25.3" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "anyhow", "textwrap", @@ -1587,12 +1400,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - [[package]] name = "vcpkg" version = "0.2.15" @@ -1699,7 +1506,7 @@ dependencies = [ [[package]] name = "weedle2" version = "4.0.0" -source = "git+https://github.com/mozilla/uniffi-rs.git?rev=d380d164cfdba9e091c461baa6855f0a2294ac5b#d380d164cfdba9e091c461baa6855f0a2294ac5b" +source = "git+https://github.com/mozilla/uniffi-rs.git?rev=22a9192162744b8a46997dcdcf628a7dba769570#22a9192162744b8a46997dcdcf628a7dba769570" dependencies = [ "nom", ] @@ -1726,15 +1533,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -1891,7 +1689,6 @@ dependencies = [ name = "wp_cli" version = "0.1.0" dependencies = [ - "reqwest", "wp_api", "wp_networking", ] @@ -1901,6 +1698,7 @@ name = "wp_networking" version = "0.1.0" dependencies = [ "http", + "reqwest", "serde_json", "uniffi", "wp_api", diff --git a/Cargo.toml b/Cargo.toml index eb0b7b959..720336272 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ resolver = "2" [workspace.dependencies] # External traits in UDL is only available since https://github.com/mozilla/uniffi-rs/pull/1867 # Point to a specific commit until a new version is released -uniffi = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "d380d164cfdba9e091c461baa6855f0a2294ac5b" } +uniffi = { git = "https://github.com/mozilla/uniffi-rs.git", rev = "22a9192162744b8a46997dcdcf628a7dba769570" } # We need to match the version used in `reqwest`: https://github.com/seanmonstar/reqwest/blob/master/Cargo.toml#L84 # So that we can implement our own traits and it'll work with both reqwest types as well as http types # diff --git a/native/android/common.gradle b/native/android/common.gradle index 4eee37920..58364e218 100644 --- a/native/android/common.gradle +++ b/native/android/common.gradle @@ -2,12 +2,12 @@ // // A convenience function for configuring a `uniffi-bindgen` task, // with appropriate dependency info. This will call `uniffi-bindgen` -// for the provided `.dylib` file in order to generate Kotlin language +// for the provided `moduleName`'s `.dylib` file in order to generate Kotlin language // bindings and include them in the source set for the project. -ext.configureUniFFIBindgen = { dylibFileName -> +ext.configureUniFFIBindgen = { moduleName -> android.libraryVariants.all { variant -> def uniffiGeneratedPath = "$buildDir/generated/source/uniffi/${variant.name}/java" - def dylibFilePath = "${project.projectDir}/../../../target/release/${dylibFileName}" + def dylibFilePath = "${project.projectDir}/../../../target/release/lib${moduleName}.dylib" def t = tasks.register("generate${variant.name.capitalize()}UniFFIBindings", Exec) { workingDir project.rootDir commandLine 'cargo', 'run', '--release', '--bin', 'uniffi_bindgen', 'generate', '--library', dylibFilePath, '--out-dir', uniffiGeneratedPath, '--language', 'kotlin' @@ -21,7 +21,7 @@ ext.configureUniFFIBindgen = { dylibFileName -> } variant.registerJavaGeneratingTask(t.get(), new File(buildDir, uniffiGeneratedPath)) def sourceSet = variant.sourceSets.find { it.name == variant.name } - sourceSet.java.srcDir new File(buildDir, "generated/source/uniffi/${variant.name}/java") + sourceSet.java.srcDir new File(buildDir, "generated/source/uniffi/${variant.name}/java/uniffi") } } diff --git a/native/android/settings.gradle b/native/android/settings.gradle index c1a38c65c..830105c30 100644 --- a/native/android/settings.gradle +++ b/native/android/settings.gradle @@ -22,4 +22,4 @@ plugins { } rootProject.name = "wordpress-rs" -include("wp_api", "wp_networking") +include("wp_api") diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index 792473c6a..753a77487 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -51,7 +51,7 @@ dependencies { } ext.setupCargo("wp_api", ext.getNativeRustTarget(com.sun.jna.Platform.RESOURCE_PREFIX)) -ext.configureUniFFIBindgen("libwp_api.dylib") +ext.configureUniFFIBindgen("wp_api") ext.addJnaDependencies() project.afterEvaluate { diff --git a/native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/NetworkResponseStatusTest.kt b/native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/NetworkResponseStatusTest.kt deleted file mode 100644 index 1af83e33f..000000000 --- a/native/android/wp_api/src/test/kotlin/rs/wordpress/wp_api/NetworkResponseStatusTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package rs.wordpress.wp_api - -import org.junit.Test -import uniffi.wp_api.NetworkResponseStatus - -class FakeNetworkResponseStatus: NetworkResponseStatus { - override fun asU16(): UShort = 200u - - override fun isSuccess(): Boolean = true - override fun isInformational(): Boolean = false - override fun isRedirection(): Boolean = false - override fun isClientError(): Boolean = false - override fun isServerError(): Boolean = false -} - -class NetworkResponseStatusTest { - @Test - fun networkResponseStatusCanBeOverridden() { - val fakeStatus = FakeNetworkResponseStatus() - assert(fakeStatus.asU16() == 200.toUShort()) - assert(fakeStatus.isSuccess()) - assert(!fakeStatus.isInformational()) - assert(!fakeStatus.isRedirection()) - assert(!fakeStatus.isClientError()) - assert(!fakeStatus.isServerError()) - } -} diff --git a/native/android/wp_networking/build.gradle b/native/android/wp_networking/build.gradle deleted file mode 100644 index a08bc6b76..000000000 --- a/native/android/wp_networking/build.gradle +++ /dev/null @@ -1,72 +0,0 @@ -plugins { - id("com.android.library") - id("org.jetbrains.kotlin.android") - id("org.mozilla.rust-android-gradle.rust-android") - id("com.automattic.android.publish-to-s3") -} - -apply from: '../common.gradle' - -java { - toolchain { - languageVersion.set(JavaLanguageVersion.of(17)) - } -} - -android { - namespace = "rs.wordpress.wp_networking" - - compileSdk = 33 - - defaultConfig { - minSdk = 24 - - testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - - ndk { - debugSymbolLevel 'FULL' - } - } - - sourceSets { - androidTest.jniLibs.srcDirs += "$buildDir/rustJniLibs/android" - } - - packagingOptions { - doNotStrip "**/*.so" - } -} - -repositories { - mavenCentral() - google() -} - -dependencies { - implementation project(':wp_api') - - androidTestImplementation "androidx.test:runner:1.4.0" - androidTestImplementation "androidx.test:rules:1.4.0" - androidTestImplementation "junit:junit:4.13.2" - - testImplementation "junit:junit:4.13.2" -} - -ext.setupCargo("wp_networking", ext.getNativeRustTarget(com.sun.jna.Platform.RESOURCE_PREFIX)) -ext.configureUniFFIBindgen("libwp_networking.dylib") -ext.addJnaDependencies() - -project.afterEvaluate { - publishing { - publications { - MavenPublication(MavenPublication) { - from components.release - - groupId "rs.wordpress" - artifactId "wp_networking" - // version is set by 'publish-to-s3' plugin - } - } - } -} - diff --git a/native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt b/native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt deleted file mode 100644 index 30aef5c83..000000000 --- a/native/android/wp_networking/src/androidTest/kotlin/rs/wordpress/wp_networking/WPNetworkingTest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package rs.wordpress.wp_networking - -import org.junit.Test - -class WPNetworkingTest { - @Test - fun testSomething() { - wpApi().listPosts(null) - } -} diff --git a/native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt b/native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt deleted file mode 100644 index 49815b86f..000000000 --- a/native/android/wp_networking/src/main/kotlin/rs/wordpress/wp_networking/WPNetworking.kt +++ /dev/null @@ -1,20 +0,0 @@ -package rs.wordpress.wp_networking - -import uniffi.wp_api.WpApiInterface -import uniffi.wp_api.WpAuthentication -import uniffi.wp_api.WpNetworkRequest -import uniffi.wp_api.WpNetworkResponse -import uniffi.wp_api.WpNetworkingInterface -import uniffi.wp_networking.wpApiWithCustomNetworking - -fun wpApi(): WpApiInterface { - val authentication = WpAuthentication(authToken = "ZGVtbzo0alp6IGNid2UgOXdkVCBFcE1kIGpQVDQgTkZCRg==") - val siteUrl = "https://clever-coffee-nut.jurassic.ninja" - return wpApiWithCustomNetworking(siteUrl, authentication, WPNetworking()) -} - -class WPNetworking: WpNetworkingInterface { - override fun request(request: WpNetworkRequest): WpNetworkResponse { - TODO("Not yet implemented") - } -} diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 7810eb738..5cf489d96 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -1,7 +1,5 @@ #![allow(dead_code, unused_variables)] -use std::{collections::HashMap, sync::Arc}; - pub use api_error::*; pub use pages::*; pub use posts::*; @@ -10,88 +8,10 @@ pub mod api_error; pub mod pages; pub mod posts; -pub trait WPNetworkingInterface: Send + Sync { - fn request(&self, request: WPNetworkRequest) -> WPNetworkResponse; -} - -pub enum RequestMethod { - GET, - POST, - PUT, - DELETE, -} - -pub trait NetworkResponseStatus: Send + Sync { - fn as_u16(&self) -> u16; - fn is_informational(&self) -> bool; - fn is_success(&self) -> bool; - fn is_redirection(&self) -> bool; - fn is_client_error(&self) -> bool; - fn is_server_error(&self) -> bool; -} - -impl NetworkResponseStatus for http::StatusCode { - fn as_u16(&self) -> u16 { - self.as_u16() - } - - fn is_informational(&self) -> bool { - self.is_informational() - } - - fn is_success(&self) -> bool { - self.is_success() - } - - fn is_redirection(&self) -> bool { - self.is_redirection() - } - - fn is_client_error(&self) -> bool { - self.is_client_error() - } - - fn is_server_error(&self) -> bool { - self.is_informational() - } -} - -pub struct WPNetworkRequest { - pub method: RequestMethod, - pub url: String, - // TODO: We probably want to implement a specific type for these headers instead of using a - // regular HashMap. - // - // It could be something similar to `reqwest`'s [`header`](https://docs.rs/reqwest/latest/reqwest/header/index.html) - // module. - pub header_map: Option>, -} - -pub struct WPNetworkResponse { - pub status: Arc, - pub body: Vec, -} - #[derive(Debug, Clone)] // TODO: This will probably become an `enum` where we support multiple authentication types. pub struct WPAuthentication { pub auth_token: String, } -pub trait WPApiInterface: Send + Sync { - fn list_posts(&self, params: Option) -> Result; - fn create_post(&self, params: Option) -> PostCreateResponse; - fn retrieve_post( - &self, - post_id: u32, - params: Option, - ) -> PostRetrieveResponse; - - fn update_post(&self, post_id: u32, params: Option) -> PostUpdateResponse; - - fn delete_post(&self, post_id: u32, params: Option) -> PostDeleteResponse; - - fn list_pages(&self, params: Option) -> PageListResponse; -} - uniffi::include_scaffolding!("wp_api"); diff --git a/wp_api/src/wp_api.udl b/wp_api/src/wp_api.udl index e3eb5bb0a..fb4b6be9d 100644 --- a/wp_api/src/wp_api.udl +++ b/wp_api/src/wp_api.udl @@ -1,7 +1,6 @@ namespace wp_api { }; - enum ClientErrorType { "BadRequest", "Unauthorized", @@ -17,61 +16,10 @@ interface WPApiError { UnknownError(); }; -[Trait] -interface WPNetworkingInterface { - WPNetworkResponse request(WPNetworkRequest request); -}; - dictionary WPAuthentication { string auth_token; }; -// https://developer.wordpress.org/rest-api/reference/posts/ -// TODO: The schema and some of the action arguments for `/posts` endpoint has the notion of `context`. -// This is an `enum` value, but it can only contain partial values per field. -// This is an important design element to get right, because it's a common pattern for the API. -// -// IMPORTANT: This design does not include error handling yet! -[Trait] -interface WPApiInterface { - [Throws=WPApiError] - PostListResponse list_posts(PostListParams? params); - PostCreateResponse create_post(PostCreateParams? params); - PostRetrieveResponse retrieve_post(u32 post_id, PostRetrieveParams? params); - PostUpdateResponse update_post(u32 post_id, PostUpdateParams? params); - PostDeleteResponse delete_post(u32 post_id, PostDeleteParams? params); - - PageListResponse list_pages(PageListParams? params); -}; - -enum RequestMethod { - "GET", - "POST", - "PUT", - "DELETE" -}; - -dictionary WPNetworkRequest { - RequestMethod method; - string url; - record? header_map; -}; - -[Trait] -interface NetworkResponseStatus { - u16 as_u16(); - boolean is_informational(); - boolean is_success(); - boolean is_redirection(); - boolean is_client_error(); - boolean is_server_error(); -}; - -dictionary WPNetworkResponse { - bytes body; - NetworkResponseStatus status; -}; - dictionary PostListResponse { sequence? post_list; }; diff --git a/wp_cli/Cargo.toml b/wp_cli/Cargo.toml index 9ae541d6a..786ddedf6 100644 --- a/wp_cli/Cargo.toml +++ b/wp_cli/Cargo.toml @@ -5,6 +5,5 @@ edition = "2021" default-run = "wp_cli" [dependencies] -reqwest = { version = "0.11.22", features = ["blocking"] } wp_api = { path = "../wp_api" } wp_networking = { path = "../wp_networking" } diff --git a/wp_cli/src/main.rs b/wp_cli/src/main.rs index 347a209b9..bc1066f02 100644 --- a/wp_cli/src/main.rs +++ b/wp_cli/src/main.rs @@ -1,7 +1,7 @@ use std::{fs::read_to_string, sync::Arc}; -use reqwest::{blocking::Client, header::HeaderMap}; -use wp_api::{WPAuthentication, WPNetworkRequest, WPNetworkResponse, WPNetworkingInterface}; +use wp_api::WPAuthentication; +use wp_networking::WPNetworking; fn main() { // A very naive approach just to get things working for now - this whole code will be deleted @@ -44,39 +44,3 @@ fn main() { // } // // -struct WPNetworking { - client: Client, -} - -impl Default for WPNetworking { - fn default() -> Self { - Self { - client: Client::new(), - } - } -} - -impl WPNetworkingInterface for WPNetworking { - fn request(&self, request: WPNetworkRequest) -> wp_api::WPNetworkResponse { - let method = match request.method { - wp_api::RequestMethod::GET => reqwest::Method::GET, - wp_api::RequestMethod::POST => reqwest::Method::POST, - wp_api::RequestMethod::PUT => reqwest::Method::PUT, - wp_api::RequestMethod::DELETE => reqwest::Method::DELETE, - }; - - let request_headers: HeaderMap = (&request.header_map.unwrap()).try_into().unwrap(); - - // TODO: Error handling - let response = self - .client - .request(method, request.url) - .headers(request_headers) - .send() - .unwrap(); - WPNetworkResponse { - status: Arc::new(response.status()), - body: response.text().unwrap().as_bytes().to_vec(), - } - } -} diff --git a/wp_networking/Cargo.toml b/wp_networking/Cargo.toml index 3a6234069..51dab6776 100644 --- a/wp_networking/Cargo.toml +++ b/wp_networking/Cargo.toml @@ -9,6 +9,7 @@ name = "wp_networking" [dependencies] http = { workspace = true } +reqwest = { version = "0.11.22", features = ["blocking"] } serde_json = "1.0" uniffi = { workspace = true } wp_api = { path = "../wp_api" } diff --git a/wp_networking/build.rs b/wp_networking/build.rs deleted file mode 100644 index d065ab5d6..000000000 --- a/wp_networking/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - uniffi::generate_scaffolding("src/wp_networking.udl").unwrap(); -} diff --git a/wp_networking/src/lib.rs b/wp_networking/src/lib.rs index 6cb9d76d2..65a09c07a 100644 --- a/wp_networking/src/lib.rs +++ b/wp_networking/src/lib.rs @@ -1,23 +1,129 @@ #![allow(dead_code, unused_variables)] + use std::{collections::HashMap, sync::Arc}; +use http::HeaderMap; +use reqwest::blocking::Client; use wp_api::{ ClientErrorType, PageListParams, PageListResponse, PostCreateParams, PostCreateResponse, PostDeleteParams, PostDeleteResponse, PostListParams, PostListResponse, PostObject, PostRetrieveParams, PostRetrieveResponse, PostUpdateParams, PostUpdateResponse, WPApiError, - WPApiInterface, WPAuthentication, WPNetworkRequest, WPNetworkResponse, WPNetworkingInterface, + WPAuthentication, }; -pub fn add_custom(left: i32, right: i32) -> i32 { - left + right +pub struct WPNetworking { + client: Client, +} + +impl Default for WPNetworking { + fn default() -> Self { + Self { + client: Client::new(), + } + } +} + +impl WPNetworkingInterface for WPNetworking { + fn request(&self, request: WPNetworkRequest) -> WPNetworkResponse { + let method = match request.method { + RequestMethod::GET => reqwest::Method::GET, + RequestMethod::POST => reqwest::Method::POST, + RequestMethod::PUT => reqwest::Method::PUT, + RequestMethod::DELETE => reqwest::Method::DELETE, + }; + + let request_headers: HeaderMap = (&request.header_map.unwrap()).try_into().unwrap(); + + // TODO: Error handling + let response = self + .client + .request(method, request.url) + .headers(request_headers) + .send() + .unwrap(); + WPNetworkResponse { + status: Arc::new(response.status()), + body: response.text().unwrap().as_bytes().to_vec(), + } + } +} + +pub trait WPNetworkingInterface: Send + Sync { + fn request(&self, request: WPNetworkRequest) -> WPNetworkResponse; +} + +pub trait NetworkResponseStatus: Send + Sync { + fn as_u16(&self) -> u16; + fn is_informational(&self) -> bool; + fn is_success(&self) -> bool; + fn is_redirection(&self) -> bool; + fn is_client_error(&self) -> bool; + fn is_server_error(&self) -> bool; +} + +impl NetworkResponseStatus for http::StatusCode { + fn as_u16(&self) -> u16 { + self.as_u16() + } + + fn is_informational(&self) -> bool { + self.is_informational() + } + + fn is_success(&self) -> bool { + self.is_success() + } + + fn is_redirection(&self) -> bool { + self.is_redirection() + } + + fn is_client_error(&self) -> bool { + self.is_client_error() + } + + fn is_server_error(&self) -> bool { + self.is_informational() + } +} + +pub enum RequestMethod { + GET, + POST, + PUT, + DELETE, +} + +pub struct WPNetworkRequest { + pub method: RequestMethod, + pub url: String, + // TODO: We probably want to implement a specific type for these headers instead of using a + // regular HashMap. + // + // It could be something similar to `reqwest`'s [`header`](https://docs.rs/reqwest/latest/reqwest/header/index.html) + // module. + pub header_map: Option>, } -pub fn combine_strings(a: String, b: String) -> String { - format!("{}-{}", a, b) +pub struct WPNetworkResponse { + pub status: Arc, + pub body: Vec, } -pub fn panic_from_rust() { - std::fs::read_to_string("doesnt_exist.txt").unwrap(); +pub trait WPApiInterface: Send + Sync { + fn list_posts(&self, params: Option) -> Result; + fn create_post(&self, params: Option) -> PostCreateResponse; + fn retrieve_post( + &self, + post_id: u32, + params: Option, + ) -> PostRetrieveResponse; + + fn update_post(&self, post_id: u32, params: Option) -> PostUpdateResponse; + + fn delete_post(&self, post_id: u32, params: Option) -> PostDeleteResponse; + + fn list_pages(&self, params: Option) -> PageListResponse; } pub fn wp_api_with_custom_networking( @@ -28,7 +134,7 @@ pub fn wp_api_with_custom_networking( Arc::new(WPApi { site_url, authentication, - networking_interface, + networking_interface: networking_interface.clone(), }) } @@ -41,14 +147,14 @@ struct WPApi { impl WPApiInterface for WPApi { fn list_posts(&self, params: Option) -> Result { let mut header_map = HashMap::new(); - // TODO: Authorization headers should be generated through its type not like a cave man + // // TODO: Authorization headers should be generated through its type not like a cave man header_map.insert( "Authorization".into(), format!("Basic {}", self.authentication.auth_token).into(), ); let response = self.networking_interface.request(WPNetworkRequest { - method: wp_api::RequestMethod::GET, + method: RequestMethod::GET, // TODO: Centralize the endpoints url: format!("{}/wp-json/wp/v2/posts?context=edit", self.site_url).into(), header_map: Some(header_map), @@ -103,28 +209,3 @@ fn parse_list_posts_response(response: &WPNetworkResponse) -> Result Date: Tue, 23 Jan 2024 16:19:39 -0500 Subject: [PATCH 14/18] Cleanup wp_networking --- wp_cli/src/main.rs | 33 +------ wp_networking/src/lib.rs | 203 ++++++--------------------------------- 2 files changed, 33 insertions(+), 203 deletions(-) diff --git a/wp_cli/src/main.rs b/wp_cli/src/main.rs index bc1066f02..7f73375bd 100644 --- a/wp_cli/src/main.rs +++ b/wp_cli/src/main.rs @@ -1,7 +1,7 @@ -use std::{fs::read_to_string, sync::Arc}; +use std::fs::read_to_string; use wp_api::WPAuthentication; -use wp_networking::WPNetworking; +use wp_networking::WPApi; fn main() { // A very naive approach just to get things working for now - this whole code will be deleted @@ -15,32 +15,7 @@ fn main() { auth_token: auth_base64_token.into(), }; - let post_list = wp_networking::wp_api_with_custom_networking( - url.into(), - authentication.clone(), - Arc::new(WPNetworking::default()), - ) - .list_posts(None) - .unwrap(); + let wp_networking = WPApi::new(url.into(), authentication); + let post_list = wp_networking.list_posts(None).unwrap(); println!("{:?}", post_list); - - // let post_list_with_custom_networking = wp_networking::wp_api_with_custom_networking( - // mock_authentication.clone(), - // Arc::new(CustomWPNetworking {}), - // ) - // .list_posts(None); - // println!( - // "Post List with custom networking: {:?}", - // post_list_with_custom_networking - // ); } - -// struct CustomWPNetworking {} -// -// impl WPNetworkingInterface for CustomWPNetworking { -// fn request(&self, _request: WPNetworkRequest) -> WPNetworkResponse { -// todo!() -// } -// } -// -// diff --git a/wp_networking/src/lib.rs b/wp_networking/src/lib.rs index 65a09c07a..a12bf632a 100644 --- a/wp_networking/src/lib.rs +++ b/wp_networking/src/lib.rs @@ -1,194 +1,48 @@ #![allow(dead_code, unused_variables)] -use std::{collections::HashMap, sync::Arc}; - -use http::HeaderMap; +use http::Method; use reqwest::blocking::Client; use wp_api::{ - ClientErrorType, PageListParams, PageListResponse, PostCreateParams, PostCreateResponse, - PostDeleteParams, PostDeleteResponse, PostListParams, PostListResponse, PostObject, - PostRetrieveParams, PostRetrieveResponse, PostUpdateParams, PostUpdateResponse, WPApiError, - WPAuthentication, + ClientErrorType, PostListParams, PostListResponse, PostObject, WPApiError, WPAuthentication, }; -pub struct WPNetworking { +pub struct WPApi { client: Client, + site_url: String, + authentication: WPAuthentication, } -impl Default for WPNetworking { - fn default() -> Self { +impl WPApi { + pub fn new(site_url: String, authentication: WPAuthentication) -> Self { Self { - client: Client::new(), + client: reqwest::blocking::Client::new(), + site_url, + authentication, } } -} -impl WPNetworkingInterface for WPNetworking { - fn request(&self, request: WPNetworkRequest) -> WPNetworkResponse { - let method = match request.method { - RequestMethod::GET => reqwest::Method::GET, - RequestMethod::POST => reqwest::Method::POST, - RequestMethod::PUT => reqwest::Method::PUT, - RequestMethod::DELETE => reqwest::Method::DELETE, - }; - - let request_headers: HeaderMap = (&request.header_map.unwrap()).try_into().unwrap(); - - // TODO: Error handling + pub fn list_posts( + &self, + params: Option, + ) -> Result { + let url = format!("{}/wp-json/wp/v2/posts?context=edit", self.site_url); let response = self .client - .request(method, request.url) - .headers(request_headers) + .request(Method::GET, url) + .header( + "Authorization", + format!("Basic {}", self.authentication.auth_token), + ) .send() .unwrap(); - WPNetworkResponse { - status: Arc::new(response.status()), - body: response.text().unwrap().as_bytes().to_vec(), - } - } -} - -pub trait WPNetworkingInterface: Send + Sync { - fn request(&self, request: WPNetworkRequest) -> WPNetworkResponse; -} - -pub trait NetworkResponseStatus: Send + Sync { - fn as_u16(&self) -> u16; - fn is_informational(&self) -> bool; - fn is_success(&self) -> bool; - fn is_redirection(&self) -> bool; - fn is_client_error(&self) -> bool; - fn is_server_error(&self) -> bool; -} - -impl NetworkResponseStatus for http::StatusCode { - fn as_u16(&self) -> u16 { - self.as_u16() - } - - fn is_informational(&self) -> bool { - self.is_informational() - } - - fn is_success(&self) -> bool { - self.is_success() - } - - fn is_redirection(&self) -> bool { - self.is_redirection() - } - - fn is_client_error(&self) -> bool { - self.is_client_error() - } - - fn is_server_error(&self) -> bool { - self.is_informational() - } -} - -pub enum RequestMethod { - GET, - POST, - PUT, - DELETE, -} - -pub struct WPNetworkRequest { - pub method: RequestMethod, - pub url: String, - // TODO: We probably want to implement a specific type for these headers instead of using a - // regular HashMap. - // - // It could be something similar to `reqwest`'s [`header`](https://docs.rs/reqwest/latest/reqwest/header/index.html) - // module. - pub header_map: Option>, -} - -pub struct WPNetworkResponse { - pub status: Arc, - pub body: Vec, -} - -pub trait WPApiInterface: Send + Sync { - fn list_posts(&self, params: Option) -> Result; - fn create_post(&self, params: Option) -> PostCreateResponse; - fn retrieve_post( - &self, - post_id: u32, - params: Option, - ) -> PostRetrieveResponse; - - fn update_post(&self, post_id: u32, params: Option) -> PostUpdateResponse; - - fn delete_post(&self, post_id: u32, params: Option) -> PostDeleteResponse; - - fn list_pages(&self, params: Option) -> PageListResponse; -} - -pub fn wp_api_with_custom_networking( - site_url: String, - authentication: WPAuthentication, - networking_interface: Arc, -) -> Arc { - Arc::new(WPApi { - site_url, - authentication, - networking_interface: networking_interface.clone(), - }) -} - -struct WPApi { - site_url: String, - authentication: WPAuthentication, - networking_interface: Arc, -} - -impl WPApiInterface for WPApi { - fn list_posts(&self, params: Option) -> Result { - let mut header_map = HashMap::new(); - // // TODO: Authorization headers should be generated through its type not like a cave man - header_map.insert( - "Authorization".into(), - format!("Basic {}", self.authentication.auth_token).into(), - ); - - let response = self.networking_interface.request(WPNetworkRequest { - method: RequestMethod::GET, - // TODO: Centralize the endpoints - url: format!("{}/wp-json/wp/v2/posts?context=edit", self.site_url).into(), - header_map: Some(header_map), - }); - parse_list_posts_response(&response) - } - - fn create_post(&self, params: Option) -> PostCreateResponse { - todo!() - } - - fn retrieve_post( - &self, - post_id: u32, - params: Option, - ) -> PostRetrieveResponse { - todo!() - } - - fn update_post(&self, post_id: u32, params: Option) -> PostUpdateResponse { - todo!() - } - - fn delete_post(&self, post_id: u32, params: Option) -> PostDeleteResponse { - todo!() - } - - fn list_pages(&self, params: Option) -> PageListResponse { - todo!() + parse_list_posts_response(response) } } -fn parse_list_posts_response(response: &WPNetworkResponse) -> Result { - let status_code = response.status.as_u16(); +fn parse_list_posts_response( + response: reqwest::blocking::Response, +) -> Result { + let status_code = response.status().as_u16(); // TODO: Further parse the response body to include error message if let Some(client_error_type) = ClientErrorType::from_status_code(status_code) { return Err(WPApiError::ClientError { @@ -196,13 +50,14 @@ fn parse_list_posts_response(response: &WPNetworkResponse) -> Result = serde_json::from_slice(&response.body).or_else(|err| { + let body = response.text().unwrap(); + let post_list: Vec = serde_json::from_str(&body).or_else(|err| { Err(WPApiError::ParsingError { reason: err.to_string(), - response: std::str::from_utf8(&response.body).unwrap().to_string(), + response: body, }) })?; Ok(PostListResponse { From 10f0263620ee68b229ca6e95492f4cf91d711e81 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 24 Jan 2024 12:46:37 -0500 Subject: [PATCH 15/18] Re-introduce WPNetworkRequest and utilize in both Rust and Kotlin --- native/android/common.gradle | 8 +++ .../kotlin/rs/wordpress/wp_api/LibraryTest.kt | 14 ++++++ .../kotlin/rs/wordpress/wp_api/Library.kt | 9 +++- wp_api/src/lib.rs | 49 +++++++++++++++++++ wp_api/src/wp_api.udl | 18 +++++++ wp_cli/src/main.rs | 4 +- wp_networking/src/lib.rs | 34 +++++++------ 7 files changed, 119 insertions(+), 17 deletions(-) diff --git a/native/android/common.gradle b/native/android/common.gradle index 58364e218..4c7e231ef 100644 --- a/native/android/common.gradle +++ b/native/android/common.gradle @@ -18,6 +18,8 @@ ext.configureUniFFIBindgen = { moduleName -> inputs.dir "${project.rootDir}/../../uniffi_bindgen/" // Re-generate if our uniffi-bindgen version changes. inputs.file "${project.rootDir}/../../Cargo.lock" + // Re-generate if the module source code changes + inputs.dir "${project.rootDir}/../../${moduleName}/" } variant.registerJavaGeneratingTask(t.get(), new File(buildDir, uniffiGeneratedPath)) def sourceSet = variant.sourceSets.find { it.name == variant.name } @@ -59,6 +61,12 @@ ext.setupCargo = { moduleName, nativeRustTarget -> ext.getNativeRustTarget = { resourcePrefix -> switch (resourcePrefix) { case 'darwin': + // For unit tests to work in Apple Silicon, we need to return 'darwin-aarch64' here + // However, that runs the cargo task as `cargoBuildDarwin-aarch64` which is not properly + // cached by cargo and requires a rebuild every time. This results in a significant + // development time loss, so for now, we are returning 'darwin' and using instrumented + // tests instead. + // return 'darwin-aarch64' return 'darwin' case 'darwin-aarch64': return 'darwin-aarch64' diff --git a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt index 09942ceed..dac8857bd 100644 --- a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt +++ b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt @@ -4,9 +4,23 @@ package rs.wordpress.wp_api import org.junit.Before +import org.junit.Test +import uniffi.wp_api.RequestMethod +import uniffi.wp_api.WpAuthentication class LibraryTest { + private val siteUrl = "_omitted_" + private val authentication = WpAuthentication(authToken = "_omitted_") + private val library = Library(siteUrl, authentication) + @Before fun setup() { } + + @Test + fun testBasicPostListRequest() { + val request = library.postListRequest() + assert(request.method == RequestMethod.GET) + assert(request.url == "$siteUrl/wp-json/wp/v2/posts?context=edit") + } } diff --git a/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt b/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt index e8eaef93e..01981f4d6 100644 --- a/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt +++ b/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt @@ -3,5 +3,12 @@ */ package rs.wordpress.wp_api -class Library { +import uniffi.wp_api.WpApiHelper +import uniffi.wp_api.WpAuthentication +import uniffi.wp_api.WpNetworkRequest + +class Library(siteUrl: String, authentication: WpAuthentication) { + private val wpApiHelper = WpApiHelper(siteUrl, authentication) + + fun postListRequest(): WpNetworkRequest = wpApiHelper.postListRequest() } diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 5cf489d96..cccfe2828 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -1,5 +1,7 @@ #![allow(dead_code, unused_variables)] +use std::collections::HashMap; + pub use api_error::*; pub use pages::*; pub use posts::*; @@ -8,10 +10,57 @@ pub mod api_error; pub mod pages; pub mod posts; +pub struct WPApiHelper { + site_url: String, + authentication: WPAuthentication, +} + +impl WPApiHelper { + pub fn new(site_url: String, authentication: WPAuthentication) -> Self { + Self { + site_url, + authentication, + } + } + + pub fn post_list_request(&self) -> WPNetworkRequest { + let url = format!("{}/wp-json/wp/v2/posts?context=edit", self.site_url); + + let mut header_map = HashMap::new(); + header_map.insert( + "Authorization".into(), + format!("Basic {}", self.authentication.auth_token).into(), + ); + WPNetworkRequest { + method: RequestMethod::GET, + url, + header_map: Some(header_map), + } + } +} + #[derive(Debug, Clone)] // TODO: This will probably become an `enum` where we support multiple authentication types. pub struct WPAuthentication { pub auth_token: String, } +pub enum RequestMethod { + GET, + POST, + PUT, + DELETE, +} + +pub struct WPNetworkRequest { + pub method: RequestMethod, + pub url: String, + // TODO: We probably want to implement a specific type for these headers instead of using a + // regular HashMap. + // + // It could be something similar to `reqwest`'s [`header`](https://docs.rs/reqwest/latest/reqwest/header/index.html) + // module. + pub header_map: Option>, +} + uniffi::include_scaffolding!("wp_api"); diff --git a/wp_api/src/wp_api.udl b/wp_api/src/wp_api.udl index fb4b6be9d..88ff42a7d 100644 --- a/wp_api/src/wp_api.udl +++ b/wp_api/src/wp_api.udl @@ -20,6 +20,24 @@ dictionary WPAuthentication { string auth_token; }; +enum RequestMethod { + "GET", + "POST", + "PUT", + "DELETE" +}; + +interface WPApiHelper { + constructor(string url, WPAuthentication authentication); + WPNetworkRequest post_list_request(); +}; + +dictionary WPNetworkRequest { + RequestMethod method; + string url; + record? header_map; +}; + dictionary PostListResponse { sequence? post_list; }; diff --git a/wp_cli/src/main.rs b/wp_cli/src/main.rs index 7f73375bd..4ac30d3ee 100644 --- a/wp_cli/src/main.rs +++ b/wp_cli/src/main.rs @@ -1,7 +1,7 @@ use std::fs::read_to_string; use wp_api::WPAuthentication; -use wp_networking::WPApi; +use wp_networking::WPNetworking; fn main() { // A very naive approach just to get things working for now - this whole code will be deleted @@ -15,7 +15,7 @@ fn main() { auth_token: auth_base64_token.into(), }; - let wp_networking = WPApi::new(url.into(), authentication); + let wp_networking = WPNetworking::new(url.into(), authentication); let post_list = wp_networking.list_posts(None).unwrap(); println!("{:?}", post_list); } diff --git a/wp_networking/src/lib.rs b/wp_networking/src/lib.rs index a12bf632a..c061ac310 100644 --- a/wp_networking/src/lib.rs +++ b/wp_networking/src/lib.rs @@ -1,23 +1,22 @@ #![allow(dead_code, unused_variables)] -use http::Method; +use http::HeaderMap; use reqwest::blocking::Client; use wp_api::{ - ClientErrorType, PostListParams, PostListResponse, PostObject, WPApiError, WPAuthentication, + ClientErrorType, PostListParams, PostListResponse, PostObject, WPApiError, WPApiHelper, + WPAuthentication, }; -pub struct WPApi { +pub struct WPNetworking { client: Client, - site_url: String, - authentication: WPAuthentication, + helper: WPApiHelper, } -impl WPApi { +impl WPNetworking { pub fn new(site_url: String, authentication: WPAuthentication) -> Self { Self { client: reqwest::blocking::Client::new(), - site_url, - authentication, + helper: WPApiHelper::new(site_url, authentication), } } @@ -25,20 +24,27 @@ impl WPApi { &self, params: Option, ) -> Result { - let url = format!("{}/wp-json/wp/v2/posts?context=edit", self.site_url); + let wp_request = self.helper.post_list_request(); + let request_headers: HeaderMap = (&wp_request.header_map.unwrap()).try_into().unwrap(); let response = self .client - .request(Method::GET, url) - .header( - "Authorization", - format!("Basic {}", self.authentication.auth_token), - ) + .request(request_method(wp_request.method), wp_request.url) + .headers(request_headers) .send() .unwrap(); parse_list_posts_response(response) } } +fn request_method(method: wp_api::RequestMethod) -> http::Method { + match method { + wp_api::RequestMethod::GET => reqwest::Method::GET, + wp_api::RequestMethod::POST => reqwest::Method::POST, + wp_api::RequestMethod::PUT => reqwest::Method::PUT, + wp_api::RequestMethod::DELETE => reqwest::Method::DELETE, + } +} + fn parse_list_posts_response( response: reqwest::blocking::Response, ) -> Result { From 4e4c896535432cdec54028a81952b400a02aac00 Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 24 Jan 2024 13:26:48 -0500 Subject: [PATCH 16/18] Add WPNetworkResponse and basic parsing to wp_api --- Cargo.lock | 3 +-- wp_api/Cargo.toml | 1 + wp_api/src/lib.rs | 33 +++++++++++++++++++++++++++++++++ wp_api/src/wp_api.udl | 7 +++++++ wp_networking/Cargo.toml | 9 --------- wp_networking/src/lib.rs | 32 ++++++-------------------------- 6 files changed, 48 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8b312ac2d..b625b015c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1681,6 +1681,7 @@ version = "0.1.0" dependencies = [ "http", "serde", + "serde_json", "thiserror", "uniffi", ] @@ -1699,7 +1700,5 @@ version = "0.1.0" dependencies = [ "http", "reqwest", - "serde_json", - "uniffi", "wp_api", ] diff --git a/wp_api/Cargo.toml b/wp_api/Cargo.toml index b7499a463..239e36345 100644 --- a/wp_api/Cargo.toml +++ b/wp_api/Cargo.toml @@ -10,6 +10,7 @@ name = "wp_api" [dependencies] http = { workspace = true } serde = { version = "1.0", features = [ "derive" ] } +serde_json = "1.0" thiserror = "1.0" uniffi = { workspace = true } diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index cccfe2828..4fefa6714 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -63,4 +63,37 @@ pub struct WPNetworkRequest { pub header_map: Option>, } +pub struct WPNetworkResponse { + pub status_code: u16, + pub body: Vec, +} + +pub fn parse_post_list_response( + response: WPNetworkResponse, +) -> Result { + // TODO: Further parse the response body to include error message + // TODO: Lots of unwraps to get a basic setup working + if let Some(client_error_type) = ClientErrorType::from_status_code(response.status_code) { + return Err(WPApiError::ClientError { + error_type: client_error_type, + status_code: response.status_code, + }); + } + let status = http::StatusCode::from_u16(response.status_code).unwrap(); + if status.is_server_error() { + return Err(WPApiError::ServerError { + status_code: response.status_code, + }); + } + let post_list: Vec = serde_json::from_slice(&response.body).or_else(|err| { + Err(WPApiError::ParsingError { + reason: err.to_string(), + response: std::str::from_utf8(&response.body).unwrap().to_string(), + }) + })?; + Ok(PostListResponse { + post_list: Some(post_list), + }) +} + uniffi::include_scaffolding!("wp_api"); diff --git a/wp_api/src/wp_api.udl b/wp_api/src/wp_api.udl index 88ff42a7d..b7e181a4b 100644 --- a/wp_api/src/wp_api.udl +++ b/wp_api/src/wp_api.udl @@ -1,4 +1,6 @@ namespace wp_api { + [Throws=WPApiError] + PostListResponse parse_post_list_response(WPNetworkResponse response); }; enum ClientErrorType { @@ -38,6 +40,11 @@ dictionary WPNetworkRequest { record? header_map; }; +dictionary WPNetworkResponse { + bytes body; + u16 status_code; +}; + dictionary PostListResponse { sequence? post_list; }; diff --git a/wp_networking/Cargo.toml b/wp_networking/Cargo.toml index 51dab6776..0f44c93d6 100644 --- a/wp_networking/Cargo.toml +++ b/wp_networking/Cargo.toml @@ -3,16 +3,7 @@ name = "wp_networking" version = "0.1.0" edition = "2021" -[lib] -crate-type = ["lib", "cdylib", "staticlib"] -name = "wp_networking" - [dependencies] http = { workspace = true } reqwest = { version = "0.11.22", features = ["blocking"] } -serde_json = "1.0" -uniffi = { workspace = true } wp_api = { path = "../wp_api" } - -[build-dependencies] -uniffi = { workspace = true , features = [ "build", "cli" ] } diff --git a/wp_networking/src/lib.rs b/wp_networking/src/lib.rs index c061ac310..fc88b8517 100644 --- a/wp_networking/src/lib.rs +++ b/wp_networking/src/lib.rs @@ -3,8 +3,7 @@ use http::HeaderMap; use reqwest::blocking::Client; use wp_api::{ - ClientErrorType, PostListParams, PostListResponse, PostObject, WPApiError, WPApiHelper, - WPAuthentication, + PostListParams, PostListResponse, WPApiError, WPApiHelper, WPAuthentication, WPNetworkResponse, }; pub struct WPNetworking { @@ -32,7 +31,7 @@ impl WPNetworking { .headers(request_headers) .send() .unwrap(); - parse_list_posts_response(response) + wp_api::parse_post_list_response(wp_network_response(response)) } } @@ -45,28 +44,9 @@ fn request_method(method: wp_api::RequestMethod) -> http::Method { } } -fn parse_list_posts_response( - response: reqwest::blocking::Response, -) -> Result { - let status_code = response.status().as_u16(); - // TODO: Further parse the response body to include error message - if let Some(client_error_type) = ClientErrorType::from_status_code(status_code) { - return Err(WPApiError::ClientError { - error_type: client_error_type, - status_code, - }); +fn wp_network_response(response: reqwest::blocking::Response) -> WPNetworkResponse { + WPNetworkResponse { + status_code: response.status().as_u16(), + body: response.bytes().unwrap().to_vec(), } - if response.status().is_server_error() { - return Err(WPApiError::ServerError { status_code }); - } - let body = response.text().unwrap(); - let post_list: Vec = serde_json::from_str(&body).or_else(|err| { - Err(WPApiError::ParsingError { - reason: err.to_string(), - response: body, - }) - })?; - Ok(PostListResponse { - post_list: Some(post_list), - }) } From 6dee4e82d73baf73b5865375e20130c8e25b909e Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 24 Jan 2024 13:34:42 -0500 Subject: [PATCH 17/18] Make a basic post list request from Kotlin --- native/android/wp_api/build.gradle | 2 ++ .../kotlin/rs/wordpress/wp_api/LibraryTest.kt | 8 +++++++ .../wp_api/src/main/AndroidManifest.xml | 3 +++ .../kotlin/rs/wordpress/wp_api/Library.kt | 23 +++++++++++++++++++ 4 files changed, 36 insertions(+) create mode 100644 native/android/wp_api/src/main/AndroidManifest.xml diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index 753a77487..ee3567a37 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -43,6 +43,8 @@ repositories { } dependencies { + implementation("com.squareup.okhttp3:okhttp:4.12.0") + androidTestImplementation "androidx.test:runner:1.4.0" androidTestImplementation "androidx.test:rules:1.4.0" androidTestImplementation "junit:junit:4.13.2" diff --git a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt index dac8857bd..87f752d69 100644 --- a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt +++ b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt @@ -5,6 +5,7 @@ package rs.wordpress.wp_api import org.junit.Before import org.junit.Test +import uniffi.wp_api.PostObject import uniffi.wp_api.RequestMethod import uniffi.wp_api.WpAuthentication @@ -23,4 +24,11 @@ class LibraryTest { assert(request.method == RequestMethod.GET) assert(request.url == "$siteUrl/wp-json/wp/v2/posts?context=edit") } + + @Test + fun testMakeBasicPostListRequest() { + val postListResponse = library.makePostListRequest() + val firstPost: PostObject = postListResponse.postList!!.first() + assert(firstPost.title?.raw == "Hello world!") + } } diff --git a/native/android/wp_api/src/main/AndroidManifest.xml b/native/android/wp_api/src/main/AndroidManifest.xml new file mode 100644 index 000000000..f145d83b0 --- /dev/null +++ b/native/android/wp_api/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + + diff --git a/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt b/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt index 01981f4d6..41c219051 100644 --- a/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt +++ b/native/android/wp_api/src/main/kotlin/rs/wordpress/wp_api/Library.kt @@ -3,12 +3,35 @@ */ package rs.wordpress.wp_api +import okhttp3.OkHttpClient +import okhttp3.Request +import uniffi.wp_api.PostListResponse import uniffi.wp_api.WpApiHelper import uniffi.wp_api.WpAuthentication import uniffi.wp_api.WpNetworkRequest +import uniffi.wp_api.WpNetworkResponse +import uniffi.wp_api.parsePostListResponse class Library(siteUrl: String, authentication: WpAuthentication) { private val wpApiHelper = WpApiHelper(siteUrl, authentication) + private val client = OkHttpClient() fun postListRequest(): WpNetworkRequest = wpApiHelper.postListRequest() + + fun makePostListRequest(): PostListResponse { + val wpNetworkRequest = postListRequest() + return parsePostListResponse(request(wpNetworkRequest)) + } + + private fun request(wpNetworkRequest: WpNetworkRequest): WpNetworkResponse { + val requestBuilder = Request.Builder() + .url(wpNetworkRequest.url) + wpNetworkRequest.headerMap?.forEach { (key, value) -> + requestBuilder.header(key, value) + } + + client.newCall(requestBuilder.build()).execute().use { response -> + return WpNetworkResponse(response.body!!.bytes(), response.code.toUShort()) + } + } } From be6d38b2b7e8fa2914c6280ffa168b94e3d7b30e Mon Sep 17 00:00:00 2001 From: Oguz Kocer Date: Wed, 24 Jan 2024 14:51:08 -0500 Subject: [PATCH 18/18] Add a Kotlin test to verify basic authentication error --- native/android/wp_api/build.gradle | 1 + .../kotlin/rs/wordpress/wp_api/LibraryTest.kt | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/native/android/wp_api/build.gradle b/native/android/wp_api/build.gradle index ee3567a37..30e6d1704 100644 --- a/native/android/wp_api/build.gradle +++ b/native/android/wp_api/build.gradle @@ -48,6 +48,7 @@ dependencies { androidTestImplementation "androidx.test:runner:1.4.0" androidTestImplementation "androidx.test:rules:1.4.0" androidTestImplementation "junit:junit:4.13.2" + androidTestImplementation 'org.jetbrains.kotlin:kotlin-test' testImplementation "junit:junit:4.13.2" } diff --git a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt index 87f752d69..7bac8a6cb 100644 --- a/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt +++ b/native/android/wp_api/src/androidTest/kotlin/rs/wordpress/wp_api/LibraryTest.kt @@ -7,7 +7,9 @@ import org.junit.Before import org.junit.Test import uniffi.wp_api.PostObject import uniffi.wp_api.RequestMethod +import uniffi.wp_api.WpApiException import uniffi.wp_api.WpAuthentication +import kotlin.test.assertFailsWith class LibraryTest { private val siteUrl = "_omitted_" @@ -31,4 +33,14 @@ class LibraryTest { val firstPost: PostObject = postListResponse.postList!!.first() assert(firstPost.title?.raw == "Hello world!") } + + @Test + fun testBasicAuthenticationError() { + val unauthenticatedLibrary = Library(siteUrl, WpAuthentication("invalid_token")) + val exception = assertFailsWith { + unauthenticatedLibrary.makePostListRequest() + } + val expectedStatusCode: UShort = 401u + assert(exception.statusCode == expectedStatusCode) + } }