Skip to content

Instantly share code, notes, and snippets.

@cjbarre
Last active December 14, 2019 13:49
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cjbarre/e93b78c2ef4b704f0eb222691dbdd5fa to your computer and use it in GitHub Desktop.
Save cjbarre/e93b78c2ef4b704f0eb222691dbdd5fa to your computer and use it in GitHub Desktop.
Example: Send A Signed Multi-Part Media HTTP POST Request With Clojure | Clojure, Twitter API, media/upload, OAuth 1.0, multipart/form-data, Authorization, Signed Request
;; Example: Send A Signed Multi-Part Media HTTP POST Request With Clojure
;;
;; Keywords: Clojure, Twitter API, media/upload, OAuth 1.0, multipart/form-data, Authorization, Signed Request
;;
;; Dependencies: [clj-http "2.2.0"] [clj-oauth "1.5.5"]
;;
;; Description:
;; The code is meant to read well as an example, not be idiomatic or efficiently organized.
;;
;; I am leaving the raw materials here, put it together how you'd like it!
;;
;; This example should work if you have the dependencies and run it.
;;
;; If you get this working, the APPEND and FINALIZE commands are just as easy.
;; Remember to base64 encode your byte array if you use the media_data parameter in your APPEND command request.
;; Don't try to type convert your byte-array, just pass it into the :content parameter of the multipart entry as is.
;;
;;
(ns twitter-signed-multi-part-post-request-example.core
(:require [clj-http.client :as http]
[oauth.client :as oauth]))
;; I grabbed a JPEG from the internet.
;;
;; Wherever your media comes from, just make sure you can turn it into a byte-array.
;;
(def grab-the-media-url "http://2.bp.blogspot.com/-Idj_-PCo-8k/Uh7bJseIHwI/AAAAAAAAHMU/ph8-m5x3LcY/s640/Autumn-Jpegs3.jpg")
;; I used the clj-http get function with the {:as :byte-array} option.
;;
;; This converts the body of your HTTP response to a byte-array.
;;
(def media
(let [options {:as :byte-array}
response (http/get grab-the-media-url options)]
(-> response :body)))
;; I store my OAuth parameters together here for easy access.
;;
;; Be sure to replace all the values below.
;;
(def oauth-parameters
{:consumer-credentials
{:key "<Your Consumer Key Here>"
:secret "<Your Consumer Key Secret Here>"}
:urls
{:request-token "<Current Twitter Request Token URL Here>"
:access-token "<Current Twitter Access Token URL Here>"
:authorize "<Current Twitter Authorize URL Here>"}
:algorithm :hmac-sha1
:user-credentials
{:token "<Your User Access Token Here>"
:secret "<Your User Access Token Secret Here>"}})
;; I create an OAuth consumer here with information from the oauth-parameters map.
;;
;; I am making use of the make-consumer function from clj-oauth here.
;;
;; The only OAuth parameter not used here is :user-credentials.
;;
(def oauth-consumer
(let [{:keys [consumer-credentials urls algorithm]} oauth-parameters]
(oauth/make-consumer
(-> consumer-credentials :key)
(-> consumer-credentials :secret)
(-> urls :request-token)
(-> urls :access-token)
(-> urls :authorize)
algorithm)))
;; I MUST use the following api url for multi-part media uploads.
;;
(def upload-api-url "https://upload.twitter.com/1.1/media/upload.json")
;; I MUST use the HTTP POST verb here.
;;
(def http-method :post)
;; I create my OAuth credentials for this request.
;;
;; I am making use of the credentials function from clj-oauth here.
;;
;; The :user-credentials are now being used from oauth-parameters.
;;
;; Request parameters are not signed when sending multipart/form-data.
;;
(def oauth-credentials
(let [{:keys [user-credentials]} oauth-parameters]
(oauth/credentials
oauth-consumer
(-> user-credentials :token)
(-> user-credentials :secret)
http-method
upload-api-url)))
;; I keep my parameters together here for easy access.
;;
;; total-bytes simply comes from a count on the media byte-array
;;
;; I'm displaying how to send the INIT command in this example.
(def http-request-parameters
{:command "INIT"
:total-bytes (-> media count str)
:media-type "image/jpeg"})
;; I keep my headers together here for easy access.
;;
;; I am making use of the authorization-header function from clj-oauth here.
;;
;; Check the OAuth 1.0 specification for the Authorizaton hdeader for more information about why this is necessary.
;;
(def http-request-headers
{:authorization (oauth/authorization-header oauth-credentials)})
;; I create an HTTP request before sending it here to bring it all together.
;;
;; Note the :multipart parameter -- check the clj-http readme.
;;
(def signed-http-request
{:method http-method
:url upload-api-url
:headers {"Authorization" (-> http-request-headers :authorization)}
:multipart [{:name "command" :content (-> http-request-parameters :command)}
{:name "total_bytes" :content (-> http-request-parameters :total-bytes)}
{:name "media_type" :content (-> http-request-parameters :media-type)}]})
;; I send the signed http request.
;;
;; I am making use of the more general request function from clj-http here.
;;
(http/request signed-http-request)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment