Skip to content

Commit

Permalink
feat: Docs Restored. Unsupported product type setting implemented. (#731
Browse files Browse the repository at this point in the history
)

* 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
  • Loading branch information
kidunot89 committed Apr 19, 2023
1 parent b2d6608 commit f4b153f
Show file tree
Hide file tree
Showing 33 changed files with 2,723 additions and 118 deletions.
45 changes: 45 additions & 0 deletions access-functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,51 @@ function wc_graphql_camel_case_to_underscore( $string ) {
}
}//end if

/**
* Plugin global functions.
*
* @package Axis\Plugin_Distributor
*/

if ( ! function_exists( 'woographql_setting' ) ) :
/**
* Get an option value from WooGraphQL settings
*
* @param string $option_name The key of the option to return.
* @param mixed $default The default value the setting should return if no value is set.
* @param string $section_name The settings section name.
*
* @return mixed|string|int|boolean
*/
function woographql_setting( string $option_name, $default = '', $section_name = 'woographql_settings' ) {
$section_fields = get_option( $section_name );

/**
* Filter the section fields
*
* @param array $section_fields The values of the fields stored for the section
* @param string $section_name The name of the section
* @param mixed $default The default value for the option being retrieved
*/
$section_fields = apply_filters( 'woographql_settings_section_fields', $section_fields, $section_name, $default );

/**
* Get the value from the stored data, or return the default
*/
$value = isset( $section_fields[ $option_name ] ) ? $section_fields[ $option_name ] : $default;

/**
* Filter the value before returning it
*
* @param mixed $value The value of the field
* @param mixed $default The default value if there is no value set
* @param string $option_name The name of the option
* @param array $section_fields The setting values within the section
* @param string $section_name The name of the section the setting belongs to
*/
return apply_filters( 'woographql_settings_section_field_value', $value, $default, $option_name, $section_fields, $section_name );
}
endif;



Expand Down
2 changes: 1 addition & 1 deletion bin/_lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ install_wordpress() {

# Install Wordpress + integrated plugins for testing/development.
composer install
composer require --dev -W \
composer require --dev --no-interaction -W \
johnpbloch/wordpress:* \
wp-graphql/wp-graphql-jwt-authentication \
wpackagist-plugin/woocommerce \
Expand Down
215 changes: 215 additions & 0 deletions docs/configuring-graphql-client-for-user-session.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
# Configuring a GraphQL Client for WooCommerce User Session Management

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.

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!

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.

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`.

```javascript
fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'woocommerce-session': `Session ${sessionToken}`,
},
body: JSON.stringify({
query: `
query {
cart {
contents {
nodes {
key
product {
node {
id
name
}
}
quantity
subtotal
}
}
}
}
`,
}),
})
.then((response) => response.json())
.then((data) => console.log(data));
```

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.

## Creating the Apollo Client instance

First, let's create the Apollo Client instance and focus on the `link` option. We'll use the `from` utility function from `@apollo/client`:

```javascript
import { from } from '@apollo/client';

// ...

const client = new ApolloClient({
link: from([
createSessionLink(),
createErrorLink(),
new HttpLink({ uri: endpoint }),
]),
cache: new InMemoryCache(),
});
```

## Defining the `createSessionLink` function

Next, define the `createSessionLink` function as follows:

```javascript
import { setContext } from '@apollo/client/link/context';

function createSessionLink() {
return setContext(async (operation) => {
const headers = {};
const sessionToken = await getSessionToken();

if (sessionToken) {
headers['woocommerce-session'] = `Session ${sessionToken}`;

return { headers };
}

return {};
});
}
```

## About the environment variables

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.

Here's a sample .env file to help you get started:

```makefile
SESSION_TOKEN_LS_KEY=my_session_token
REFRESH_TOKEN_LS_KEY=my_refresh_token
AUTH_TOKEN_LS_KEY=my_auth_token
AUTH_KEY_TIMEOUT=30000
GRAPHQL_ENDPOINT=http://woographql.local/graphql
```

## Defining the `getSessionToken` and `fetchSessionToken` functions

Here are the `getSessionToken` and `fetchSessionToken` functions:

```javascript
import { GraphQLClient } from 'graphql-request';

// Session Token Management.
async function fetchSessionToken() {
let sessionToken;
try {
const graphQLClient = new GraphQLClient(process.env.GRAPHQL_ENDPOINT);

const cartData = await graphQLClient.request(GetCartDocument);

// If user doesn't have an account return accountNeeded flag.
sessionToken = cartData?.cart?.sessionToken;

if (!sessionToken) {
throw new Error('Failed to retrieve a new session token');
}
} catch (err) {
console.error(err);
}

return sessionToken;
}

export async function getSessionToken(forceFetch = false) {
let sessionToken = localStorage.getItem(process.env.SESSION_TOKEN_LS_KEY as string);
if (!sessionToken || forceFetch) {
sessionToken = await fetchSessionToken();
}
return sessionToken;
}
```
Defining the `GetCartDocument`
Here's the `GetCartDocument`:
```javascript
import { gql } from '@apollo/client';

export const GetCartDocument = gql`
query {
customer {
sessionToken
}
}
`;

```
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:
```javascript
import { onError } from '@apollo/client/link/error';
import { Observable } from '@apollo/client/utilities';

function createErrorLink() {
return onError(({ graphQLErrors, operation, forward }) => {
const targetErrors = [
'The iss do not match with this server',
'invalid-secret-key | Expired token',
'invalid-secret-key | Signature verification failed',
'Expired token',
'Wrong number of segments',
];
let observable;
if (graphQLErrors?.length) {
graphQLErrors.map(({ debugMessage, message }) => {
if (targetErrors.includes(message) || targetErrors.includes(debugMessage)) {
observable = new Observable((observer) => {
getSessionToken(true)
.then((sessionToken) => {
operation.setContext(({ headers = {} }) => {
const nextHeaders = headers;

if (sessionToken) {
nextHeaders['woocommerce-session'] = `Session ${sessionToken}`;
} else {
delete nextHeaders['woocommerce-session'];
}

return {
headers: nextHeaders,
};
});
})
.then(() => {
const subscriber = {
next: observer.next.bind(observer),
error: observer.error.bind(observer),
complete: observer.complete.bind(observer),
};
forward(operation).subscribe(subscriber);
})
.catch((error) => {
observer.error(error);
});
});
}
return message;
});
}
return observable;
});
}
```
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.
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 comments on commit f4b153f

Please sign in to comment.