Skip to content

Commit f4b153f

Browse files
authored
feat: Docs Restored. Unsupported product type setting implemented. (#731)
* feat: docs started. Unsupported product type implemented. * chore: WPCS compliance met * chore: file renamed. * feat: More docs written * feat: 2 more docs written * fix: settings docs images added. * fix: settings docs images added. * devops: UnsupportProductTypeTest added. * devops: More docs changes. * chore: WPCS compliance met * devops: UnsupportedProductTypeTest updated. * chore: WPCS compliance met
1 parent b2d6608 commit f4b153f

33 files changed

+2723
-118
lines changed

access-functions.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,51 @@ function wc_graphql_camel_case_to_underscore( $string ) {
229229
}
230230
}//end if
231231

232+
/**
233+
* Plugin global functions.
234+
*
235+
* @package Axis\Plugin_Distributor
236+
*/
237+
238+
if ( ! function_exists( 'woographql_setting' ) ) :
239+
/**
240+
* Get an option value from WooGraphQL settings
241+
*
242+
* @param string $option_name The key of the option to return.
243+
* @param mixed $default The default value the setting should return if no value is set.
244+
* @param string $section_name The settings section name.
245+
*
246+
* @return mixed|string|int|boolean
247+
*/
248+
function woographql_setting( string $option_name, $default = '', $section_name = 'woographql_settings' ) {
249+
$section_fields = get_option( $section_name );
250+
251+
/**
252+
* Filter the section fields
253+
*
254+
* @param array $section_fields The values of the fields stored for the section
255+
* @param string $section_name The name of the section
256+
* @param mixed $default The default value for the option being retrieved
257+
*/
258+
$section_fields = apply_filters( 'woographql_settings_section_fields', $section_fields, $section_name, $default );
259+
260+
/**
261+
* Get the value from the stored data, or return the default
262+
*/
263+
$value = isset( $section_fields[ $option_name ] ) ? $section_fields[ $option_name ] : $default;
264+
265+
/**
266+
* Filter the value before returning it
267+
*
268+
* @param mixed $value The value of the field
269+
* @param mixed $default The default value if there is no value set
270+
* @param string $option_name The name of the option
271+
* @param array $section_fields The setting values within the section
272+
* @param string $section_name The name of the section the setting belongs to
273+
*/
274+
return apply_filters( 'woographql_settings_section_field_value', $value, $default, $option_name, $section_fields, $section_name );
275+
}
276+
endif;
232277

233278

234279

bin/_lib.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ install_wordpress() {
3232

3333
# Install Wordpress + integrated plugins for testing/development.
3434
composer install
35-
composer require --dev -W \
35+
composer require --dev --no-interaction -W \
3636
johnpbloch/wordpress:* \
3737
wp-graphql/wp-graphql-jwt-authentication \
3838
wpackagist-plugin/woocommerce \
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
# Configuring a GraphQL Client for WooCommerce User Session Management
2+
3+
In this comprehensive guide, we'll walk you through the process of configuring a GraphQL client to manage user sessions and credentials when working with WooGraphQL. By following the steps outlined in this tutorial, you'll learn how to create a GraphQL client that maintains a valid WooCommerce session in the `woocommerce_sessions` DB table. This knowledge will enable you to build robust applications that interact smoothly with WooCommerce while providing a seamless experience for your users and shortening development time.
4+
5+
By properly handling the session token, you can implement session pass-off functionality, allowing you to fallback on the cart page, my-account page, or any other page living in WordPress that relies on user sessions. Note that implementing the session pass-off functionality is out of the scope of this guide. So, let's dive in and explore the intricacies of setting up a GraphQL client that effectively manages user sessions for your e-commerce store!
6+
7+
When using WooGraphQL cart and customer functionality, there are certain prerequisites. A WooGraphQL session token, distributed by the QL Session Handler, must be passed as an HTTP header with the name `woocommerce-session`, prefixed with `Session`. This header should be included in all session data-altering mutations. Note that the required name `woocommerce-session` can be changed using WordPress filters.
8+
9+
For simple requests using `fetch`, this is quite easy to implement. Here's an example of a WooGraphQL request executed with `fetch`, performing a cart query and passing the woocommerce-session header with a value of `Session ${sessionToken}`. The `sessionToken` is read from `localStorage`.
10+
11+
```javascript
12+
fetch(endpoint, {
13+
method: 'POST',
14+
headers: {
15+
'Content-Type': 'application/json',
16+
'woocommerce-session': `Session ${sessionToken}`,
17+
},
18+
body: JSON.stringify({
19+
query: `
20+
query {
21+
cart {
22+
contents {
23+
nodes {
24+
key
25+
product {
26+
node {
27+
id
28+
name
29+
}
30+
}
31+
quantity
32+
subtotal
33+
}
34+
}
35+
}
36+
}
37+
`,
38+
}),
39+
})
40+
.then((response) => response.json())
41+
.then((data) => console.log(data));
42+
```
43+
44+
However, if you're using a library or framework like Apollo, configuring a middleware layer is required, which can be confusing if not explained or demonstrated effectively. In this guide, we'll walk you through setting up the Apollo Client and its middleware to work with WooGraphQL.
45+
46+
## Creating the Apollo Client instance
47+
48+
First, let's create the Apollo Client instance and focus on the `link` option. We'll use the `from` utility function from `@apollo/client`:
49+
50+
```javascript
51+
import { from } from '@apollo/client';
52+
53+
// ...
54+
55+
const client = new ApolloClient({
56+
link: from([
57+
createSessionLink(),
58+
createErrorLink(),
59+
new HttpLink({ uri: endpoint }),
60+
]),
61+
cache: new InMemoryCache(),
62+
});
63+
```
64+
65+
## Defining the `createSessionLink` function
66+
67+
Next, define the `createSessionLink` function as follows:
68+
69+
```javascript
70+
import { setContext } from '@apollo/client/link/context';
71+
72+
function createSessionLink() {
73+
return setContext(async (operation) => {
74+
const headers = {};
75+
const sessionToken = await getSessionToken();
76+
77+
if (sessionToken) {
78+
headers['woocommerce-session'] = `Session ${sessionToken}`;
79+
80+
return { headers };
81+
}
82+
83+
return {};
84+
});
85+
}
86+
```
87+
88+
## About the environment variables
89+
90+
Before we dive into the guide, it's important to note that the `process.env.*` variables used throughout the tutorial are simply string values stored in an `.env` file and loaded using the [**dotenv**](https://www.npmjs.com/package/dotenv) package. As a reader, you can replace these variables with any values that suit your needs.
91+
92+
Here's a sample .env file to help you get started:
93+
94+
```makefile
95+
SESSION_TOKEN_LS_KEY=my_session_token
96+
REFRESH_TOKEN_LS_KEY=my_refresh_token
97+
AUTH_TOKEN_LS_KEY=my_auth_token
98+
AUTH_KEY_TIMEOUT=30000
99+
GRAPHQL_ENDPOINT=http://woographql.local/graphql
100+
```
101+
102+
## Defining the `getSessionToken` and `fetchSessionToken` functions
103+
104+
Here are the `getSessionToken` and `fetchSessionToken` functions:
105+
106+
```javascript
107+
import { GraphQLClient } from 'graphql-request';
108+
109+
// Session Token Management.
110+
async function fetchSessionToken() {
111+
let sessionToken;
112+
try {
113+
const graphQLClient = new GraphQLClient(process.env.GRAPHQL_ENDPOINT);
114+
115+
const cartData = await graphQLClient.request(GetCartDocument);
116+
117+
// If user doesn't have an account return accountNeeded flag.
118+
sessionToken = cartData?.cart?.sessionToken;
119+
120+
if (!sessionToken) {
121+
throw new Error('Failed to retrieve a new session token');
122+
}
123+
} catch (err) {
124+
console.error(err);
125+
}
126+
127+
return sessionToken;
128+
}
129+
130+
export async function getSessionToken(forceFetch = false) {
131+
let sessionToken = localStorage.getItem(process.env.SESSION_TOKEN_LS_KEY as string);
132+
if (!sessionToken || forceFetch) {
133+
sessionToken = await fetchSessionToken();
134+
}
135+
return sessionToken;
136+
}
137+
```
138+
139+
Defining the `GetCartDocument`
140+
141+
Here's the `GetCartDocument`:
142+
143+
```javascript
144+
import { gql } from '@apollo/client';
145+
146+
export const GetCartDocument = gql`
147+
query {
148+
customer {
149+
sessionToken
150+
}
151+
}
152+
`;
153+
154+
```
155+
156+
It's highly recommended to retrieve the session token outside of the Apollo Client for better control of its value. To ensure no request gets sent without a session token, we must define an error link to capture failed queries caused by an invalid or expired session token, delete the current session, and retrieve a new one. Here's the `createErrorLink` function:
157+
158+
```javascript
159+
import { onError } from '@apollo/client/link/error';
160+
import { Observable } from '@apollo/client/utilities';
161+
162+
function createErrorLink() {
163+
return onError(({ graphQLErrors, operation, forward }) => {
164+
const targetErrors = [
165+
'The iss do not match with this server',
166+
'invalid-secret-key | Expired token',
167+
'invalid-secret-key | Signature verification failed',
168+
'Expired token',
169+
'Wrong number of segments',
170+
];
171+
let observable;
172+
if (graphQLErrors?.length) {
173+
graphQLErrors.map(({ debugMessage, message }) => {
174+
if (targetErrors.includes(message) || targetErrors.includes(debugMessage)) {
175+
observable = new Observable((observer) => {
176+
getSessionToken(true)
177+
.then((sessionToken) => {
178+
operation.setContext(({ headers = {} }) => {
179+
const nextHeaders = headers;
180+
181+
if (sessionToken) {
182+
nextHeaders['woocommerce-session'] = `Session ${sessionToken}`;
183+
} else {
184+
delete nextHeaders['woocommerce-session'];
185+
}
186+
187+
return {
188+
headers: nextHeaders,
189+
};
190+
});
191+
})
192+
.then(() => {
193+
const subscriber = {
194+
next: observer.next.bind(observer),
195+
error: observer.error.bind(observer),
196+
complete: observer.complete.bind(observer),
197+
};
198+
forward(operation).subscribe(subscriber);
199+
})
200+
.catch((error) => {
201+
observer.error(error);
202+
});
203+
});
204+
}
205+
return message;
206+
});
207+
}
208+
return observable;
209+
});
210+
}
211+
```
212+
213+
With the creation of the error link, we now have an Apollo Client that completely manages the WooCommerce session. Note that this doesn't account for all use cases, specifically dealing with registered WooCommerce customers. In such cases, you'll need to use a second JWT for identifying their WordPress account, called an Authentication Token or auth token for short. For handling user authentication, auth tokens, and refresh tokens, refer to the next guide.
214+
215+
This should provide you with a solid foundation for setting up a GraphQL client that effectively manages user sessions in your e-commerce application. By following the steps outlined, you'll be able to create a seamless experience for your users when interacting with both WooCommerce, ultimately saving development time and effort.

0 commit comments

Comments
 (0)