How to implement authentication and paging on top of rom-http?

Hi,

I’m building an adapter for a specific API on top of rom-http. I’ve got it working to the point of making requests, but now I hit the proverbial wall.

The API implements a simple paging mechanism. On > 60 results a __next key is added in with a url to the next page.

This is an example of the returned json:

{
  "d": {
    "__next": "https://start.exactonline.nl/api/v1/123456/cashflow/Receivables?$skiptoken=guid'b74c1ad9-b2bd-4e54-a028-55d66b5063d2'",
    "results": [
      // ... List of up to 60 results
    ]
  }
}

In my ResponseHandler I process and return the contents of [d][results]. What is the best way to implement this paging mechanism in ROM?

My thinking was that I’d set the next page uri in the dataset. Calling dataset.response uses next_page_uri if present or else the standard resource uri.

First I tried adding next_page_uri as on option to Dataset, and set it in the ResponseHandler. The problem is I’d only replace the dataset instance in the ResponseHandler itself and not the dataset instance of the relation.

I could end up with adding an attr_accessor :next_page_uri in Dataset, but I’m not sure this is safe since the dataset seems to be instantiated at boot (I’m using dry-web-roda). I don’t want to accidentaly share the next page uri for different users (not sure if this is the case though).

On top of this the token refresh mechanism I added suffers from the same problem. I’ve added option :access_token to Dataset. When the token is expired the RequestHandler replaces its dataset instance with a new one with the new access_token, but the dataset instance that’s injected in ResponseHandler is not updated and still has the expired token offcourse. Because of this it’s not possible to perform a next_page request in ResponseHandler.

Using attr_accessor instead of option could solve this, permitting me to change the state of the shared dataset instance. But I’d rather avoid changing the state this way, it doesn’t feel very ROM-ish :wink:

I hope this all makes sense, thanks for helping! :slight_smile:

I’ve recently started reworking rom-http adapter by simplifying it and making it use more of the core APIs. This should make it simpler to achieve what you need. Anyhow, it’s not released yet, so for the time being I would suggest trying out latest master and see if you can make it work. Please feel free to ask more questions here.

Hi @solnic, thanks for replying. Actually I was already working with master. Your rework made me delete a few lines of code, cheers!

What I’m still struggling with is how to implement the following flow, what am I missing?

  1. UserRelation fetches all users. The given OAuth2 access token is expired.
  2. MyDefaultRequestHandler fetches a new access token with the given refresh token and performs the request.
  3. The response contains the first 60 records and an url of the next page.
  4. The relation returns the records of the first page but has access to the second page with the updated access token. Something like users.next_page perhaps.

Where and how should I store the updated access token and next page url so it’s available to a Relation? In MyDefaultResponseHandler?

I guess it boils down to this: how do I change the state of the relation or dataset from MyDefaultRequestHandler? Should I even do this?

Hi @solnic, I haven’t been able make this work in a nice way yet :frowning: Additional input -if any- would be much appreciated. Cheers!

@solnic may I ask if there is any improved approach for solving this problem now? Particularly an approach to paging, its not clear to me what the best pattern might be for that from the docs.

I do not know - I’ve actually never used rom-http, I’m just helping with its maintenance.