Skip to content
This repository was archived by the owner on Feb 4, 2025. It is now read-only.

Commit d058757

Browse files
authored
Merge pull request #1424 from wordpress-mobile/react-native/image-size-networking
Add React Native store for making calls to a given path
2 parents d52d3af + d050706 commit d058757

File tree

15 files changed

+628
-0
lines changed

15 files changed

+628
-0
lines changed

example/src/androidTest/java/org/wordpress/android/fluxc/release/ReleaseStack_AppComponent.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,4 +66,6 @@ public interface ReleaseStack_AppComponent {
6666
void inject(ReleaseStack_TransactionsTest test);
6767
void inject(ReleaseStack_WCOrderListTest test);
6868
void inject(ReleaseStack_PostSchedulingTestJetpack test);
69+
void inject(ReleaseStack_ReactNativeWPAPIRequestTest test);
70+
void inject(ReleaseStack_ReactNativeWPComRequestTest test);
6971
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package org.wordpress.android.fluxc.release
2+
3+
import kotlinx.coroutines.runBlocking
4+
import org.junit.Assert.assertTrue
5+
import org.junit.Test
6+
import org.wordpress.android.fluxc.example.BuildConfig
7+
import org.wordpress.android.fluxc.store.AccountStore
8+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Success
9+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Error
10+
import org.wordpress.android.fluxc.store.ReactNativeStore
11+
import org.wordpress.android.fluxc.store.SiteStore
12+
import javax.inject.Inject
13+
14+
class ReleaseStack_ReactNativeWPAPIRequestTest : ReleaseStack_Base() {
15+
@Inject lateinit var reactNativeStore: ReactNativeStore
16+
@Inject internal lateinit var siteStore: SiteStore
17+
@Inject internal lateinit var accountStore: AccountStore
18+
19+
@Throws(Exception::class)
20+
override fun setUp() {
21+
super.setUp()
22+
mReleaseStackAppComponent.inject(this)
23+
}
24+
25+
@Test
26+
fun testWPAPICallOnSelfHosted() {
27+
val url = BuildConfig.TEST_WPORG_URL_SH_WPAPI_SIMPLE + "/wp-json/wp/v2/media/"
28+
val params = mapOf("context" to "view")
29+
val response = runBlocking { reactNativeStore.performWPAPIRequest(url, params) }
30+
31+
val failureMessage = "Call failed with error: ${(response as? Error)?.error}"
32+
assertTrue(failureMessage, response is Success)
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.wordpress.android.fluxc.release
2+
3+
import kotlinx.coroutines.runBlocking
4+
import org.junit.Assert.assertTrue
5+
import org.junit.Test
6+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Success
7+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Error
8+
import org.wordpress.android.fluxc.store.ReactNativeStore
9+
import javax.inject.Inject
10+
11+
class ReleaseStack_ReactNativeWPComRequestTest : ReleaseStack_WPComBase() {
12+
@Inject lateinit var reactNativeStore: ReactNativeStore
13+
14+
override fun setUp() {
15+
super.setUp()
16+
mReleaseStackAppComponent.inject(this)
17+
init()
18+
}
19+
20+
@Test
21+
fun testWpComCall() {
22+
val url = "https://public-api.wordpress.com/wp/v2/sites/${siteFromDb.siteId}/media"
23+
val params = mapOf("context" to "edit")
24+
val response = runBlocking { reactNativeStore.performWPComRequest(url, params) }
25+
26+
val failureMessage = "Call failed with error: ${(response as? Error)?.error}"
27+
assertTrue(failureMessage, response is Success)
28+
}
29+
}

example/src/main/java/org/wordpress/android/fluxc/example/MainFragment.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ class MainFragment : Fragment() {
9191
themes.setOnClickListener(getOnClickListener(ThemeFragment()))
9292
woo.setOnClickListener(getOnClickListener(WooCommerceFragment()))
9393
notifs.setOnClickListener(getOnClickListener(NotificationsFragment()))
94+
reactnative.setOnClickListener(getOnClickListener(ReactNativeFragment()))
9495
}
9596

9697
// Private methods
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.wordpress.android.fluxc.example
2+
3+
import android.content.Context
4+
import android.net.Uri
5+
import android.os.Bundle
6+
import android.view.LayoutInflater
7+
import android.view.View
8+
import android.view.ViewGroup
9+
import android.widget.EditText
10+
import androidx.fragment.app.Fragment
11+
import dagger.android.support.AndroidSupportInjection
12+
import kotlinx.android.synthetic.main.fragment_reactnative.*
13+
import kotlinx.coroutines.Dispatchers
14+
import kotlinx.coroutines.GlobalScope
15+
import kotlinx.coroutines.launch
16+
import kotlinx.coroutines.withContext
17+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse
18+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Error
19+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse.Success
20+
import org.wordpress.android.fluxc.store.ReactNativeStore
21+
import org.wordpress.android.util.AppLog
22+
import javax.inject.Inject
23+
24+
class ReactNativeFragment : Fragment() {
25+
@Inject internal lateinit var reactNativeStore: ReactNativeStore
26+
27+
override fun onAttach(context: Context?) {
28+
AndroidSupportInjection.inject(this)
29+
super.onAttach(context)
30+
}
31+
32+
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
33+
inflater.inflate(R.layout.fragment_reactnative, container, false)
34+
35+
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
36+
super.onViewCreated(view, savedInstanceState)
37+
38+
wp_api_request_button.setOnClickListener {
39+
makeCall(reactNativeStore::performWPAPIRequest, wp_api_url_field, "WP api")
40+
}
41+
wp_com_request_button.setOnClickListener {
42+
makeCall(reactNativeStore::performWPComRequest, wp_com_url_field, "WP.com")
43+
}
44+
}
45+
46+
private fun makeCall(
47+
callFunction: suspend (path: String, params: Map<String, String>) -> ReactNativeFetchResponse,
48+
urlField: EditText,
49+
callType: String
50+
) {
51+
val uri = Uri.parse(urlField.text.toString())
52+
val fullPath = "${uri.scheme}://${uri.authority}${uri.path}"
53+
val paramMap = uri.queryParameterNames.map { name ->
54+
name to uri.getQueryParameter(name)
55+
}.toMap()
56+
57+
GlobalScope.launch(Dispatchers.Main) {
58+
val response = withContext(Dispatchers.IO) {
59+
callFunction(fullPath, paramMap)
60+
}
61+
62+
when (response) {
63+
is Success -> {
64+
prependToLog("$callType call succeeded")
65+
AppLog.i(AppLog.T.API, "$callType call result: ${response.result}")
66+
}
67+
is Error -> prependToLog("$callType call failed: ${response.error}")
68+
}
69+
}
70+
}
71+
}

example/src/main/java/org/wordpress/android/fluxc/example/di/FragmentsModule.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.wordpress.android.fluxc.example.MainFragment
88
import org.wordpress.android.fluxc.example.MediaFragment
99
import org.wordpress.android.fluxc.example.NotificationsFragment
1010
import org.wordpress.android.fluxc.example.PostsFragment
11+
import org.wordpress.android.fluxc.example.ReactNativeFragment
1112
import org.wordpress.android.fluxc.example.SignedOutActionsFragment
1213
import org.wordpress.android.fluxc.example.SiteSelectorDialog
1314
import org.wordpress.android.fluxc.example.SitesFragment
@@ -84,4 +85,7 @@ internal abstract class FragmentsModule {
8485

8586
@ContributesAndroidInjector
8687
abstract fun provideStoreSelectorDialogInjector(): StoreSelectorDialog
88+
89+
@ContributesAndroidInjector
90+
abstract fun provideReactNativeFramgmentInjector(): ReactNativeFragment
8791
}

example/src/main/res/layout/fragment_main.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@
123123
android:layout_height="wrap_content"
124124
android:layout_gravity="right"
125125
android:text="Notifications"/>
126+
127+
<Button
128+
android:id="@+id/reactnative"
129+
android:layout_width="wrap_content"
130+
android:layout_height="wrap_content"
131+
android:layout_gravity="right"
132+
android:text="React Native"/>
126133
</LinearLayout>
127134
</LinearLayout>
128135
</LinearLayout>
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:app="http://schemas.android.com/apk/res-auto"
4+
android:layout_width="match_parent"
5+
android:layout_height="match_parent"
6+
android:padding="16dp"
7+
>
8+
9+
<FrameLayout
10+
android:id="@+id/wp_com_box"
11+
android:layout_width="match_parent"
12+
android:layout_height="0dp"
13+
android:background="#1000"
14+
android:padding="30dp"
15+
app:layout_constraintTop_toTopOf="parent"
16+
app:layout_constraintBottom_toBottomOf="@+id/wp_com_request_button"
17+
/>
18+
19+
<EditText
20+
android:id="@+id/wp_com_url_field"
21+
android:layout_width="0dp"
22+
android:layout_height="wrap_content"
23+
android:layout_margin="20dp"
24+
android:hint="Enter full WP.com path"
25+
app:layout_constraintTop_toTopOf="@+id/wp_com_box"
26+
app:layout_constraintStart_toStartOf="@id/wp_com_box"
27+
app:layout_constraintEnd_toEndOf="@+id/wp_com_box"
28+
/>
29+
30+
<Button
31+
android:id="@+id/wp_com_request_button"
32+
android:layout_width="wrap_content"
33+
android:layout_height="wrap_content"
34+
android:padding="8dp"
35+
android:text="Execute WP.com Request"
36+
app:layout_constraintTop_toBottomOf="@+id/wp_com_url_field"
37+
app:layout_constraintStart_toStartOf="parent"
38+
app:layout_constraintEnd_toEndOf="parent"
39+
/>
40+
41+
42+
<FrameLayout
43+
android:id="@+id/wp_api_box"
44+
android:layout_width="match_parent"
45+
android:layout_height="0dp"
46+
android:background="#1000"
47+
android:layout_marginTop="30dp"
48+
android:paddingBottom="30dp"
49+
app:layout_constraintTop_toBottomOf="@id/wp_com_box"
50+
app:layout_constraintBottom_toBottomOf="@+id/wp_api_request_button"
51+
/>
52+
53+
<EditText
54+
android:id="@+id/wp_api_url_field"
55+
android:layout_width="match_parent"
56+
android:layout_height="wrap_content"
57+
android:layout_margin="10dp"
58+
android:hint="Enter full WP api path"
59+
app:layout_constraintTop_toTopOf="@+id/wp_api_box"
60+
app:layout_constraintStart_toStartOf="@+id/wp_api_box"
61+
app:layout_constraintEnd_toEndOf="@+id/wp_api_box"
62+
/>
63+
64+
<Button
65+
android:id="@+id/wp_api_request_button"
66+
android:layout_width="wrap_content"
67+
android:layout_height="wrap_content"
68+
android:text="Execute WP api Request"
69+
app:layout_constraintTop_toBottomOf="@+id/wp_api_url_field"
70+
app:layout_constraintStart_toStartOf="@id/wp_api_box"
71+
app:layout_constraintEnd_toEndOf="@id/wp_api_box"
72+
/>
73+
74+
</androidx.constraintlayout.widget.ConstraintLayout>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package org.wordpress.android.fluxc.network.rest.wpapi.reactnative
2+
3+
import com.android.volley.RequestQueue
4+
import com.google.gson.JsonElement
5+
import com.nhaarman.mockitokotlin2.mock
6+
import com.nhaarman.mockitokotlin2.whenever
7+
import junit.framework.Assert.assertEquals
8+
import junit.framework.Assert.fail
9+
import junit.framework.AssertionFailedError
10+
import org.junit.Before
11+
import org.junit.Test
12+
import org.wordpress.android.fluxc.Dispatcher
13+
import org.wordpress.android.fluxc.network.BaseRequest.BaseNetworkError
14+
import org.wordpress.android.fluxc.network.UserAgent
15+
import org.wordpress.android.fluxc.network.rest.wpapi.WPAPIGsonRequestBuilder
16+
import org.wordpress.android.fluxc.network.rest.wpapi.WPAPIGsonRequestBuilder.Response.Error
17+
import org.wordpress.android.fluxc.network.rest.wpapi.WPAPIGsonRequestBuilder.Response.Success
18+
import org.wordpress.android.fluxc.store.ReactNativeFetchResponse
19+
import org.wordpress.android.fluxc.test
20+
21+
class ReactNativeWPAPIRestClientTest {
22+
private val wpApiGsonRequestBuilder = mock<WPAPIGsonRequestBuilder>()
23+
private val dispatcher = mock<Dispatcher>()
24+
private val requestQueue = mock<RequestQueue>()
25+
private val userAgent = mock<UserAgent>()
26+
27+
private val url = "a_url"
28+
val params = mapOf("a_key" to "a_value")
29+
30+
private lateinit var subject: ReactNativeWPAPIRestClient
31+
32+
@Before
33+
fun setUp() {
34+
subject = ReactNativeWPAPIRestClient(wpApiGsonRequestBuilder, dispatcher, requestQueue, userAgent)
35+
}
36+
37+
@Test
38+
fun `fetch handles successful response`() = test {
39+
val errorHandler: (BaseNetworkError) -> ReactNativeFetchResponse = { _ ->
40+
throw AssertionFailedError("errorHandler should not have been called")
41+
}
42+
43+
val expected = mock<ReactNativeFetchResponse>()
44+
val expectedJson = mock<JsonElement>()
45+
val successHandler = { data: JsonElement ->
46+
if (data != expectedJson) fail("expected data was not passed to successHandler")
47+
expected
48+
}
49+
50+
val expectedRestCallResponse = Success(expectedJson)
51+
verifyRestApi(successHandler, errorHandler, expectedRestCallResponse, expected)
52+
}
53+
54+
@Test
55+
fun `fetch handles failure response`() = test {
56+
val successHandler = { _: JsonElement ->
57+
throw AssertionFailedError("successHandler should not have been called")
58+
}
59+
60+
val expected = mock<ReactNativeFetchResponse>()
61+
val expectedBaseNetworkError = mock<BaseNetworkError>()
62+
val errorHandler = { error: BaseNetworkError ->
63+
if (error != expectedBaseNetworkError) fail("expected error was not passed to errorHandler")
64+
expected
65+
}
66+
67+
val mockedRestCallResponse = Error<JsonElement>(expectedBaseNetworkError)
68+
verifyRestApi(successHandler, errorHandler, mockedRestCallResponse, expected)
69+
}
70+
71+
private suspend fun verifyRestApi(
72+
successHandler: (JsonElement) -> ReactNativeFetchResponse,
73+
errorHandler: (BaseNetworkError) -> ReactNativeFetchResponse,
74+
expectedRestCallResponse: WPAPIGsonRequestBuilder.Response<JsonElement>,
75+
expected: ReactNativeFetchResponse
76+
) {
77+
whenever(wpApiGsonRequestBuilder.syncGetRequest(
78+
subject,
79+
url,
80+
params,
81+
emptyMap(),
82+
JsonElement::class.java,
83+
true)
84+
).thenReturn(expectedRestCallResponse)
85+
86+
val actual = subject.fetch(url, params, successHandler, errorHandler)
87+
assertEquals(expected, actual)
88+
}
89+
}

0 commit comments

Comments
 (0)