diff --git a/includes/api/class-wc-rest-shipping-methods-controller.php b/includes/api/class-wc-rest-shipping-methods-controller.php new file mode 100644 index 00000000000..b7f9cdac390 --- /dev/null +++ b/includes/api/class-wc-rest-shipping-methods-controller.php @@ -0,0 +1,221 @@ + + */ + public function register_routes() { + register_rest_route( $this->namespace, '/' . $this->rest_base, array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + 'args' => $this->get_collection_params(), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) ); + register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_item' ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + 'args' => array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) ); + } + + /** + * Check whether a given request has permission to view system status. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|boolean + */ + public function get_items_permissions_check( $request ) { + if ( ! wc_rest_check_manager_permissions( 'shipping_methods', 'read' ) ) { + return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); + } + return true; + } + + /** + * Check if a given request has access to read a shipping method. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|boolean + */ + public function get_item_permissions_check( $request ) { + if ( ! wc_rest_check_manager_permissions( 'shipping_methods', 'read' ) ) { + return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); + } + return true; + } + + /** + * Get shipping methods. + * + * @param WP_REST_Request $request Full details about the request. + * @return WP_Error|WP_REST_Response + */ + public function get_items( $request ) { + $wc_shipping = WC_Shipping::instance(); + $response = array(); + foreach ( $wc_shipping->get_shipping_methods() as $id => $shipping_method ) { + $method = $this->prepare_item_for_response( $shipping_method, $request ); + $method = $this->prepare_response_for_collection( $method ); + $response[] = $method; + } + return rest_ensure_response( $response ); + } + + /** + * Get a single Shipping Method. + * + * @param WP_REST_Request $request + * @return WP_REST_Response|WP_Error + */ + public function get_item( $request ) { + $wc_shipping = WC_Shipping::instance(); + $methods = $wc_shipping->get_shipping_methods(); + if ( empty ( $methods[ $request['id'] ] ) ) { + return new WP_Error( 'woocommerce_rest_shipping_method_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) ); + } + + $method = $methods[ $request['id'] ]; + $response = $this->prepare_item_for_response( $method, $request ); + + return rest_ensure_response( $response ); + } + + /** + * Prepare a shipping method for response. + * + * @param WP_Comment $method Shipping method object. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response $response Response data. + */ + public function prepare_item_for_response( $method, $request ) { + $data = array( + 'id' => $method->id, + 'title' => $method->method_title, + 'description' => $method->method_description, + ); + + $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; + $data = $this->add_additional_fields_to_object( $data, $request ); + $data = $this->filter_response_by_context( $data, $context ); + + // Wrap the data in a response object. + $response = rest_ensure_response( $data ); + + $response->add_links( $this->prepare_links( $method, $request ) ); + + /** + * Filter product reviews object returned from the REST API. + * + * @param WP_REST_Response $response The response object. + * @param Object $method Product review object used to create response. + * @param WP_REST_Request $request Request object. + */ + return apply_filters( 'woocommerce_rest_prepare_shipping_method', $response, $method, $request ); + } + + /** + * Prepare links for the request. + * + * @param Object $method Shipping method object. + * @param WP_REST_Request $request Request object. + * @return array Links for the given product review. + */ + protected function prepare_links( $method, $request ) { + $links = array( + 'self' => array( + 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $method->id ) ), + ), + 'collection' => array( + 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), + ), + ); + + return $links; + } + + /** + * Get the shipping method schema, conforming to JSON Schema. + * + * @return array + */ + public function get_item_schema() { + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'shipping_method', + 'type' => 'object', + 'properties' => array( + 'id' => array( + 'description' => __( 'Method ID.', 'woocommerce' ), + 'type' => 'string', + 'context' => array( 'view' ), + ), + 'title' => array( + 'description' => __( 'Shipping method title.', 'woocommerce' ), + 'type' => 'string', + 'context' => array( 'view' ), + ), + 'description' => array( + 'description' => __( 'Shipping method description.', 'woocommerce' ), + 'type' => 'string', + 'context' => array( 'view' ), + ), + ), + ); + + return $this->add_additional_fields_schema( $schema ); + } + + /** + * Get any query params needed. + * + * @return array + */ + public function get_collection_params() { + return array( + 'context' => $this->get_context_param( array( 'default' => 'view' ) ), + ); + } + +} diff --git a/includes/class-wc-api.php b/includes/class-wc-api.php index 61756f651f2..ba496e0affc 100644 --- a/includes/class-wc-api.php +++ b/includes/class-wc-api.php @@ -174,6 +174,7 @@ class WC_API extends WC_Legacy_API { include_once( dirname( __FILE__ ) . '/api/class-wc-rest-webhooks-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-system-status-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-system-status-tools-controller.php' ); + include_once( dirname( __FILE__ ) . '/api/class-wc-rest-shipping-methods-controller.php' ); } /** @@ -212,6 +213,7 @@ class WC_API extends WC_Legacy_API { 'WC_REST_Webhooks_Controller', 'WC_REST_System_Status_Controller', 'WC_REST_System_Status_Tools_Controller', + 'WC_REST_Shipping_Methods_Controller', ); foreach ( $controllers as $controller ) { diff --git a/includes/wc-rest-functions.php b/includes/wc-rest-functions.php index 083fe11c330..48b3a725248 100644 --- a/includes/wc-rest-functions.php +++ b/includes/wc-rest-functions.php @@ -306,10 +306,11 @@ function wc_rest_check_product_term_permissions( $taxonomy, $context = 'read', $ */ function wc_rest_check_manager_permissions( $object, $context = 'read' ) { $objects = array( - 'reports' => 'view_woocommerce_reports', - 'settings' => 'manage_woocommerce', - 'system_status' => 'manage_woocommerce', - 'attributes' => 'manage_product_terms', + 'reports' => 'view_woocommerce_reports', + 'settings' => 'manage_woocommerce', + 'system_status' => 'manage_woocommerce', + 'attributes' => 'manage_product_terms', + 'shipping_methods' => 'manage_woocommerce', ); $permission = current_user_can( $objects[ $object ] ); diff --git a/tests/unit-tests/api/shipping-methods.php b/tests/unit-tests/api/shipping-methods.php new file mode 100644 index 00000000000..ff23b7e9022 --- /dev/null +++ b/tests/unit-tests/api/shipping-methods.php @@ -0,0 +1,136 @@ +endpoint = new WC_REST_Shipping_Methods_Controller(); + $this->user = $this->factory->user->create( array( + 'role' => 'administrator', + ) ); + } + + /** + * Test route registration. + * + * @since 2.7.0 + */ + public function test_register_routes() { + $routes = $this->server->get_routes(); + $this->assertArrayHasKey( '/wc/v1/shipping_methods', $routes ); + $this->assertArrayHasKey( '/wc/v1/shipping_methods/(?P[\w-]+)', $routes ); + } + + /** + * Test getting all shipping methods. + * + * @since 2.7.0 + */ + public function test_get_shipping_methods() { + wp_set_current_user( $this->user ); + + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods' ) ); + $methods = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertContains( array( + 'id' => 'free_shipping', + 'title' => 'Free Shipping', + 'description' => 'Free Shipping is a special method which can be triggered with coupons and minimum spends.', + '_links' => array( + 'self' => array( + array( + 'href' => rest_url( '/wc/v1/shipping_methods/free_shipping' ), + ), + ), + 'collection' => array( + array( + 'href' => rest_url( '/wc/v1/shipping_methods' ), + ), + ), + ), + ), $methods ); + } + + /** + * Tests to make sure shipping methods cannot viewed without valid permissions. + * + * @since 2.7.0 + */ + public function test_get_shipping_methods_without_permission() { + wp_set_current_user( 0 ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods' ) ); + $this->assertEquals( 401, $response->get_status() ); + } + + /** + * Tests getting a single shipping method. + * + * @since 2.7.0 + */ + public function test_get_shipping_method() { + wp_set_current_user( $this->user ); + + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods/local_pickup' ) ); + $method = $response->get_data(); + + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( array( + 'id' => 'local_pickup', + 'title' => 'Local Pickup', + 'description' => 'Allow customers to pick up orders themselves. By default, when using local pickup store base taxes will apply regardless of customer address.', + ), $method ); + } + + /** + * Tests getting a single shipping method without the correct permissions. + * + * @since 2.7.0 + */ + public function test_get_shipping_method_without_permission() { + wp_set_current_user( 0 ); + + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods/local_pickup' ) ); + $this->assertEquals( 401, $response->get_status() ); + } + + /** + * Tests getting a shipping method with an invalid ID. + * + * @since 2.7.0 + */ + public function test_get_shipping_method_invalid_id() { + wp_set_current_user( $this->user ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v1/shipping_methods/fake_method' ) ); + $this->assertEquals( 404, $response->get_status() ); + } + + /** + * Test the product review schema. + * + * @since 2.7.0 + */ + public function test_shipping_method_schema() { + wp_set_current_user( $this->user ); + + $request = new WP_REST_Request( 'OPTIONS', '/wc/v1/shipping_methods' ); + $response = $this->server->dispatch( $request ); + $data = $response->get_data(); + $properties = $data['schema']['properties']; + + $this->assertEquals( 3, count( $properties ) ); + $this->assertArrayHasKey( 'id', $properties ); + $this->assertArrayHasKey( 'title', $properties ); + $this->assertArrayHasKey( 'description', $properties ); + } + +}