Skip to content

Commit b2f85a5

Browse files
authored
Merge pull request #216 from wp-graphql/release/v0.3.3
Release v0.3.3
2 parents bef3537 + 8ee87f4 commit b2f85a5

34 files changed

+1279
-222
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ branches:
1717
- master
1818
- release-v0.1.2
1919
- release-v0.2.2
20-
- release-v0.3.1
20+
- release-v0.3.3
2121

2222
cache:
2323
apt: true

README.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,20 @@
66
## Quick Install
77
1. Install & activate [WooCommerce](https://woocommerce.com/)
88
2. Install & activate [WPGraphQL](https://www.wpgraphql.com/)
9-
3. (Optional) Install & activate [WPGraphQL-JWT-Authentication](https://github.com/wp-graphql/wp-graphql-jwt-authentication) to add a `login` mutation that returns a JSON Web Token.
10-
4. Clone or download the zip of this repository into your WordPress plugin directory & activate the **WP GraphQL WooCommerce** plugin
9+
3. Clone or download the zip of this repository into your WordPress plugin directory & activate the **WP GraphQL WooCommerce** plugin.
10+
4. (Optional) Install & activate [WPGraphQL-JWT-Authentication](https://github.com/wp-graphql/wp-graphql-jwt-authentication) to add a `login` mutation that returns a JSON Web Token.
11+
5. (Optional) Install & activate [WPGraphQL-CORS](https://github.com/funkhaus/wp-graphql-cors) to add an extra layer of security using HTTP CORS and some of WPGraphQL advanced functionality.
1112

1213
## What does this plugin do?
1314
It adds WooCommerce functionality to the WPGraphQL schema using WooCommerce's [CRUD](https://github.com/woocommerce/woocommerce/wiki/CRUD-Objects-in-3.0) objects.
1415

1516
## Features
16-
- Query product, customers, coupons, order, refund, product variations with complex filtering options.
17-
- Add items to cart and process user store session using HTTP header defined by WooGraphQL's built-in session handler
18-
- Create/process user checkout with the `checkout` mutation.
17+
- Query **product**, **product variations**, **customers**, **coupons**, **orders**, **refunds** and **more** with complex filtering options.
18+
- Manipulate customer session data using customer and cart mutations while managing customer session token using HTTP headers or cookies *(not recommended)*. *[HTTP header example w/ React/Apollo](https://github.com/wp-graphql/wp-graphql-woocommerce/pull/88)*
19+
- Create orders using the `order` mutations with the `checkout` mutation.
1920

2021
## Future Features
22+
- Payment Processing
2123
- Adminstrator mutations. Eg. Creating and deleting products, coupons, and refunds.
2224

2325
## Playground
@@ -84,14 +86,16 @@ If you use the command with at least a `suite` specified, **Codeception** will r
8486

8587
To learn more about the usage of Codeception with WordPress view the [Documentation](https://codeception.com/for/wordpress)
8688

87-
## Functional and Acceptance Tests (Docker/Docker-Compose required)
89+
## Functional and Acceptance Tests (Docker && Docker-Compose required)
8890
It's possible to run functional and acceptance tests, but is very limited at the moment. The script docker entrypoint script runs all three suites (acceptance, functional, and wpunit) at once. This will change eventually, however as of right now, this is the limitation.
8991

9092
### Running tests
91-
Even though the two suite use a Docker environment to run, the docker environment relies on a few environmental variables defined in `.env.dist` and a volume source provided by the test install script and the configuration `codeception.dist.yml`. If you have created a `codeception.yml` file ensure it is identical to `codeception.dist.yml` or delete it.
93+
Even though the two suites use a Docker environment to run, the `testing` service in the `docker.compose.yml` file requires the `.env.dist` and `codeception.dist.yml` untouched.
9294
Run the following in the terminal to run all three suites. Isolating specific suites should be simple to figure out.
9395
```
94-
docker-compose run --rm -e SUITE=acceptance;wpunit;functional -e DEBUG=1 -e COVERAGE=1 testing --scale app=0
96+
docker-compose run --rm -e \
97+
SUITE=acceptance;wpunit;functional \
98+
-e DEBUG=1 -e COVERAGE=1 testing --scale app=0
9599
```
96100
- The `COVERAGE`, and `DEBUG` vars are optional flags for toggle codecoverage and debug output.
97101
- `--scale app=0` ensures that the service running a local app doesn't create any instances. It must be added or a collision with `mysql` will occur. More on this service in the next section
@@ -118,6 +122,4 @@ If you get HTTP 500 error upon activation or accessing the `endpoint` and have *
118122

119123
**GraphQL-PHP** :point_right: **[OpenCollective](https://opencollective.com/webonyx-graphql-php)**
120124

121-
## Follow
122-
[![alt text](http://i.imgur.com/tXSoThF.png)](https://twitter.com/woographql)
123-
[![alt text](http://i.imgur.com/P3YfQoD.png)](https://www.facebook.com/woographql)
125+
## Follow [![alt text](http://i.imgur.com/tXSoThF.png)](https://twitter.com/woographql)[![alt text](http://i.imgur.com/P3YfQoD.png)](https://www.facebook.com/woographql)

README.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Tags: GraphQL, WooCommerce, WPGraphQL
44
Requires at least: 4.9
55
Tested up to: 5.2
66
Requires PHP: 5.6
7-
Stable tag: 0.3.2
7+
Stable tag: 0.3.3
88
License: GPL-3
99
License URI: https://www.gnu.org/licenses/gpl-3.0.html
1010
Maintained at: https://github.com/wp-graphql/wp-graphql-woocommerce

includes/connection/class-cart-items.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ public static function get_connection_config( $args = array() ) {
8484
*/
8585
public static function get_connection_args() {
8686
return array(
87-
'needShipping' => array(
87+
'needsShipping' => array(
8888
'type' => 'Boolean',
8989
'description' => __( 'Limit results to cart items that require shipping', 'wp-graphql-woocommerce' ),
9090
),

includes/data/connection/class-cart-item-connection-resolver.php

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
* Class Cart_Item_Connection_Resolver
2121
*/
2222
class Cart_Item_Connection_Resolver extends AbstractConnectionResolver {
23+
/**
24+
* Include shared connection functions.
25+
*/
26+
use WC_Connection_Functions;
27+
2328
/**
2429
* Confirms if cart items should be retrieved.
2530
*
@@ -30,23 +35,34 @@ public function should_execute() {
3035
}
3136

3237
/**
33-
* Creates query arguments array
38+
* Creates cart item filters.
39+
*
40+
* @return array
3441
*/
3542
public function get_query_args() {
36-
$query_args = array();
43+
$query_args = array( 'filters' => array() );
3744
if ( ! empty( $this->args['where'] ) ) {
3845
$where_args = $this->args['where'];
39-
if ( ! empty( $where_args['needShipping'] ) ) {
40-
$query_args['filters'] = array();
41-
$query_args['filters'][] = function( $cart_item ) {
46+
if ( isset( $where_args['needsShipping'] ) ) {
47+
$needs_shipping = $where_args['needsShipping'];
48+
$query_args['filters'][] = function( $cart_item ) use ( $needs_shipping ) {
4249
$product = \WC()->product_factory->get_product( $cart_item['product_id'] );
43-
if ( $product ) {
44-
return $product->needs_shipping();
45-
}
50+
return $needs_shipping === (bool) $product->needs_shipping();
4651
};
4752
}
4853
}
4954

55+
/**
56+
* Filter the $query_args to allow folks to customize queries programmatically.
57+
*
58+
* @param array $query_args The args that will be passed to the WP_Query.
59+
* @param mixed $source The source that's passed down the GraphQL queries.
60+
* @param array $args The inputArgs on the field.
61+
* @param AppContext $context The AppContext passed down the GraphQL tree.
62+
* @param ResolveInfo $info The ResolveInfo passed down the GraphQL tree.
63+
*/
64+
$query_args = apply_filters( 'graphql_cart_item_connection_query_args', $query_args, $this->source, $this->args, $this->context, $this->info );
65+
5066
return $query_args;
5167
}
5268

@@ -58,13 +74,9 @@ public function get_query_args() {
5874
public function get_query() {
5975
$cart_items = array_values( $this->source->get_cart() );
6076

61-
if ( ! empty( $this->query_args['filters'] ) ) {
62-
if ( is_array( $this->query_args['filters'] ) ) {
63-
foreach ( $this->query_args['filters'] as $filter ) {
64-
$cart_items = array_filter( $cart_items, $filter );
65-
}
66-
} else {
67-
$cart_items = array_filter( $cart_items, $this->query_args['filters'] );
77+
if ( ! empty( $this->query_args['filters'] ) && is_array( $this->query_args['filters'] ) ) {
78+
foreach ( $this->query_args['filters'] as $filter ) {
79+
$cart_items = array_filter( $cart_items, $filter );
6880
}
6981
}
7082

@@ -122,4 +134,15 @@ protected function get_cursor_for_node( $node, $key = null ) {
122134
public function get_items() {
123135
return ! empty( $this->query ) ? $this->query : array();
124136
}
137+
138+
/**
139+
* Wrapper for "WC_Connection_Functions::is_valid_cart_item_offset()"
140+
*
141+
* @param integer $offset Post ID.
142+
*
143+
* @return bool
144+
*/
145+
public function is_valid_offset( $offset ) {
146+
return $this->is_valid_cart_item_offset( $offset );
147+
}
125148
}

includes/data/connection/class-coupon-connection-resolver.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
* Class Coupon_Connection_Resolver
2020
*/
2121
class Coupon_Connection_Resolver extends AbstractConnectionResolver {
22-
use Common_CPT_Input_Sanitize_Functions;
22+
/**
23+
* Include shared connection functions.
24+
*/
25+
use WC_Connection_Functions;
2326

2427
/**
2528
* The name of the post type, or array of post types the connection resolver is resolving for
@@ -105,7 +108,7 @@ public function get_query_args() {
105108
/**
106109
* Collect the input_fields and sanitize them to prepare them for sending to the WP_Query
107110
*/
108-
$input_fields = [];
111+
$input_fields = array();
109112
if ( ! empty( $this->args['where'] ) ) {
110113
$input_fields = $this->sanitize_input_fields( $this->args['where'] );
111114
}
@@ -163,7 +166,7 @@ public function get_query() {
163166
* @return array
164167
*/
165168
public function get_items() {
166-
return ! empty( $this->query->posts ) ? $this->query->posts : [];
169+
return ! empty( $this->query->posts ) ? $this->query->posts : array();
167170
}
168171

169172
/**
@@ -222,4 +225,15 @@ public function sanitize_input_fields( array $where_args ) {
222225

223226
return $args;
224227
}
228+
229+
/**
230+
* Wrapper for "WC_Connection_Functions::is_valid_post_offset()"
231+
*
232+
* @param integer $offset Post ID.
233+
*
234+
* @return bool
235+
*/
236+
public function is_valid_offset( $offset ) {
237+
return $this->is_valid_post_offset( $offset );
238+
}
225239
}

includes/data/connection/class-customer-connection-resolver.php

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@
1818
* Class Customer_Connection_Resolver
1919
*/
2020
class Customer_Connection_Resolver extends AbstractConnectionResolver {
21+
/**
22+
* Include shared connection functions.
23+
*/
24+
use WC_Connection_Functions;
25+
2126
/**
2227
* Confirms the uses has the privileges to query Customers
2328
*
@@ -36,14 +41,26 @@ public function should_execute() {
3641
* Creates query arguments array
3742
*/
3843
public function get_query_args() {
44+
/**
45+
* Prepare for later use
46+
*/
47+
$last = ! empty( $this->args['last'] ) ? $this->args['last'] : null;
48+
3949
/**
4050
* Set the $query_args based on various defaults and primary input $args
4151
*/
4252
$query_args['count_total'] = false;
43-
$query_args['offset'] = $this->get_offset();
4453
$query_args['orderby'] = 'ID';
4554
$query_args['order'] = ! empty( $this->args['last'] ) ? 'ASC' : 'DESC';
46-
$query_args['number'] = $this->get_query_amount();
55+
$query_args['number'] = $this->get_query_amount() + 1;
56+
57+
/**
58+
* Set the graphql_cursor_offset which is used by Config::graphql_wp_user_query_cursor_pagination_support
59+
* to filter the WP_User_Query to support cursor pagination
60+
*/
61+
$cursor_offset = $this->get_offset();
62+
$query_args['graphql_cursor_offset'] = $cursor_offset;
63+
$query_args['graphql_cursor_compare'] = ( ! empty( $last ) ) ? '>' : '<';
4764

4865
$input_fields = array();
4966
if ( ! empty( $this->args['where'] ) ) {
@@ -69,6 +86,51 @@ public function get_query_args() {
6986

7087
$query_args['fields'] = 'ID';
7188

89+
/**
90+
* Map the orderby inputArgs to the WP_User_Query
91+
*/
92+
if ( ! empty( $this->args['where']['orderby'] ) && is_array( $this->args['where']['orderby'] ) ) {
93+
$query_args['orderby'] = array();
94+
foreach ( $this->args['where']['orderby'] as $orderby_input ) {
95+
/**
96+
* These orderby options should not include the order parameter.
97+
*/
98+
if ( in_array( $orderby_input['field'], array( 'login__in', 'nicename__in' ), true ) ) {
99+
$query_args['orderby'] = esc_sql( $orderby_input['field'] );
100+
} elseif ( ! empty( $orderby_input['field'] ) ) {
101+
$query_args['orderby'] = array(
102+
esc_sql( $orderby_input['field'] ) => esc_sql( $orderby_input['order'] ),
103+
);
104+
}
105+
}
106+
}
107+
108+
/**
109+
* Convert meta_value_num to seperate meta_value value field which our
110+
* graphql_wp_term_query_cursor_pagination_support knowns how to handle
111+
*/
112+
if ( isset( $query_args['orderby'] ) && 'meta_value_num' === $query_args['orderby'] ) {
113+
$query_args['orderby'] = array(
114+
'meta_value' => empty( $query_args['order'] ) ? 'DESC' : $query_args['order'], // WPCS: slow query OK.
115+
);
116+
unset( $query_args['order'] );
117+
$query_args['meta_type'] = 'NUMERIC';
118+
}
119+
/**
120+
* If there's no orderby params in the inputArgs, set order based on the first/last argument
121+
*/
122+
if ( empty( $query_args['orderby'] ) ) {
123+
$query_args['order'] = ! empty( $last ) ? 'ASC' : 'DESC';
124+
}
125+
126+
if (
127+
empty( $query_args['role'] ) &&
128+
empty( $query_args['role__in'] ) &&
129+
empty( $query_args['role__not_in'] )
130+
) {
131+
$query_args['role'] = 'customer';
132+
}
133+
72134
$query_args = apply_filters(
73135
'graphql_customer_connection_query_args',
74136
$query_args,
@@ -150,4 +212,15 @@ public function sanitize_input_fields( array $where_args ) {
150212

151213
return $args;
152214
}
215+
216+
/**
217+
* Wrapper for "WC_Connection_Functions::is_valid_user_offset()"
218+
*
219+
* @param integer $offset User ID.
220+
*
221+
* @return bool
222+
*/
223+
public function is_valid_offset( $offset ) {
224+
return $this->is_valid_user_offset( $offset );
225+
}
153226
}

includes/data/connection/class-order-connection-resolver.php

Lines changed: 25 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
* Class Order_Connection_Resolver
2020
*/
2121
class Order_Connection_Resolver extends AbstractConnectionResolver {
22-
use Common_CPT_Input_Sanitize_Functions;
22+
/**
23+
* Include shared connection functions.
24+
*/
25+
use WC_Connection_Functions;
2326

2427
/**
2528
* The name of the post type, or array of post types the connection resolver is resolving for
@@ -48,21 +51,21 @@ public function __construct( $source, $args, $context, $info ) {
4851
}
4952

5053
/**
51-
* Confirms the uses has the privileges to query Orders
54+
* Checks if user is authorized to query orders
5255
*
5356
* @return bool
5457
*/
5558
public function should_execute() {
56-
$post_type_obj = get_post_type_object( 'shop_order' );
57-
switch ( true ) {
58-
case current_user_can( $post_type_obj->cap->edit_posts ):
59-
case is_a( $this->source, Customer::class )
60-
&& 'orders' === $this->info->fieldName
61-
&& get_current_user_id() === $this->source->ID:
62-
return true;
63-
default:
64-
return false;
59+
$post_type_obj = get_post_type_object( $this->post_type );
60+
if ( current_user_can( $post_type_obj->cap->edit_posts ) ) {
61+
return true;
62+
}
63+
64+
if ( is_a( $this->source, Customer::class ) ) {
65+
return 'orders' === $this->info->fieldName && get_current_user_id() === $this->source->ID;
6566
}
67+
68+
return false;
6669
}
6770

6871
/**
@@ -274,4 +277,15 @@ public function sanitize_input_fields( array $where_args ) {
274277

275278
return $args;
276279
}
280+
281+
/**
282+
* Wrapper for "WC_Connection_Functions::is_valid_post_offset()"
283+
*
284+
* @param integer $offset Post ID.
285+
*
286+
* @return bool
287+
*/
288+
public function is_valid_offset( $offset ) {
289+
return $this->is_valid_post_offset( $offset );
290+
}
277291
}

0 commit comments

Comments
 (0)