Skip to content

Commit

Permalink
[RNMobile] Support POST requests (#49371)
Browse files Browse the repository at this point in the history
* Add post request function to the RN bridge [Android]

* Add POST request method in fetch handler

* Add POST request to demo app

* Divide supported endpoints by request method

* Add default value for `data` param

* Add `media` supported path for POST requests

* Add post request function to the RN bridge [iOS]

* Allow POST requests on iOS

* Update react-native-editor CHANGELOG

* Add POST request to demo app [iOS]

* Remove WPCOM endpoint
  • Loading branch information
fluiddot authored Apr 5, 2023
1 parent dd66a82 commit 82ed8b7
Show file tree
Hide file tree
Showing 12 changed files with 127 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ public void getOtherMediaOptions(ReadableArray filter, final Callback jsCallback

@ReactMethod
public void fetchRequest(String path, boolean enableCaching, Promise promise) {
mGutenbergBridgeJS2Parent.performRequest(path,
mGutenbergBridgeJS2Parent.performGetRequest(path,
enableCaching,
promise::resolve,
errorBundle -> {
Expand All @@ -345,6 +345,21 @@ public void fetchRequest(String path, boolean enableCaching, Promise promise) {
});
}

@ReactMethod
public void postRequest(String path, ReadableMap data, Promise promise) {
mGutenbergBridgeJS2Parent.performPostRequest(path, data,
promise::resolve,
errorBundle -> {
WritableMap writableMap = Arguments.makeNativeMap(errorBundle);
if (writableMap.hasKey("code")) {
String code = String.valueOf(writableMap.getInt("code"));
promise.reject(code, new Error(), writableMap);
} else {
promise.reject(new Error(), writableMap);
}
});
}

@ReactMethod
public void requestUnsupportedBlockFallback(String content, String blockId, String blockName, String blockTitle) {
mGutenbergBridgeJS2Parent.gutenbergDidRequestUnsupportedBlockFallback((savedContent, savedBlockId) ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

import androidx.core.util.Consumer;

import com.facebook.react.bridge.ReadableMap;

public interface RequestExecutor {
void performRequest(String path, boolean enableCaching, Consumer<String> onSuccess, Consumer<Bundle> onError);
void performGetRequest(String path, boolean enableCaching, Consumer<String> onSuccess, Consumer<Bundle> onError);
void performPostRequest(String path, ReadableMap data, Consumer<String> onSuccess, Consumer<Bundle> onError);
}
Original file line number Diff line number Diff line change
Expand Up @@ -430,8 +430,13 @@ public void requestMediaPickFrom(String mediaSource,
}

@Override
public void performRequest(String pathFromJS, boolean enableCaching, Consumer<String> onSuccess, Consumer<Bundle> onError) {
mRequestExecutor.performRequest(pathFromJS, enableCaching, onSuccess, onError);
public void performGetRequest(String pathFromJS, boolean enableCaching, Consumer<String> onSuccess, Consumer<Bundle> onError) {
mRequestExecutor.performGetRequest(pathFromJS, enableCaching, onSuccess, onError);
}

@Override
public void performPostRequest(String pathFromJS, ReadableMap data, Consumer<String> onSuccess, Consumer<Bundle> onError) {
mRequestExecutor.performPostRequest(pathFromJS, data, onSuccess, onError);
}

@Override
Expand Down
4 changes: 4 additions & 0 deletions packages/react-native-bridge/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,10 @@ export function fetchRequest( path, enableCaching = true ) {
return RNReactNativeGutenbergBridge.fetchRequest( path );
}

export function postRequest( path, data = {} ) {
return RNReactNativeGutenbergBridge.postRequest( path, data );
}

export function showUserSuggestions() {
return RNReactNativeGutenbergBridge.showUserSuggestions();
}
Expand Down
11 changes: 9 additions & 2 deletions packages/react-native-bridge/ios/GutenbergBridgeDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,19 @@ public protocol GutenbergBridgeDelegate: class {
///
func editorDidAutosave()

/// Tells the delegate that the editor needs to perform a network request.
/// Tells the delegate that the editor needs to perform a GET request.
/// The paths given to perform the request are from the WP ORG REST API.
/// https://developer.wordpress.org/rest-api/reference/
/// - Parameter path: The path to perform the request.
/// - Parameter completion: Completion handler to be called with the result or an error.
func gutenbergDidRequestFetch(path: String, completion: @escaping (Swift.Result<Any, NSError>) -> Void)
func gutenbergDidGetRequestFetch(path: String, completion: @escaping (Swift.Result<Any, NSError>) -> Void)

/// Tells the delegate that the editor needs to perform a POST request.
/// The paths given to perform the request are from the WP ORG REST API.
/// https://developer.wordpress.org/rest-api/reference/
/// - Parameter path: The path to perform the request.
/// - Parameter completion: Completion handler to be called with the result or an error.
func gutenbergDidPostRequestFetch(path: String, data: [String: AnyObject]?, completion: @escaping (Swift.Result<Any, NSError>) -> Void)

/// Tells the delegate to display a fullscreen image from a given URL
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ @interface RCT_EXTERN_MODULE(RNReactNativeGutenbergBridge, NSObject)
RCT_EXTERN_METHOD(editorDidEmitLog:(NSString *)message logLevel:(int)logLevel)
RCT_EXTERN_METHOD(editorDidAutosave)
RCT_EXTERN_METHOD(fetchRequest:(NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(postRequest:(NSString *)path data:(NSDictionary *)parameters resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXTERN_METHOD(requestImageFullscreenPreview:(NSString *)currentImageUrlString originalImageUrlString:(NSString *)originalImageUrlString)
RCT_EXTERN_METHOD(requestMediaEditor:(NSString *)mediaUrl callback:(RCTResponseSenderBlock)callback)
RCT_EXTERN_METHOD(requestUnsupportedBlockFallback:(NSString *)content blockId:(NSString *)blockId blockName:(NSString *)blockName blockTitle:(NSString *)blockTitle)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,19 @@ public class RNReactNativeGutenbergBridge: RCTEventEmitter {

@objc
func fetchRequest(_ path: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
self.delegate?.gutenbergDidRequestFetch(path: path, completion: { (result) in
self.delegate?.gutenbergDidGetRequestFetch(path: path, completion: { (result) in
switch result {
case .success(let response):
resolver(response)
case .failure(let error):
rejecter("\(error.code)", error.description, error)
}
})
}

@objc
func postRequest(_ path: String, data: [String: AnyObject]?, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
self.delegate?.gutenbergDidPostRequestFetch(path: path, data: data, completion: { (result) in
switch result {
case .success(let response):
resolver(response)
Expand Down
1 change: 1 addition & 0 deletions packages/react-native-editor/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ For each user feature we should also add a importance categorization label to i
-->

## Unreleased
- [*] Support POST requests [#49371]

## 1.92.0
* No User facing changes *
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,10 @@ public void editorDidEmitLog(String message, LogLevel logLevel) {
}

@Override
public void performRequest(String path, boolean enableCaching, Consumer<String> onSuccess, Consumer<Bundle> onError) {}
public void performGetRequest(String path, boolean enableCaching, Consumer<String> onSuccess, Consumer<Bundle> onError) {}

@Override
public void performPostRequest(String path, ReadableMap data, Consumer<String> onSuccess, Consumer<Bundle> onError) {}

@Override
public void gutenbergDidRequestUnsupportedBlockFallback(ReplaceUnsupportedBlockCallback replaceUnsupportedBlockCallback,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,11 @@ class GutenbergViewController: UIViewController {
}

extension GutenbergViewController: GutenbergBridgeDelegate {
func gutenbergDidRequestFetch(path: String, completion: @escaping (Result<Any, NSError>) -> Void) {
func gutenbergDidGetRequestFetch(path: String, completion: @escaping (Result<Any, NSError>) -> Void) {
completion(Result.success([:]))
}

func gutenbergDidPostRequestFetch(path: String, data: [String: AnyObject]?, completion: @escaping (Result<Any, NSError>) -> Void) {
completion(Result.success([:]))
}

Expand Down
49 changes: 37 additions & 12 deletions packages/react-native-editor/src/api-fetch-setup.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,50 @@
/**
* WordPress dependencies
*/
import { fetchRequest } from '@wordpress/react-native-bridge';
import { fetchRequest, postRequest } from '@wordpress/react-native-bridge';
import apiFetch from '@wordpress/api-fetch';

const SUPPORTED_METHODS = [ 'GET', 'POST' ];
// Please add only wp.org API paths here!
const SUPPORTED_ENDPOINTS = [
/wp\/v2\/(media|categories|blocks|themes)\/?\d*?.*/i,
/wp\/v2\/search\?.*/i,
/oembed\/1\.0\/proxy\?.*/i,
];
const SUPPORTED_ENDPOINTS = {
GET: [
/wp\/v2\/(media|categories|blocks|themes)\/?\d*?.*/i,
/wp\/v2\/search\?.*/i,
/oembed\/1\.0\/proxy\?.*/i,
],
POST: [],
};

// [ONLY ON ANDROID] The requests made to these endpoints won't be cached.
const DISABLED_CACHING_ENDPOINTS = [ /wp\/v2\/(blocks)\/?\d*?.*/i ];

const setTimeoutPromise = ( delay ) =>
new Promise( ( resolve ) => setTimeout( resolve, delay ) );

const fetchHandler = ( { path }, retries = 20, retryCount = 1 ) => {
if ( ! isPathSupported( path ) ) {
return Promise.reject( `Unsupported path: ${ path }` );
const fetchHandler = (
{ path, method = 'GET', data },
retries = 20,
retryCount = 1
) => {
if ( ! isMethodSupported( method ) ) {
return Promise.reject( `Unsupported method: ${ method }` );
}

if ( ! isPathSupported( path, method ) ) {
return Promise.reject(
`Unsupported path for method ${ method }: ${ path }`
);
}

const responsePromise = fetchRequest( path, shouldEnableCaching( path ) );
let responsePromise;
switch ( method ) {
case 'GET':
responsePromise = fetchRequest( path, shouldEnableCaching( path ) );
break;
case 'POST':
responsePromise = postRequest( path, data );
break;
}

const parseResponse = ( response ) => {
if ( typeof response === 'string' ) {
Expand All @@ -45,8 +67,11 @@ const fetchHandler = ( { path }, retries = 20, retryCount = 1 ) => {
} );
};

export const isPathSupported = ( path ) =>
SUPPORTED_ENDPOINTS.some( ( pattern ) => pattern.test( path ) );
export const isMethodSupported = ( method ) =>
SUPPORTED_METHODS.includes( method );

export const isPathSupported = ( path, method ) =>
SUPPORTED_ENDPOINTS[ method ].some( ( pattern ) => pattern.test( path ) );

export const shouldEnableCaching = ( path ) =>
! DISABLED_CACHING_ENDPOINTS.some( ( pattern ) => pattern.test( path ) );
Expand Down
46 changes: 26 additions & 20 deletions packages/react-native-editor/src/test/api-fetch-setup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@
*/
import { isPathSupported, shouldEnableCaching } from '../api-fetch-setup';

const supportedPaths = [
'wp/v2/media/54?context=edit&_locale=user',
'wp/v2/media/5?context=edit',
'wp/v2/media/54/',
'wp/v2/media/',
'wp/v2/media?context=edit&_locale=user',
'wp/v2/categories/',
'wp/v2/blocks/28?_locale=user',
'/wp/v2/blocks?per_page=100&context=edit&_locale=user',
];
const supportedPaths = {
GET: [
'wp/v2/media/54?context=edit&_locale=user',
'wp/v2/media/5?context=edit',
'wp/v2/media/54/',
'wp/v2/media/',
'wp/v2/media?context=edit&_locale=user',
'wp/v2/categories/',
'wp/v2/blocks/28?_locale=user',
'/wp/v2/blocks?per_page=100&context=edit&_locale=user',
],
};

const unsupportedPaths = [
'wp/v1/media/', // Made up example.
];
const unsupportedPaths = {
GET: [
'wp/v1/media/', // Made up example.
],
};

const enabledCachingPaths = [
'wp/v2/media/54?context=edit&_locale=user',
Expand All @@ -33,15 +37,17 @@ const disabledCachingPaths = [
];

describe( 'isPathSupported', () => {
supportedPaths.forEach( ( path ) => {
it( `supports ${ path }`, () => {
expect( isPathSupported( path ) ).toBe( true );
describe( 'GET requests', () => {
supportedPaths.GET.forEach( ( path ) => {
it( `supports ${ path }`, () => {
expect( isPathSupported( path, 'GET' ) ).toBe( true );
} );
} );
} );

unsupportedPaths.forEach( ( path ) => {
it( `does not support ${ path }`, () => {
expect( isPathSupported( path ) ).toBe( false );
unsupportedPaths.GET.forEach( ( path ) => {
it( `does not support ${ path }`, () => {
expect( isPathSupported( path, 'GET' ) ).toBe( false );
} );
} );
} );
} );
Expand Down

1 comment on commit 82ed8b7

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flaky tests detected in 82ed8b7.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/4616278091
📝 Reported issues:

Please sign in to comment.