From 301328e17390d42182d4a50c4f50b741f436b024 Mon Sep 17 00:00:00 2001 From: icp Date: Thu, 4 Apr 2024 21:14:57 +0530 Subject: [PATCH] Oauth token interceptor (#50) * Format * Add Oauth token interceptor * API.md --- API.md | 68 +++++++++++++++-------- CHANGELOG.md | 4 ++ README.md | 8 +++ src/babashka/http_client.clj | 1 + src/babashka/http_client/interceptors.clj | 17 +++++- test/babashka/http_client_test.clj | 9 ++- 6 files changed, 81 insertions(+), 26 deletions(-) diff --git a/API.md b/API.md index b78b438..c0fdcc5 100644 --- a/API.md +++ b/API.md @@ -24,9 +24,11 @@ - [`default-interceptors`](#babashka.http-client.interceptors/default-interceptors) - Default interceptor chain. - [`form-params`](#babashka.http-client.interceptors/form-params) - Request: encodes :form-params map and adds :body. - [`multipart`](#babashka.http-client.interceptors/multipart) - Adds appropriate body and header if making a multipart request. + - [`oauth-token`](#babashka.http-client.interceptors/oauth-token) - Request: adds :authorization header based on :oauth-token (a string token) in request. - [`query-params`](#babashka.http-client.interceptors/query-params) - Request: encodes :query-params map and appends to :uri. - [`throw-on-exceptional-status-code`](#babashka.http-client.interceptors/throw-on-exceptional-status-code) - Response: throw on exceptional status codes. - [`unexceptional-statuses`](#babashka.http-client.interceptors/unexceptional-statuses) + - [`uri-with-query`](#babashka.http-client.interceptors/uri-with-query) - We can't use the URI constructor because it encodes all arguments for us. - [`babashka.http-client.websocket`](#babashka.http-client.websocket) - Code is very much based on hato's websocket code. - [`abort!`](#babashka.http-client.websocket/abort!) - Closes this WebSocket's input and output abruptly. - [`close!`](#babashka.http-client.websocket/close!) - Initiates an orderly closure of this WebSocket's output by sending a Close message with the given status code and the reason. @@ -64,9 +66,9 @@ Constructs a `java.net.Authenticator`. ``` Constructs a `java.net.CookieHandler` using `java.net.CookieManager`. - + Options: - + * `:store` - an optional `java.net.CookieStore` implementation * `:policy` - a `java.net.CookiePolicy` or one of `:accept-all`, `:accept-none`, `:original-server`

Source

@@ -78,9 +80,9 @@ Constructs a `java.net.CookieHandler` using `java.net.CookieManager`. ``` Constructs a `java.util.concurrent.Executor`. - + Options: - + * `:threads` - constructs a `ThreadPoolExecutor` with the specified number of threads

Source

@@ -126,9 +128,9 @@ Constructs a `javax.net.ssl.SSLContext`. ``` Constructs a `javax.net.ssl.SSLParameters`. - + Options: - + * `:ciphers` - a list of cipher suite names * `:protocols` - a list of protocol names

Source

@@ -178,7 +180,7 @@ Options used to create the (implicit) default client. ``` Convenience wrapper for [`request`](#babashka.http-client/request) with method `:delete` -

Source

+

Source

## `get` ``` clojure @@ -188,7 +190,7 @@ Convenience wrapper for [`request`](#babashka.http-client/request) with method ` ``` Convenience wrapper for [`request`](#babashka.http-client/request) with method `:get` -

Source

+

Source

## `head` ``` clojure @@ -198,7 +200,7 @@ Convenience wrapper for [`request`](#babashka.http-client/request) with method ` ``` Convenience wrapper for [`request`](#babashka.http-client/request) with method `:head` -

Source

+

Source

## `patch` ``` clojure @@ -208,7 +210,7 @@ Convenience wrapper for [`request`](#babashka.http-client/request) with method ` ``` Convenience wrapper for [`request`](#babashka.http-client/request) with method `:patch` -

Source

+

Source

## `post` ``` clojure @@ -218,7 +220,7 @@ Convenience wrapper for [`request`](#babashka.http-client/request) with method ` ``` Convenience wrapper for [`request`](#babashka.http-client/request) with method `:post` -

Source

+

Source

## `put` ``` clojure @@ -228,7 +230,7 @@ Convenience wrapper for [`request`](#babashka.http-client/request) with method ` ``` Convenience wrapper for [`request`](#babashka.http-client/request) with method `:put` -

Source

+

Source

## `request` ``` clojure @@ -250,13 +252,14 @@ Perform request. Returns map with at least `:body`, `:status` * `:form-params` - a map of form params to send in the request body. * `:body` - a file, inputstream or string to send as the request body. * `:basic-auth` - a sequence of `user` `password` or map with `:user` `:pass` used for basic auth. + * `:oauth-token` - a string token used for bearer auth. * `:async` - perform request asynchronously. The response will be a `CompletableFuture` of the response map. * `:async-then` - a function that is called on the async result if successful * `:async-catch` - a function that is called on the async result if exceptional * `:timeout` - request timeout in milliseconds. * `:version` - the HTTP version: `:http1.1` or `:http2`. -

Source

+

Source

----- # babashka.http-client.interceptors @@ -272,7 +275,7 @@ Perform request. Returns map with at least `:body`, `:status` Request: adds `:accept` header. Only supported value is `:json`. -

Source

+

Source

## `basic-auth` @@ -289,7 +292,7 @@ Request: adds `:authorization` header based on `:basic-auth` (a map Request: construct uri from map -

Source

+

Source

## `decode-body` @@ -297,7 +300,7 @@ Request: construct uri from map Response: based on the value of `:as` in request, decodes as `:string`, `:stream` or `:bytes`. Defaults to `:string`. -

Source

+

Source

## `decompress-body` @@ -305,7 +308,7 @@ Response: based on the value of `:as` in request, decodes as `:string`, `:stream Response: decompresses body based on "content-encoding" header. Valid values: `gzip` and `deflate`. -

Source

+

Source

## `default-interceptors` @@ -313,7 +316,7 @@ Response: decompresses body based on "content-encoding" header. Valid values: ` Default interceptor chain. Interceptors are called in order for request and in reverse order for response. -

Source

+

Source

## `form-params` @@ -321,7 +324,7 @@ Default interceptor chain. Interceptors are called in order for request and in r Request: encodes `:form-params` map and adds `:body`. -

Source

+

Source

## `multipart` @@ -329,7 +332,16 @@ Request: encodes `:form-params` map and adds `:body`. Adds appropriate body and header if making a multipart request. -

Source

+

Source

+ +## `oauth-token` + + + + +Request: adds `:authorization` header based on `:oauth-token` (a string token) + in request. +

Source

## `query-params` @@ -337,7 +349,7 @@ Adds appropriate body and header if making a multipart request. Request: encodes `:query-params` map and appends to `:uri`. -

Source

+

Source

## `throw-on-exceptional-status-code` @@ -345,13 +357,23 @@ Request: encodes `:query-params` map and appends to `:uri`. Response: throw on exceptional status codes -

Source

+

Source

## `unexceptional-statuses` -

Source

+

Source

+ +## `uri-with-query` +``` clojure + +(uri-with-query uri new-query) +``` + +We can't use the URI constructor because it encodes all arguments for us. + See https://stackoverflow.com/a/77971448/6264 +

Source

----- # babashka.http-client.websocket diff --git a/CHANGELOG.md b/CHANGELOG.md index 57af635..38fd0da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Babashka [http-client](https://github.com/babashka/http-client): HTTP client for Clojure and babashka built on java.net.http +## Unreleased + +- [#49](https://github.com/babashka/http-client/issues/49): add `::oauth-token` interceptor + ## 0.4.16 (2024-02-10) - [#45](https://github.com/babashka/http-client/issues/45): query param values are double encoded diff --git a/README.md b/README.md index 5d0a6d1..c193828 100644 --- a/README.md +++ b/README.md @@ -155,6 +155,14 @@ Basic auth: (:body (http/get "https://postman-echo.com/basic-auth" {:basic-auth ["postman" "password"]})) ;; => "{\"authenticated\":true}" ``` +### Oauth token + +Oauth token: + +``` clojure +(:body (http/get "https://httpbin.org/bearer" {:oauth-token "qwertyuiop"})) +;; => "{\n \"authenticated\": true, \n \"token\": \"qwertyuiop\"\n}\n" +``` ### Streaming diff --git a/src/babashka/http_client.clj b/src/babashka/http_client.clj index 654dd21..21705e6 100644 --- a/src/babashka/http_client.clj +++ b/src/babashka/http_client.clj @@ -112,6 +112,7 @@ * `:form-params` - a map of form params to send in the request body. * `:body` - a file, inputstream or string to send as the request body. * `:basic-auth` - a sequence of `user` `password` or map with `:user` `:pass` used for basic auth. + * `:oauth-token` - a string token used for bearer auth. * `:async` - perform request asynchronously. The response will be a `CompletableFuture` of the response map. * `:async-then` - a function that is called on the async result if successful * `:async-catch` - a function that is called on the async result if exceptional diff --git a/src/babashka/http_client/interceptors.clj b/src/babashka/http_client/interceptors.clj index ba6fede..01052f7 100644 --- a/src/babashka/http_client/interceptors.clj +++ b/src/babashka/http_client/interceptors.clj @@ -56,6 +56,19 @@ opts) opts))}) +(def oauth-token + "Request: adds `:authorization` header based on `:oauth-token` (a string token) + in request." + {:name ::oauth-token + :request (fn [opts] + (if-let [oauth-token (:oauth-token opts)] + (let [headers (:headers opts) + auth (str "Bearer " oauth-token) + headers (assoc headers :authorization auth) + opts (assoc opts :headers headers)] + opts) + opts))}) + (def accept-header "Request: adds `:accept` header. Only supported value is `:json`." {:name ::accept-header @@ -122,8 +135,7 @@ (.getPath uri) ;;=> "/" (.getQuery uri) ;;=> q=1 (.getFragment uri) ;;=> nil - (uri-with-query uri "f=dude%26hello") - ) + (uri-with-query uri "f=dude%26hello")) (def form-params "Request: encodes `:form-params` map and adds `:body`." @@ -244,6 +256,7 @@ construct-uri accept-header basic-auth + oauth-token query-params form-params multipart diff --git a/test/babashka/http_client_test.clj b/test/babashka/http_client_test.clj index 7836a15..00893ee 100644 --- a/test/babashka/http_client_test.clj +++ b/test/babashka/http_client_test.clj @@ -178,6 +178,14 @@ (http/get "https://postman-echo.com/basic-auth" {:basic-auth ["postman" "password"]}))))) +(deftest oauth-token-test + (let [token "qwertyuiop" + response (http/get "https://httpbin.org/bearer" {:oauth-token token}) + resp-body (-> response :body (json/parse-string true))] + (is (= 200 (:status response))) + (is (:authenticated resp-body)) + (is (= token (:token resp-body))))) + (deftest get-response-object-test (let [response (http/get "http://localhost:12233/200")] (is (map? response)) @@ -515,7 +523,6 @@ (is nil? (http/->Executor {:threads -1}))) (is (instance? java.util.concurrent.ThreadPoolExecutor (http/->Executor {:threads 2})))) - (deftest uri-with-query-params-test (when (resolve `i/uri-with-query) (is (=