From 9f3705561d68b20f40c8b498ceea948ceeb5224e Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Mon, 5 Jun 2017 15:53:38 -0700 Subject: [PATCH 1/2] Adds endpoints for getting a list of supported currencies, along with their name and symbol. --- api/class-wc-rest-dev-data-controller.php | 18 ++ ...wc-rest-dev-data-currencies-controller.php | 218 ++++++++++++++++++ tests/unit-tests/data.php | 76 +++++- wc-api-dev.php | 2 + 4 files changed, 313 insertions(+), 1 deletion(-) create mode 100644 api/class-wc-rest-dev-data-currencies-controller.php diff --git a/api/class-wc-rest-dev-data-controller.php b/api/class-wc-rest-dev-data-controller.php index 025ef3c..eb1f626 100644 --- a/api/class-wc-rest-dev-data-controller.php +++ b/api/class-wc-rest-dev-data-controller.php @@ -66,6 +66,20 @@ public function get_items_permissions_check( $request ) { return true; } + /** + * Check whether a given request has permission to read site settings. + * + * @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( 'settings', 'read' ) ) { + return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) ); + } + + return true; + } + /** * Return the list of data resources. * @@ -84,6 +98,10 @@ public function get_items( $request ) { 'slug' => 'countries', 'description' => __( 'List of supported states in a given country.', 'woocommerce' ), ), + array( + 'slug' => 'currencies', + 'description' => __( 'List of supported currencies.', 'woocommerce' ), + ), ); foreach ( $resources as $resource ) { diff --git a/api/class-wc-rest-dev-data-currencies-controller.php b/api/class-wc-rest-dev-data-currencies-controller.php new file mode 100644 index 0000000..ec1ae74 --- /dev/null +++ b/api/class-wc-rest-dev-data-currencies-controller.php @@ -0,0 +1,218 @@ +namespace, '/' . $this->rest_base, array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_items' ), + 'permission_callback' => array( $this, 'get_items_permissions_check' ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) ); + register_rest_route( $this->namespace, '/' . $this->rest_base . '/current', array( + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_current_item' ), + 'permission_callback' => array( $this, 'get_item_permissions_check' ), + ), + '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( + 'location' => array( + 'description' => __( 'ISO4217 currency code.', 'woocommerce' ), + 'type' => 'string', + ), + ), + ), + 'schema' => array( $this, 'get_public_item_schema' ), + ) ); + } + + /** + * Get currency information. + * + * @param string $code + * @param WP_REST_Request $request + * @return array|mixed Response data, ready for insertion into collection data. + */ + public function get_currency( $code = false, $request ) { + $currencies = get_woocommerce_currencies(); + $data = array(); + + if ( ! array_key_exists( $code, $currencies ) ) { + return false; + } + + $currency = array( + 'code' => $code, + 'name' => $currencies[ $code ], + 'symbol' => get_woocommerce_currency_symbol( $code ), + ); + + return $currency; + } + + /** + * Return the list of currencies. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public function get_items( $request ) { + $currencies = get_woocommerce_currencies(); + foreach ( array_keys( $currencies ) as $code ) { + $currency = $this->get_currency( $code, $request ); + $response = $this->prepare_item_for_response( $currency, $request ); + $data[] = $this->prepare_response_for_collection( $response ); + } + + return rest_ensure_response( $data ); + } + + /** + * Return information for a specific currency. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public function get_item( $request ) { + $data = $this->get_currency( strtoupper( $request['currency'] ), $request ); + if ( empty( $data ) ) { + return new WP_Error( 'woocommerce_rest_data_invalid_currency', __( 'There are no currencies matching these parameters.', 'woocommerce' ), array( 'status' => 404 ) ); + } + return $this->prepare_item_for_response( $data, $request ); + } + + /** + * Return information for the current site currency. + * + * @param WP_REST_Request $request + * @return WP_Error|WP_REST_Response + */ + public function get_current_item( $request ) { + $currency = get_option( 'woocommerce_currency' ); + return $this->prepare_item_for_response( $this->get_currency( $currency, $request ), $request ); + } + + /** + * Prepare the data object for response. + * + * @param object $item Data object. + * @param WP_REST_Request $request Request object. + * @return WP_REST_Response $response Response data. + */ + public function prepare_item_for_response( $item, $request ) { + $data = $this->add_additional_fields_to_object( $item, $request ); + $data = $this->filter_response_by_context( $data, 'view' ); + $response = rest_ensure_response( $data ); + + $response->add_links( $this->prepare_links( $item ) ); + + /** + * Filter currency returned from the API. + * + * @param WP_REST_Response $response The response object. + * @param array $item Currency data. + * @param WP_REST_Request $request Request used to generate the response. + */ + return apply_filters( 'woocommerce_rest_prepare_data_currency', $response, $item, $request ); + } + + /** + * Prepare links for the request. + * + * @param object $item Data object. + * @return array Links for the given currency. + */ + protected function prepare_links( $item ) { + $code = strtoupper( $item['code'] ); + $links = array( + 'self' => array( + 'href' => rest_url( sprintf( '/%s/%s/%s', $this->namespace, $this->rest_base, $code ) ), + ), + 'collection' => array( + 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ), + ), + ); + + return $links; + } + + + /** + * Get the currency schema, conforming to JSON Schema. + * + * @return array + */ + public function get_item_schema() { + $schema = array( + '$schema' => 'http://json-schema.org/draft-04/schema#', + 'title' => 'data_currencies', + 'type' => 'object', + 'properties' => array( + 'code' => array( + 'type' => 'string', + 'description' => __( 'ISO4217 currency code.', 'woocommerce' ), + 'context' => array( 'view' ), + 'readonly' => true, + ), + 'name' => array( + 'type' => 'string', + 'description' => __( 'Full name of currency.', 'woocommerce' ), + 'context' => array( 'view' ), + 'readonly' => true, + ), + 'symbol' => array( + 'type' => 'string', + 'description' => __( 'Currency symbol.', 'woocommerce' ), + 'context' => array( 'view' ), + 'readonly' => true, + ), + ), + ); + + return $this->add_additional_fields_schema( $schema ); + } +} diff --git a/tests/unit-tests/data.php b/tests/unit-tests/data.php index ce5bb21..149eb81 100644 --- a/tests/unit-tests/data.php +++ b/tests/unit-tests/data.php @@ -27,6 +27,9 @@ public function test_register_routes() { $this->assertArrayHasKey( '/wc/v3/data', $routes ); $this->assertArrayHasKey( '/wc/v3/data/continents', $routes ); $this->assertArrayHasKey( '/wc/v3/data/countries', $routes ); + $this->assertArrayHasKey( '/wc/v3/data/currencies', $routes ); + $this->assertArrayHasKey( '/wc/v3/data/currencies/current', $routes ); + $this->assertArrayHasKey( '/wc/v3/data/currencies/(?P[\w-]+)', $routes ); } /** @@ -38,9 +41,10 @@ public function test_get_index() { $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data' ) ); $index = $response->get_data(); $this->assertEquals( 200, $response->get_status() ); - $this->assertCount( 2, $index ); + $this->assertCount( 3, $index ); $this->assertEquals( 'continents', $index[0]['slug'] ); $this->assertEquals( 'countries', $index[1]['slug'] ); + $this->assertEquals( 'currencies', $index[2]['slug'] ); } /** @@ -149,4 +153,74 @@ public function test_get_countries_without_permission() { $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/countries' ) ); $this->assertEquals( 401, $response->get_status() ); } + + /** + * Test getting currencies. + */ + public function test_get_currencies() { + wp_set_current_user( $this->user ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/currencies' ) ); + $currencies = $response->get_data(); + $this->assertEquals( 200, $response->get_status() ); + $this->assertTrue( is_array( $currencies ) ); + $this->assertGreaterThan( 1, count( $currencies ) ); + $this->assertNotEmpty( $currencies[0]['code'] ); + $this->assertNotEmpty( $currencies[0]['name'] ); + $this->assertNotEmpty( $currencies[0]['symbol'] ); + $this->assertNotEmpty( $currencies[0]['_links'] ); + } + + /** + * Test getting a single currency. + */ + public function test_get_currency() { + wp_set_current_user( $this->user ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/currencies/CAD' ) ); + $currency = $response->get_data(); + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'CAD', $currency['code'] ); + $this->assertEquals( 'Canadian dollar', $currency['name'] ); + $this->assertEquals( '$', $currency['symbol'] ); + $links = $response->get_links(); + $this->assertCount( 2, $links ); + } + + /** + * Test getting current currency. + */ + public function test_get_current_currency() { + $current = get_option( 'woocommerce_currency' ); + update_option( 'woocommerce_currency', 'BTC' ); + + wp_set_current_user( $this->user ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/currencies/current' ) ); + $currency = $response->get_data(); + $this->assertEquals( 200, $response->get_status() ); + $this->assertEquals( 'BTC', $currency['code'] ); + $this->assertEquals( 'Bitcoin', $currency['name'] ); + $this->assertEquals( '฿', $currency['symbol'] ); + $links = $response->get_links(); + $this->assertCount( 2, $links ); + + update_option( 'woocommerce_currency', $current ); + } + + /** + * Test getting currency from an invalid code. + */ + public function test_get_currency_from_invalid_code() { + wp_set_current_user( $this->user ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/currencies/xxy' ) ); + $this->assertEquals( 404, $response->get_status() ); + } + + /** + * Test getting currencies without permissions. + */ + public function test_get_currency_without_permission() { + wp_set_current_user( 0 ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/currencies' ) ); + $this->assertEquals( 401, $response->get_status() ); + } + } diff --git a/wc-api-dev.php b/wc-api-dev.php index 1fce248..0b897dd 100644 --- a/wc-api-dev.php +++ b/wc-api-dev.php @@ -79,6 +79,7 @@ public function includes() { include_once( dirname( __FILE__ ) . '/api/class-wc-rest-dev-data-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-dev-data-continents-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-dev-data-countries-controller.php' ); + include_once( dirname( __FILE__ ) . '/api/class-wc-rest-dev-data-currencies-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-dev-orders-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-dev-order-notes-controller.php' ); include_once( dirname( __FILE__ ) . '/api/class-wc-rest-dev-order-refunds-controller.php' ); @@ -121,6 +122,7 @@ public function register_routes() { 'WC_REST_Dev_Data_Controller', 'WC_REST_Dev_Data_Continents_Controller', 'WC_REST_Dev_Data_Countries_Controller', + 'WC_REST_Dev_Data_Currencies_Controller', 'WC_REST_Dev_Order_Notes_Controller', 'WC_REST_Dev_Order_Refunds_Controller', 'WC_REST_Dev_Orders_Controller', From b38220416f1f65d3b958ffa2c001cc1035370057 Mon Sep 17 00:00:00 2001 From: Justin Shreve Date: Tue, 6 Jun 2017 09:24:23 -0700 Subject: [PATCH 2/2] Limit regex check for currency code to 3 characters. --- ...class-wc-rest-dev-data-currencies-controller.php | 2 +- tests/unit-tests/data.php | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/api/class-wc-rest-dev-data-currencies-controller.php b/api/class-wc-rest-dev-data-currencies-controller.php index ec1ae74..7d4096e 100644 --- a/api/class-wc-rest-dev-data-currencies-controller.php +++ b/api/class-wc-rest-dev-data-currencies-controller.php @@ -54,7 +54,7 @@ public function register_routes() { ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); - register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]+)', array( + register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P[\w-]{3}+)', array( array( 'methods' => WP_REST_Server::READABLE, 'callback' => array( $this, 'get_item' ), diff --git a/tests/unit-tests/data.php b/tests/unit-tests/data.php index 149eb81..2f6ba0f 100644 --- a/tests/unit-tests/data.php +++ b/tests/unit-tests/data.php @@ -29,7 +29,7 @@ public function test_register_routes() { $this->assertArrayHasKey( '/wc/v3/data/countries', $routes ); $this->assertArrayHasKey( '/wc/v3/data/currencies', $routes ); $this->assertArrayHasKey( '/wc/v3/data/currencies/current', $routes ); - $this->assertArrayHasKey( '/wc/v3/data/currencies/(?P[\w-]+)', $routes ); + $this->assertArrayHasKey( '/wc/v3/data/currencies/(?P[\w-]{3}+)', $routes ); } /** @@ -212,6 +212,17 @@ public function test_get_currency_from_invalid_code() { wp_set_current_user( $this->user ); $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/currencies/xxy' ) ); $this->assertEquals( 404, $response->get_status() ); + $this->assertEquals( 'woocommerce_rest_data_invalid_currency', $response->data['code'] ); + } + + /** + * Test getting currency from an code that is too long. + */ + public function test_get_currency_from_long_code() { + wp_set_current_user( $this->user ); + $response = $this->server->dispatch( new WP_REST_Request( 'GET', '/wc/v3/data/currencies/xxyy' ) ); + $this->assertEquals( 404, $response->get_status() ); + $this->assertEquals( 'rest_no_route', $response->data['code'] ); } /**