WP-API / node-wpapi

An isomorphic JavaScript client for the WordPress REST API
http://wp-api.org/node-wpapi/
MIT License
1.68k stars 190 forks source link

Access to params of WPRequest #213

Open edygar opened 8 years ago

edygar commented 8 years ago

Is there anyway to retrieve all params from a built WPRequest?

What I'm looking for is an object like:

{
  "id": 2,
  "parent" : 1
}

Resulting from api.posts().id(1).revisions(2). And an object like:

{
  "id": 2
}

Resulting from api.posts().id(2). But the closer I've got was

function getIndexes(request) {
  /* eslint-disable no-param-reassign */
  // Internal mutations inside reduce function
  const foundIndexers = _reduce(request._path, (indexers, fragment, piece) => {
    const id = _find(request._levels[piece], cmp => cmp.validate(fragment));
    if (!id) return indexers;
    const name = id.component.match(namedGroupRegex);

    if (name) {
      indexers[name[1]] = fragment;
    }

    return indexers;
  }, { ...request._params });

  return foundIndexers;
}

Since I've no access to routesTree, I can't walk and assign the right name to each given _path component.

Thank you

kadamwhite commented 8 years ago

@edygar What is your end goal in getting access to this? The issue is that the route tree built from a handler will not necessarily match with all of the routes that handler can access -- .posts(x).revisions(y) for example will still set "id" as "x" because the least-specific routes take precedence in the tree. If you can share more information about what you're trying to achieve I may be able to suggest a solution.

edygar commented 8 years ago

It's related to the scenario presented here. For now, my middleware receives the WPRequest instance and the method to call:

dispatch(wp(api.posts().id(2).revisions()), 'get')

So its mission is to look in its cache for the requested resource so it can resolve locally or go get it on server. I know the kind of resource by taking only the non-dynamic components URL (example: postsRevisions), but then, I need the indexes of it:

// Fictional Cache Structure
{
  resources: [ { ... }, { ... }, { ... }, { ... }, { ... }],
  indexes: {
    posts: {
      id: {
        1: 3
      },
      slug: {
        'post-slug': 3
      }
    },
    postsRevisions: {
      id: {
        2: 4
      }
    }
  }
}

In this example, all resources have a local id (that refers to its index in resources array), and indexes is a map by kind (in this case, posts and postsRevisions). Inside there is a map by each indexer property (properties which are going to be used to retrieve each resource specifically). Now, I would like that given a WPRequest instance, I could resolve to the properties and kind, like:

const request = api.posts().id(1);
indexes = getIndexes(request); // ===  { id: 2 }

However, the following happens:

indexes = getIndexes(request); // ===  { parent: 2 }

From what I could gather, the WPRequest instance gives the same _levels structure for 2 different endpoints:

api.posts().id(X)._levels == api.posts().id(X).revisions()._levels == {
  "0": [
    {
      "component": "posts",
      "methods": [ ... ] 
    }
  ],
  "1": [
    {
      "component": "(?P<parent>[\\d]+)",
      "methods": [ ... ]
    }
  ],
  "2": [
    {
      "component": "revisions",
      "methods": [ ... ]
    }
  ],
  "3": [
    {
      "component": "(?P<id>[\\d]+)",
      "methods": [ ... ]
    }
  ]
}

Ideally, api.posts().id(X)._levels should give:

{
  "0": [
    {
      "component": "posts",
      "methods": [ ... ] 
    }
  ],
  "1": [
    {
      "component": "(?P<id>[\\d]+)",
      "methods": [ ... ]
    }
  ]
}
kadamwhite commented 8 years ago

To restate to see if I understand: you're looking for a unique cache key for the request? What if you rendered the URI to a string and stored that? Is there a reason that you need the object structure?

I'm hesitant to expose the internal state properties because then we have a new back-compat issue if we change how trees are generated; however WPRequest.prototype._renderURI() is already on track to be exposed. Would that string, or a derivative thereof, be sufficiently unique to be used as your cache key?

edygar commented 8 years ago

To restate to see if I understand: you're looking for a unique cache key for the request? What if you rendered the URI to a string and stored that? Is there a reason that you need the object structure?

No, I didn't represented all the cache up there, but I'm already doing that. I keep in cache requests by its generated URI (they are reorganized to group pagination and to refer to local ids).

However, I want to be able to resolve to single resources that I've already brought in one of the collections endpoints. And I would like to do it from the WPRequest instance, so the consumer mustn't be aware of the cache. If I already have the post {id: 1, slug: 'post-slug'}, I want to resolve to it either from api.posts().id(3) or api.posts().slug('post-slug'), just by looking if any of the indexer properties was filled in WPRequest.

About the tree, I thought _levels property could have the right path 'so far', but I understand the concern of a back-compat issue. What about all setters also be getters?

kadamwhite commented 8 years ago

I'd prefer not to update the setters to be getters, given that would alter the returned type for chaining methods and introduce inconsistency between setters that take arguments, and setters like .embed() that do not. I understand what you're trying to achieve but one of the design goals of this client is to be deliberaly more "dumb" than what you're asking for, in the interest of consistency and internal state management simplicity. Collapsing all routes within a given resource into a single tree was a decision not made lightly, but so far it's paid off with minimal downside (outside of this use-case).

I think that what might suit your needs more would be to wrap the API client in a service that can handle this more complex caching strategy, rather than to rely on injecting an intermediary within node-wpapi. I've used this on two separate projects with two different caching strategies, and both were solved by creating a wp service in our application that exposed more application-specific methods like getPost() or such, and could encapsulate more complex caching behavior within those methods before shelling out to node-wpapi under the hood.

If this isn't a suitable solution to your problem we can talk through whether there's any sort of meta-getter we could create to return the parameters in a specific way; but my first instinct if you're trying to do things like sidestep requests entirely is to create an external wrapper.

edygar commented 8 years ago

Just so you know, I just published the very first version of the lib.

This current issue was poorly addressed here. getIndexes should grab any useful information for finding an entity, my wish as that I could extract the params with the right params name.

kadamwhite commented 8 years ago

@edygar This is awesome, I look forward to digging into it (and hopefully coming up with a solution that works for both of us) in the coming week.