Raynes / tentacles

An Octocat is nothing without his tentacles

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Make `core` more generic so it can be used for other APIs

matthewgertner opened this issue · comments

I'm implementing an app that needs to use REST APIs for a number of services. I'm implementing a connector for Pivotal Tracker right now, and it looks to me like tentacles.core could be a good basis for this. I'd have to make it a bit more generic though (e.g. the API base URL has to be a parameter and not hardcoded, and there needs to be some way to add arbitrary headers).

Would you consider a pull request along these lines?

OK, I'm modifying the code now to support the Pivotal Tracker API. I'll take a crack at folding it back into your code base once I'm done.

Regarding https://github.com/Raynes/tentacles/blob/master/src/tentacles/core.clj#L96, I'm going to pull these headers out of the query map and add a separate headers map since this will be needed anyway to make the code more generic (at least without having the list of special cases in make-request grow arbitrarily long).

I can't see where the current special-case headers are used though. For example, searching for oauth_token doesn't yield anything except the code in core. Am I missing something?

Realized I am missing something: these options will be passed into the options parameter of Tentacles methods. I'm thinking about a way to more clearly separate the Github API options and the "meta-options" like headers, all_pages and clj-http.core/request options.

Wouldn't it be possible to remove all the header and request option stuff (e.g. throw_exceptions) and just use clj-http/client middleware (see https://github.com/dakrone/clj-http/blob/master/src/clj_http/client.clj#L781) to modify the request if necessary?

For my Pivotal Tracker connector I am using the following:

(defn wrap-pivotaltracker
  "Middleware that adds the PT authentication header."
  [client]
  (fn [req]
    (client (assoc-in req [:headers "X-TrackerToken"] pt-token))))

(defn pt-api-call
  ([method end-point] (pt-api-call method end-point nil nil))
  ([method end-point positional] (pt-api-call method end-point positional nil))
  ([method end-point positional query]
    (json/parse-string
      ((http/with-middleware (cons wrap-pivotaltracker http/*current-middleware*)
        (tentacles/with-url url
          (tentacles/raw-api-call method end-point positional query))) :body))))

It seems to work beautifully. The only thing I changed in Tentacles so far was to remove all the special casing of HTTP headers and clj-http.client/request options, which will have to be set using middleware when necessary. Let me know what you think.

@Raynes Just wondering if you had a chance to look at this. Do you disagree with taking advantage of the newer clj-http middleware features rather than special-casing the headers?

If it doesn't make the code substantially more verbose than it already is, I'm all for it. Will the API functions need to change for this? What will they look like?

The API functions don't have to change. The caller just has to use middleware to e.g. set HTTP request headers rather than having those headers special-cased in make-request. It's definitely more verbose (see #57 (comment)) but IMO this is compensated by removing the special casing and having much more power and flexibility (the middleware can change the request in arbitrary ways). Interested to know your view though. If you like the general approach I'll check whether there are any other areas in the code that need to be modified, write some tests and post a final pull request for your review.

One other unrelated question while I have your attention: how would you feel about changing from snake_case to kebab-case in Clojure maps generated from Github JSON? I'm new to the Clojure game but I feel like the JSON mapping has pretty much standardized on kebab-case. The change is trivial (see https://github.com/Raynes/tentacles/pull/58/files#diff-7a0fe2a91eefa14029ec029b1e9f6169L20).

:shipit:

The only reason I'm hesitant to change the underscores is because it negatively impacts performance and doesn't really gain you much of anything besides slight aesthetic improvement. If you want to make it optional, I'm fine with that.

@matthewgertner I'm wondering if a general purpose API wrapper would be better implemented as a different library altogether. What do you think?

If you end up creating a library that can be used by this project then it may be possible to refactor this project to using it. If there are some new, essential steps needed to use your new API wrapper library (like adding middleware), then tentacles can then do this, rather than requiring it from the person calling tentacles.

I think this can be closed. Tentacles is not the perfect example.