Skip to content

Conversation

@kadamwhite
Copy link
Collaborator

This is an alternative philosophy than the approach in #144, whereby instead of manually configuring a route, the client auto-discovers all possible endpoints and dynamically generates handlers from the route and schema data. This should be considered heavily WIP, but after discussions with @jmeas and @adamsilverstein I have a hunch that dynamically creating a handler by parsing the route definition strings will be a more future-friendly way to create a dynamic route generation method. This PR iterates on this idea with an eye towards replicating all of the internal path-specific methods (.posts().id( # ) for example) dynamically by consuming and parsing the schema data.

image

So far, we generate a tree representing all routes grouped under a common resource base (posts, in the selection above). We next need to walk that tree to deduce the setter methods needed to specify each level of the route.

"/wp/v2/pages": {},
"/wp/v2/pages/(?P<id>[\\d]+)": {},
"/wp/v2/pages/(?P<parent>[\\d]+)/revisions": {},
"/wp/v2/pages/(?P<parent>[\\d]+)/revisions/(?P<id>[\\d]+)": {},
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This shows an example of what the route definitions look like—currently, I parse out the named capture groups from the regexes and use them to construct a representation of each "level" of the tree, where a "level" is one segment of a URL between /s.

Here's what the generated tree looks like for the posts routes defined here:

image

"dynamic" and "level" are inferred while walking the tree; validate methods are generated based on whether a given input matches the regex defined in a named capture group. "names" represents the list of names given to the same capture group in different routes under the same root resource type; the name could (and hopefully will) be used to auto-generate chaining methods to populate that segment of the URL.

kadamwhite added a commit that referenced this pull request Feb 16, 2016
This is the first step of an overall architectural change with two goals:
decrease code duplication between endpoints, and enable filtering functionality
to be applied to programmatically-generated endpoint handlers (see #145)
@kadamwhite kadamwhite mentioned this pull request Mar 14, 2016
kadamwhite added a commit that referenced this pull request May 17, 2016
Comments are not currently supported as a top-level endpoint.

This adds a basic `/comments` endpoint handler, which can

- Retrieve a paged collection of comments
- Retrieve a specific comment by ID
- Retrieve comments by their parent post or page via the `.forPost()`
  query parameter method

Unit tests and integration tests have been added to validate the
enumerated behaviors of the endpoint handler work as expected.

Further [comment query parameters](http://v2.wp-api.org/reference/comments/)
will follow in subsequent PRs, possibly following #145.
kadamwhite added a commit that referenced this pull request Jun 4, 2016
This is the first step of an overall architectural change with two goals:
decrease code duplication between endpoints, and enable filtering functionality
to be applied to programmatically-generated endpoint handlers (see #145)
kadamwhite added a commit that referenced this pull request Jun 4, 2016
This PR extracts the filtering and query parameter methods into mixin
functions that can be added to a constructor to grant it the specified
methods. In addition to this significant (architectural) change, filters
and parameter setters no longer have implicit interdependencies: with
the exception of .param itself, which is a part of the base WP-Request
class, all filter methods depend on each other directly where needed so
that any filter can be included into a subclass with no restrictions.

The goal of this patch is to

1. decrease code duplication between endpoints
2. better surface and test edge cases in parameter and filter methods
3. simplify the ability to add filtering functionality to programatically-
   generated endpoint handlers (see #145)

To accomplish these goals I

- moved CollectionRequest filter methods into a filter mixin module
- moved CollectionRequest parameter methods into a parameter mixin module
- moved filter and parameter mixins from other endpoints (page, taxonomies,
  comments) into the filter and parameter mixin modules as appropriate
- unit tested the new filter and parameter mixin modules
- adjusted how `.filter` and `.param` function to address edge cases
  exposed during testing, such as unexpected behavior when calling
  `.filter` or `.param` with no second argument.
- allowed filters to initialize their own private state objects (the
  `this._filters` and `this._taxonomyFilters` objects), so that they
  can be included into a prototype without any further pre-work required
  to make them function with that constructor
- removed all existing explicit definition of `this._filters` and
  `this._taxonomyFilters` objects in endpoint request handler constructors,
  since those objects will now be created only when needed

There is currently some duplicate testing logic between the mixin files
and the request handler tests themselves; this is not seen as a detriment
but since the mixins are fully tested the assertions in the constructor
test suites may eventually be replaced with tests that verify the mixins
have been applied properly.
@kadamwhite kadamwhite force-pushed the parse-route-strings branch from c0915f1 to d77c507 Compare June 6, 2016 04:22
@jasonphillips
Copy link
Contributor

This is an excellent idea and something I'm anxiously anticipating. I've lately been building a newer rendition of my falcor-wordpress library (which currently depends on your client) that is meant to keep in sync with the latest releases of WP-API, so I have automated tests that run against the public demo.wp-api.org endpoint. And as you've also discovered, the API's endpoints undergo continuous changes with new releases, one of the most recent being a complete restructuring of all the taxonomy term paths. So building the client's paths directly off the scheme of a specified endpoint is highly desirable.

@kadamwhite kadamwhite force-pushed the parse-route-strings branch from 0440e53 to f62246e Compare June 13, 2016 09:35
@kadamwhite kadamwhite force-pushed the parse-route-strings branch from 667de63 to aba794e Compare June 14, 2016 11:45
@kadamwhite kadamwhite force-pushed the parse-route-strings branch from aba794e to 5533e3e Compare June 14, 2016 11:45
@kadamwhite kadamwhite changed the title WIP: Dynamically generate endpoint handlers based on schema Dynamically generate endpoint handlers based on schema Jun 14, 2016
@kadamwhite
Copy link
Collaborator Author

@jasonphillips This is ready for review. At present there is no functionality exposed to dynamically generate a handler from your own site, but this includes all the plumbing necessary to do so.

@jasonphillips
Copy link
Contributor

jasonphillips commented Jun 14, 2016

That's great, I'll look forward to diving into this. Basically, to answer your prior question, my Falcor router was sitting atop your library as its simpler way into the Wordpress API, but just building out the kind of single-model routing logic needed by Falcor on top of that.

So, a Falcor route (here for taxonomy terms) calls my handler classes (base class expects promises from your library in each overridden case) to fetch from the API and restructure the response for Falcor's world, following deeper nested paths in same flight (ie, fetch a post and descend into its connected terms and their properties as well).

I have been setting this up to use the live demo.wp-api.org (which always has latest dev code) in my automated tests, but I realized recently that keeping pace with the moving paths of the in-development WP-API is a real challenge. I was about to start down the road you have (parsing the list of endpoints offered by a live WP-API) and then noticed that you've already done the work.

From this point I'll probably need to decided whether to keep using your library as a whole to communicate with the API or whether to just take your completed work on parsing the available REST paths / variables and use those directly. This is great though, to have someone push forward in making sense of all the available endpoints so that they can be chained and used logically.

@kadamwhite
Copy link
Collaborator Author

@jasonphillips Thanks for the comment. I haven't used Falcor, so forgive me the confusion: this is a purely JS-side compatibility layer that digests WP API response data and reformulates it into a new structure?

From this point I'll probably need to decided whether to keep using your library as a whole to communicate with the API or whether to just take your completed work on parsing the available REST paths / variables and use those directly.

I am not clear on the issue; I had thought that your code would use my library to get the data from WP, then would transform it in some fashion once responded, but I may be misinterpreting because I'm now wondering whether you mean that you were hoping to point the wordpress-rest-api client at endpoints defined in JS, not in PHP.

Any clarification would be appreciated!

@kadamwhite
Copy link
Collaborator Author

Also it's important to note that given the looming WCEU deadline, the functionality to actually bind a wp rest api client instance to an arbitrary API endpoint with arbitrary first- or third-party routes is a this-week thing, not an another-four-months thing :)

@kadamwhite
Copy link
Collaborator Author

Basic overview of this patch:

  • Include the JSON response of the /wp-json in the library
  • Rather than manually specifying each endpoint handler (e.g. define a PostsRequest class, then define wp.posts() factory), deduce handlers for all available endpoints from the routes in the /wp-json response object
  • Handle application of parameter and filter convenience methods as mixins applied based on endpoint, not base functionality of CollectionsRequest
  • Remove CollectionsRequest entirely in favor of WPRequest and mixins

@jasonphillips
Copy link
Contributor

jasonphillips commented Jun 14, 2016

I haven't used Falcor, so forgive me the confusion: this is a purely JS-side compatibility layer that digests WP API response data and reformulates it into a new structure?

My apologies; yes Falcor would typically be run in a node.js process on the server, so that your front-end hits your Falcor instance with data requests and Falcor, in turn, fetches all the data from the third-party service (in this case, from a Wordpress REST API using your client) then restructures and returns it in one batch to front-end client.

The main advantage is the ability to express deep paths and complex sets of items to retrieve in one request, then have all those parsed out, fetched, and packaged into a single response by the node.js backend (which might be making any number of calls to the REST API in the background to fulfill those data needs, or can set a built-in cache timeout for the last time it retrieved a particular item, etc).

So it helps overcome the main drawback of REST for front-end use, which is that one would often need to make 5 or 10 requests to get all the information needed on a single page (taxonomies, recent posts, a particular full post, its children, its related items, etc). With Falcor all that can be expressed as a set of paths on a global model (postsById[15,39,55]['title','id','link'] etc) which is structured as graph data, ie. full of internal links (so recentPosts[2] actually references postsById[39], and recentPosts[2].terms.tags[0] might reference taxonomies.tags.termsBySlug['something'], etcetera). I'm using this stack with React, and I can easily decorate the data needs of components all over a page, but have them all fetched in one flight with no data repetition in the structure.

I am not clear on the issue; I had thought that your code would use my library to get the data from WP, then would transform it in some fashion once responded, but I may be misinterpreting because I'm now wondering whether you mean that you were hoping to point the wordpress-rest-api client at endpoints defined in JS, not in PHP.

Your initial assumption is correct. I was floating the question as to whether I should depend on your library or just step up and format my own completed requests to the REST server using some of the path parsing you've worked out. But I think the former is still where I'm headed. I look forward to finding some time perhaps today to try your latest code out and see where it leads me.

- All route handler tests now go in route-handlers/, since they are not
  testing code from lib/
- All key files in the handler generation flow have been moved out of
  util/ and up into lib/, since they are now the core functionality of
  the library
- All files in lib/* root have been renamed to remove verbs, exposing
  the operative methods as named exports
- WPRequest folder has been renamed to constructors/
@kadamwhite kadamwhite merged commit c1a2d49 into master Jun 15, 2016
@kadamwhite kadamwhite deleted the parse-route-strings branch June 15, 2016 01:41
kadamwhite added a commit that referenced this pull request Aug 10, 2016
There's a wealth of content in the readme, and an opportunity to write
additional user guides. However, the documentation site currently features
only the generated API docs, and ever since #145 the generated docs are
not as robust as they were before. To better highlight the documentation
contained within the REAMDE, in this PR we

- Add grunt task to zip built browser bundles (for #196)
- Implement a task to generate jekyll-ready pages from README
- Generate a documentation index with deep links to the subheds of
  content extracted from the README file
- Update CONTRIBUTING and README files to generate better output
- Add a script to print out tasks necessary to release docs branch
- Add a docs site favicon and add IE fallback favicon link
- Add a convenience package script to run jekyll in dev mode
- Add a grunt task to clean leftover files after docs release
- Reorganize Grunt tasks into modules within `build/grunt/`
- Lint build and bin directories, with support for es2015 syntax
- Support arbitrary host when running jekyll in local dev environment

There's a lot that could be done to improve the generated documentation, but as-is this closes #196

Closes #205 (this is a squashed PR)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants