Bot releases are hidden (Show)
Published by jasonbahl about 4 years ago
graphql_interface_resolve_type
filter to allow 3rd party plugins the ability to override the ResolveType for an interface. See: #1483. Thanks @kidunot89!post__in
args on custom connections. Closes #1477Published by jasonbahl about 4 years ago
Fixes a mistake from the v0.13.0 release where the built GraphiQL app files had been gitignored and left out of the release 🤦♂️
Before
There's a blank page with a loading message
After
The GraphiQL App loads into the WP Dashboard page
Published by jasonbahl about 4 years ago
This release accomplishes the following:
WPGraphQL now has GraphiQL IDE built in to WPGraphQL!
GraphiQL IDE is an incredibly useful tool for working with GraphQL APIs, and now it's available to all users of WPGraphQL without needing to find and install a separate plugin.
With WPGraphQL Active, there is now an Admin Menu for GraphQL, and within that menu is "GraphiQL IDE" link
There's also an Admin Bar short-link for GraphiQL IDE:
Clicking either link will open GraphiQL in the WP Admin:
You can now browse the WPGraphQL Schema and test Queries and Mutations all from within the WordPress Dashboard.
WPGraphQL now has a settings page where some common configurations can be set, without needing to touch code.
In the Admin Menu, there's now a "GraphQL > Settings" link
Clicking that will take you to a Settings Page:
These settings can be used to accomplish some common tasks with WPGraphQL that previously required code (filters/constants) to accomplish.
/graphql
.Along with the Settings Page, there are some basic APIs to allow 3rd party plugins to easily add GraphQL Settings.
This function should be used within the graphql_register_settings
action, and allows new Settings Sections to be registered to the Settings Page.
usage:
add_action( 'graphql_register_settings', function() {
register_graphql_settings_section( 'graphql_my_section', [
'title' => __( 'My Section', 'your-textdomain' ),
]);
});
This will add a new section to the settings page like so:
This function will register a new settings field to the specified settings section:
usage:
add_action( 'graphql_register_settings', function() {
register_graphql_settings_field( 'graphql_my_section', [
'name' => 'my_field',
'label' => __( 'My Field', 'your-textdomain' ),
'desc' => __( 'Your field description...', 'your-textdomain' ),
'type' => 'text',
'default' => "my default value...",
]);
});
This registers a text field named with the "My Field" label to the "graphql_my_section" section.
This allows multiple settings fields to be registered to a section.
usage:
add_action( 'graphql_register_settings', function() {
register_graphql_settings_section( 'graphql_new_section', [
'title' => __( 'New Section', 'your-textdomain' ),
] );
register_graphql_settings_fields( 'graphql_new_section', [
[
'name' => 'text',
'label' => __( 'Text Input', 'wp-graphql' ),
'desc' => __( 'Text input description', 'wp-graphql' ),
'type' => 'text',
'default' => 'Title'
],
[
'name' => 'textarea',
'label' => __( 'Textarea Input', 'wp-graphql' ),
'desc' => __( 'Textarea description', 'wp-graphql' ),
'type' => 'textarea'
],
[
'name' => 'checkbox',
'label' => __( 'Checkbox', 'wp-graphql' ),
'desc' => __( 'Checkbox Label', 'wp-graphql' ),
'type' => 'checkbox'
],
[
'name' => 'radio',
'label' => __( 'Radio Button', 'wp-graphql' ),
'desc' => __( 'A radio button', 'wp-graphql' ),
'type' => 'radio',
'options' => [
'yes' => 'Yes',
'no' => 'No'
]
],
[
'name' => 'multicheck',
'label' => __( 'Multile checkbox', 'wp-graphql' ),
'desc' => __( 'Multi checkbox description', 'wp-graphql' ),
'type' => 'multicheck',
'options' => [
'one' => 'One',
'two' => 'Two',
'three' => 'Three',
'four' => 'Four'
]
],
[
'name' => 'selectbox',
'label' => __( 'A Dropdown', 'wp-graphql' ),
'desc' => __( 'Dropdown description', 'wp-graphql' ),
'type' => 'select',
'default' => 'no',
'options' => [
'yes' => 'Yes',
'no' => 'No'
]
],
[
'name' => 'password',
'label' => __( 'Password', 'wp-graphql' ),
'desc' => __( 'Password description', 'wp-graphql' ),
'type' => 'password',
'default' => ''
],
[
'name' => 'file',
'label' => __( 'File', 'wp-graphql' ),
'desc' => __( 'File description', 'wp-graphql' ),
'type' => 'file',
'default' => ''
],
[
'name' => 'color',
'label' => __( 'Color', 'wp-graphql' ),
'desc' => __( 'Color description', 'wp-graphql' ),
'type' => 'color',
'default' => ''
],
[
'name' => 'user_role',
'label' => __( 'User Role', 'wp-graphql' ),
'desc' => __( 'Description for selecting a User Role.', 'wp-graphql' ),
'type' => 'user_role_select',
'default' => "administrator",
],
] );
} );
This function should be used to get settings values from GraphQL Settings pages. Each settings section is stored in a serialized array in the options table. This function will properly get the value and return it.
usage:
If you wanted to get the "color" setting from the "graphql_new_section" section, and have it default to "blue" if no value is set, you would do so like:
$option_name = 'color';
$default_value = 'blue';
$section = 'graphql_new_section';
$value = get_graphql_setting( $option_name, $default_value, $section );
We saw that there are settings for Tracing, but what exactly is Tracing?
Tracing is performance tracking for the entire GraphQL request and each field of the request. This data can be helpful when debugging bottlenecks.
With Tracing enabled from the Settings Page, executing a GraphQL Query will show the trace data in the "extensions" portion of the request:
There is data for the entire request:
And data for each field within the request:
This data can be very helpful when debugging bottlenecks.
Let's take a look at an example.
Executing the following query:
{
posts {
nodes {
id
title
}
}
}
We would see the following Trace data for the request:
...
"tracing": {
"version": 1,
"startTime": "1600294443.335",
"endTime": "1600294443.381",
"duration": 45998,
"execution": {
...
The request duration is 45998
microseconds (0.045998
seconds).
And if we scrolled down, we'd see the following trace data for the id
field of the first post:
...,
{
"path": [
"posts",
"nodes",
0,
"title"
],
"parentType": "Post",
"fieldName": "title",
"returnType": "String",
"startOffset": 27518,
"duration": 797
},
...
Here, we can see that the duration was 797
microseconds (0.000797 seconds
)
Let's override this field resolver like so:
add_filter( 'graphql_Post_fields', function( $fields ) {
$fields['title']['resolve'] = function( \WPGraphQL\Model\Post $post ) {
// Sleep for 1 second to simulate something taking a long time
sleep(1);
return $post->titleRendered;
};
return $fields;
});
Now, if we look at the trace data, we can see:
"tracing": {
"version": 1,
"startTime": "1600294618.1841",
"endTime": "1600294628.2435",
"duration": 10059434,
The entire request duration is now 10059434
microseconds ( 10.059434
seconds).
Something is clearly wrong! This request used to take well under 1 second, and now it's taking more than 10 seconds! 😱
If we scroll down we will see that the duration of each title field is looking quite long:
```json
...
{
"path": [
"posts",
"nodes",
0,
"title"
],
"parentType": "Post",
"fieldName": "title",
"returnType": "String",
"startOffset": 27518,
"duration": 1000964
},
...
The duration for the title field is now 1000964
microseconds (1.000964
seconds).
Now, we as developers know what field to start troubleshooting.
In our case we know that the title field had a sleep(1)
in the resolver, so we can remove that and we're back to super speedy execution!
Query Logs are a super helpful debugging tool. A lot of things happen under the hood to resolve a GraphQL Query, and sometimes it can be a mystery.
Enabling Query Logs can give insight into what SQL Queries are being executed, how long they're taking, the raw SQL that is executed and the stacktrace to where in the codebase the query is being executed from.
This can be super helpful when things don't seem to be behaving as you'd expect. Sometimes you get results that you don't expect, and analyzing the raw SQL queries can help identify why things are misbehaving. Sometimes it's a rogue plugin that's filtering WP_Query args too aggressively, or perhaps a GraphQL input argument isn't properly mapping to WP_Query.
In any case, seeing the raw SQL queries and the path to the code that called it can be a huge time saver when debugging.
Published by jasonbahl about 4 years ago
Published by jasonbahl about 4 years ago
Published by jasonbahl about 4 years ago
Published by jasonbahl about 4 years ago
This fixes a bug in the v0.11.0 release by moving the databaseId
field out of the UniformResourceIdentifiable
Interface and into its own DatabaseIdentifier
Interface. This also fixes a case of databaseId
being nullable in one case and causing an invalid Schema error. This PR adds a test to avoid invalid Schema errors in the future.
In many cases, consumer applications won't be affected by this breaking change, but it's a breaking change nonetheless.
The field databaseId
is no longer on the UniformResourceIdentifiable
Interface. This will break any queries that were asking for the databaseId
on the UniformResourceIdentifiable
Interface, like so: ...on UniformResourceIdentifiable { databaseId }
. Instead, these queries should ask for ...on DatabaseIdentifier { databaseId }
.
Published by jasonbahl about 4 years ago
The uri
field on the UniformResourceIdentifiable
Interface has been changed from a NonNull
field to nullable.
This will likely not affect many consumers, but it is a breaking change to the Schema and might cause issues for some consumers.
The reason for the change is because not everything that can have a URI in WordPress is guaranteed to have one. For example Post Types and Taxonomies can have a URI (archive page), but sometimes they don't and we need to provide for that flexibility in the Schema.
fileSize
field on MediaItem
and MediaSize
types (#1350, #1389)location
argument on MenuItems
connection queries was not being respected. (#1402, #1396)where
arguments had been removed from HierarchicalContentNode.children
connections (#1405, #1406)set_query_arg()
in the docs, remove double background in <pre>
blocks and add pair-programming sessions to connections guide. Thanks @jacobarriola!pageBy
example from the docs. Thanks @MarcelloTheArcane!get_allowed_post_types()
instead of $allowed_post_types
. Thanks @sboerrigter!Published by jasonbahl over 4 years ago
graphql_after_resolve_field
hook (#1153 #1369)isPostsPage
field to the Page type (#1370)/
as the path not returning the homepage (#1352 #1366)loginIn
and loginNotIn
argument fields for user connections (#1174 #1368)Published by jasonbahl over 4 years ago
Published by jasonbahl over 4 years ago
Published by jasonbahl over 4 years ago
This is quite possibly the most substantial release since the plugins inception.
Below is a break down of everything that's happening in this release i hopefully make it easier to update from previous versions if you come across anything that's breaking your applications.
children
instead of child{$postType}
.
shouldOnlyIncludeConnectedItems
and shouldOutputInFlatList
arguments for hierarchical term queries. The default now is a flat list and that can be overridden by passing an explicit parent in the where args.post.author
and post.featuredImage
now return an edge/node instead of the node directly.
Comment.author
and Comment.parent
now return an edge/node, instead of the node directly. Comment.author
nods is of type Commenter
Interface instead of CommentAuthorUnion
NodeWithFeaturedImage->MediaItem
fields are now one-to-one connections returning an edge with the nodeComment.commentedOn
field is now a formal one-to-one connection returning an edge/node where the node is a ContentNode
InterfaceContentNode.revisionOf
is now a formal one-to-one connection returning an edge/node where the node is a ContentNode
interfaceHierarchicalContentNode.parent
is now a formal one-to-one connection returning an edge/node where the node is of ContentNode
InterfacePostObject.author
field is now formal one-to-one connection between a Post and the Author, returning an edge/node where the Node is of type User.ContentNode.terms
field a formal connection to the TermNode
type. Previously was a listOf
.Comment.children
field was changed to Comment.replies
PostObject.terms
, PostObject.termNames
, PostObject.termSlugs
fields in favor of formal connectionsTaxonomy.connectedPostTypeNames
in favor of Taxonomy.connectedPostTypes
connection.Taxonomy.connectedPostTypes
to formal connection instead of listOf
.authorId
on Post model to return global ID. (Adds new authorDatabaseId
field to get the Integer ID of the author.)Post.featuredImageId
on Post Model to Global Relay ID (add Post.featuredImageDatabaseId
field to get the Integer ID of the featured image)parent=>0
logic from connections. For the cases where top-level items should be queried, parent=>0
can be used as an argument, but the default will be a flat-list of connected itemsPostObject->Comments
connections when queried from revision nodes (will resolve using theasPreview
argument to singular RootQuery
entry points for Post objects. This allows for the Preview node to be returned instead of the published/draft node.graphql_resolve_revision_meta_from_parent
to allow certain meta fields (ACF, for example) to bypass using the parent node meta for resolution and use the revision node's metaasPreview
argument to single post nodes.Post.isRevision
field to Post modelContentNode.isPreview
, ContentNode.previewRevisionDatabaseId
and ContentNode.previewRevisionId
fields to ContentNode
interfaceUser
Type gets a connection registered to it automatically.
oneToOne => true
when registering a connection$resolver->oneToOne()->get_connection()
Commenter
Interface. An Interface shared by Commenters (CommentAuthor and User) when querying for the author of a CommentTaxonomy.connectedContentTypes
field as a formal connection from Taxonomy->ContentType
ContentType.contentNodes
field that returns a connection of content nodes of that post typeAbstractConnectionResolver->setQueryArg
for ->set_query_arg
. And set_query_arg() now
returns instance of the resolver to allow for chaining.databaseId
field to Comment
typeauthorDatabaseId
field to get the Integer ID of the author.Post.featuredImageDatabaseId
field to get the Integer ID of the featured imageHierarchicalTermNode
InterfaceHierarchicalTermNode
to Terms of hierarchical taxonomies (Categories, custom hierarchical taxonomy types)Commenter
Interface to the User
TypeUser.userId
in favor of user.databaseId
location
field to menuItems
as listOf MenuItemLocation (read more)location
field to menus
as listOf MenuItemLocation? (read more)moderate_comments
capability. closes #1287 (see details)parent=>0
logic from connections. For the cases where top-level items should be queried, parent=>0
can be used as an argument, but the default will be a flat-list of connected itemsRootQuery.menuItems
field. (read more)generalSettings.url
on Multisite endpoints (see: #1334)Published by jasonbahl over 4 years ago
$_SERVER['HTTP_HOST']
but providing a filter graphql_is_graphql_http_request
filter so servers with unique setups can define their own way to determine if a request is indeed a GraphQL request.Published by jasonbahl over 4 years ago
I'm listing breaking changes under "Consumer" and "Server" breaking changes.
These are changes to the Schema or how GraphQL acts for consumers that may break consuming applications, depending on how the consuming application interacts with WPGraphQL.
ContentType.connectedTaxonomies
field from a list to a formal connectionContentType.connectedTaxonomyNames
fieldThese changes are to internals of the codebase that might affect plugins that extend WPGraphQL on the server.
register_graphql_scalar()
to be more consistent with other register_graphql_*
methods. Before: register_graphql_scalar( $config )
, Now: register_graphql_scalar( $type_name, $config );
(@pristas-peter, I know you're using this method)Post.parentId
in the Post Model is now the global ID and Post.parentDatabaseId
is now the database ID of the parentTerm.parentId
in the Term Model is now the global ID and Term.parentDatabaseId
is now the database ID of the parentgraphql_register_types
hook moved higher in the init_type_registry()
method. Unlikely to affect extensions, but possible thing to debug if you run into issues.$_SERVER['HTTP_HOST']
to $_SERVER['SERVER_NAME']
to allow the server to define the server name instead of the client. Thanks @WybrenKoelmans!Theme.version
field from type Float
to String
graphql_format_type_name()
access functionregister_graphql_interfaces_to_types()
method, allowing interfaces to be registered by extensions and applied to existing registered typesPost.enqueuedScriptsQueue
and Post.enqueuedStylesheetsQueue
fields to the Post ModelTerm.enqueuedScriptsQueue
and Term.enqueuedStylesheetsQueue
fields to the Post ModelUser.enqueuedScriptsQueue
and User.enqueuedStylesheetsQueue
fields to the Post ModelEnqueuedScript
and EnqueuedStylesheet
type and EnqueuedAsset
InterfacePublished by jasonbahl over 4 years ago
n/a. Please open an issue if you discover otherwise.
register_graphql_scalar
API (see: #1271)graphql_connection_amount_requested
filter. Thanks @esamattis!Published by jasonbahl over 4 years ago
Published by jasonbahl over 4 years ago
Possible Breaking Change: Fields returning a uri have been adjusted to include a leading slash. Depending on your use case, this might cause unexpected behavior.
For example, if you queried:
{
post(id: "hello-world", idType: URI) {
id
title
uri
}
}
OLD RESPONSE:
{
"data": {
"post": {
"id": "cG9zdDox",
"title": "Hello world!",
"uri": "hello-world/" # note the missing leading slash
}
}
}
NEW RESPONSE:
{
"data": {
"post": {
"id": "cG9zdDox",
"title": "Hello world!",
"uri": "/hello-world/" # note the leading slash
}
}
}
Published by jasonbahl over 4 years ago
n/a
@access
tag. Thanks @izzygld!Published by jasonbahl over 4 years ago
This release focuses on adjusting how Nodes are resolved to prevent errors in cases where the nodes are determined to be considered private and non-viewable by the requesting user. (#1138)
More details below:
Schema Breaking changes are changes to the shape of the Schema that would require clients interacting with WPGraphQL to update to remain compatible.
n/a: The shape of the Schema remains unchanged in this release. Clients shouldn't need to adjust their queries/mutations to remain compatible with this release.
ancestors
field removed from Post Type Objects. Possibly will add back in the future as a formal connection.
Internal Breaking Changes are changes to internals that might require plugins that extend WPGraphQL to change in order to remain compatible.
ContentTypeConnectionResolver
, TaxonomyConnectionResolver
and UserRoleConnectionResolver
to not extend the AbstractConnectionResolver class, but instead make use of the Relay::connectionFromArray()
methodresolveNode
config arg from most connections registered by the core plugin as the new method for resolving connections doesn't wait until the last second to resolve nodes
WPGraphQL makes use of a concept called (Deferred Resolution)[https://github.com/wp-graphql/wp-graphql/pull/722#issue-261315185], to ensure that database queries are executed as efficiently as possible.
WPGraphQL was deferring the resolution of nodes too late, causing errors when Nodes were determined to be private after being loaded.
Take the following query for example :
{
posts {
nodes {
id
databaseId
title
}
}
}
This query might return a payload like so:
{
"data": {
"posts": {
"nodes": [
{
"id": "cG9zdDoyNDI=",
"databaseId": 242,
"title": "Test Post"
},
{
"id": "cG9zdDox",
"databaseId": 1,
"title": "Hello world!"
}
]
}
},
}
Looks great! Just what we'd expect (assuming the site had only 2 posts).
Well, let's say we had a membership plugin installed (or something similar) that used meta to determine whether a Post should be considered private or not. And let's say that Post 242
was set to private and should not be returned to a public user.
Because of how the Deferred resolution was working (before this release), the Post would have been resolved too late to be stripped out of the results, and would return a null
within the list of Post nodes, and would include an error like so:
{
"errors": [
{
"debugMessage": "Cannot return null for non-nullable field Post.id",
...
}
],
"data": {
"posts": {
"nodes": [
null,
{
"id": "cG9zdDox",
"databaseId": 1,
"title": "Hello world!"
}
]
}
},
}
This behavior is problematic.
First, it throws errors, when there really isn't an error. Nothing has actually gone wrong. The user is asking for Posts, and GraphQL should be able to return posts without error.
If a Post is private, it shouldn't be exposed at all. It should be as if it doesn't even exist in the system. Be returning a null value, we are exposing that something is there behind the scenes.
The correct behavior should be to return a list of Posts, and no errors returned. If a Post is private, it should simply not be included in the list at all.
This release fixes this issue by changing how the Deferred resolution of nodes happens.
Given the query above, resolution used to work like so:
Because of the late resolution of the Node, this was causing the Cannot return null for non-nullable field Post.id
error. There's no way to strip a private node out of the list of returned nodes if we're resolving nodes at the last possible tree in the Graph.
This pull request changes the behavior to resolve the nodes earlier.
Given the query above, resolution now works like so:
Cannot return null for non-nullable field Post.id
will be returned.To accomplish this, some changes to the ConnectionResolver classes were made.
Now, Nodes are resolved a step earlier, and the resolved nodes are now passed from the Connection down to Edges/Nodes.
Edges/Nodes now have the full nodes as context in their resolvers, instead of just an ID.
This can be HUGE when needing to add edge data to connections, where before an entity ID was the only context provided, and that can be too little information to be useful.
You can read more about a concrete case where the functionality was problematic, and how this release fixes it here: https://github.com/wp-graphql/wp-graphql/issues/1138#issuecomment-580269285
Below is a list of changes the AbstractConnectionResolver Class. If your Plugin extends this class, the below information should help with upgrading.
abstract public function get_loader_name()
abstract public function get_ids()
get_items()
methodabstract public function get_node_by_id()
get_amount_requested()
, get_offset()
, get_query_amount()
, has_next_page()
, has_previous_page()
, get_start_cursor()
, get_end_cursor()
, get_nodes()
, get_cursor_for_node()
, get_edges()
, get_page_info()
Published by jasonbahl over 4 years ago