Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Refactor to use clj-oauth2

  • Loading branch information...
commit 4235ddabe03d0e47f4142674990fa0741fba2858 1 parent 8b67af8
Pat Patterson authored March 16, 2012
13  README.md
Source Rendered
... ...
@@ -1,4 +1,15 @@
1  
-Very embryonic Clojure toolkit for Force.com REST API. Brace yourself for rapid evolution!
  1
+Clojure toolkit for Force.com REST API. 
  2
+
  3
+This project currently uses [a fork of clj-oauth2](https://github.com/metadaddy-sfdc/clj-oauth2). You will need to grab that code and install it into your local Maven repo:
  4
+
  5
+    $ git clone git://github.com/metadaddy-sfdc/clj-oauth2.git
  6
+    $ cd clj-oauth2
  7
+    $ lein install
  8
+
  9
+I am working with Moritz to merge this into [the mainline clj-oauth2](https://github.com/DerGuteMoritz/clj-oauth2)
  10
+
  11
+Deployment
  12
+----------
2 13
 
3 14
 Create a remote access app (Setup | App Setup | Develop | Remote Access).
4 15
 
BIN  maven_repository/clj-oauth2/clj-oauth2/0.3.1/clj-oauth2-0.3.1.jar
Binary file not shown
9  maven_repository/clj-oauth2/clj-oauth2/0.3.1/clj-oauth2-0.3.1.pom
... ...
@@ -0,0 +1,9 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
  3
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  4
+  <modelVersion>4.0.0</modelVersion>
  5
+  <groupId>clj-oauth2</groupId>
  6
+  <artifactId>clj-oauth2</artifactId>
  7
+  <version>0.3.1</version>
  8
+  <description>POM was created from install:install-file</description>
  9
+</project>
12  maven_repository/clj-oauth2/clj-oauth2/maven-metadata-local.xml
... ...
@@ -0,0 +1,12 @@
  1
+<?xml version="1.0" encoding="UTF-8"?>
  2
+<metadata>
  3
+  <groupId>clj-oauth2</groupId>
  4
+  <artifactId>clj-oauth2</artifactId>
  5
+  <versioning>
  6
+    <release>0.3.1</release>
  7
+    <versions>
  8
+      <version>0.3.1</version>
  9
+    </versions>
  10
+    <lastUpdated>20120316170610</lastUpdated>
  11
+  </versioning>
  12
+</metadata>
21  project.clj
... ...
@@ -1,10 +1,13 @@
1  
-(defproject clojure-force-sample "1.0.0"
2  
-  :description "Force.com REST API Sample in Clojure"
3  
-  :dependencies [[org.clojure/clojure "1.2.0"]
4  
-                 [org.clojure/clojure-contrib "1.2.0"]
5  
-                 [compojure "0.4.1"]
6  
-                 [ring/ring-jetty-adapter "0.2.5"]
  1
+(defproject clojure-force "1.0.0-SNAPSHOT"
  2
+  :description "FIXME: write description"
  3
+  :dependencies [[org.clojure/clojure "1.3.0"]
  4
+	 							 [org.clojure/data.json "0.1.1"]
  5
+                 [compojure "1.0.1"]
  6
+                 [ring/ring-jetty-adapter "1.0.2"]
  7
+								 [clj-http "0.2.6"] ; for clj-oauth2
  8
+								 [uri "1.1.0"] ; for clj-oauth2
  9
+                 [commons-codec/commons-codec "1.6"] ; for clj-oauth2
  10
+								 [clj-oauth2 "0.3.1"]
7 11
                  [enlive "1.0.0"]]
8  
-  :dev-dependencies [[lein-run "1.0.0"]
9  
-                     [ring/ring-devel "0.2.5"]]
10  
-  :main sample.app)
  12
+  :repositories {"local" ~(str (.toURI (java.io.File. "maven_repository")))}
  13
+  :main clojure-force.core)
204  src/clojure_force/core.clj
... ...
@@ -0,0 +1,204 @@
  1
+(ns clojure-force.core
  2
+	(:use (clojure-force force)
  3
+	      [clojure.pprint :only [pprint]]
  4
+				[clojure.walk :only [keywordize-keys]]
  5
+				[compojure.core :only [defroutes GET POST]]
  6
+				[ring.middleware.session :only [wrap-session]]
  7
+				[ring.middleware.params :only [wrap-params]]
  8
+		    [ring.middleware.keyword-params :only [wrap-keyword-params]])
  9
+	(:require [ring.adapter.jetty :as jetty]
  10
+		        [clj-oauth2.client :as oauth2]
  11
+		        [clj-oauth2.ring :as oauth2-ring]
  12
+		        [compojure.route :as route]
  13
+						[net.cgrand.enlive-html :as html]))
  14
+		
  15
+; oauth2 2.0 Parameters for Force.com
  16
+(def login-uri
  17
+	(get (System/getenv) "LOGIN_URI" "https://login.salesforce.com"))
  18
+	
  19
+(defn my-excluded [uri]
  20
+	(contains? #{"/exclude1" "/exclude2"} uri))
  21
+
  22
+(def force-com-oauth2
  23
+  {:authorization-uri (str login-uri "/services/oauth2/authorize")
  24
+   :access-token-uri (str login-uri "/services/oauth2/token")
  25
+   :redirect-uri (System/getenv "REDIRECT_URI") ; TODO - figure out default
  26
+   :client-id (System/getenv "CLIENT_ID")
  27
+   :client-secret (System/getenv "CLIENT_SECRET")
  28
+   :scope ["id" "api" "refresh_token"]
  29
+   :grant-type "authorization_code"
  30
+   :force-https (System/getenv "FORCE_HTTPS") ; on Heroku the app thinks it is always http
  31
+   :trace-messages (Boolean/valueOf (get (System/getenv) "DEBUG" "false"))
  32
+   :get-state oauth2-ring/get-state-from-session
  33
+   :put-state oauth2-ring/put-state-in-session
  34
+   :get-target oauth2-ring/get-target-from-session
  35
+   :put-target oauth2-ring/put-target-in-session
  36
+   :get-oauth2-data oauth2-ring/get-oauth2-data-from-session
  37
+   :put-oauth2-data oauth2-ring/put-oauth2-data-in-session
  38
+   :exclude #"^/exclude.*"})
  39
+
  40
+; HTML templating stuff                  
  41
+(def field-sel [:select#field :> html/first-child])
  42
+
  43
+(html/defsnippet field-model "templates/index.html" field-sel
  44
+  [{name :name label :label}]
  45
+  [:option] (html/do->
  46
+        (html/content label)
  47
+        (html/set-attr :value name)))
  48
+
  49
+(def link-sel [:table.accountlist :> html/first-child])
  50
+    
  51
+(html/defsnippet link-model "templates/index.html" link-sel
  52
+  [{name :Name id :Id}]
  53
+  [:a] (html/do->
  54
+        (html/content name)
  55
+        (html/set-attr :href (str "detail?id=" id))))
  56
+    
  57
+(html/deftemplate index "templates/index.html"
  58
+    [display-name fields accounts]
  59
+    [:span#displayname] (html/content display-name)
  60
+    [:select#field] (html/content (map field-model fields))
  61
+    [:table.accountlist] (html/content (map link-model accounts)))
  62
+
  63
+(html/deftemplate detail "templates/detail.html"
  64
+    [account]
  65
+    [:td#accountname] (html/content (get account :Name))
  66
+    [:a#industry] (html/do->
  67
+                    (html/content (get account :Industry))
  68
+                    (html/set-attr :href (str "/?field=Industry&value=" (get account :Industry) "&search=Search")))
  69
+    [:td#tickersymbol] (html/content (get account :TickerSymbol))
  70
+    [:input#id] (html/set-attr :value (get account :Id)))
  71
+
  72
+(html/deftemplate edit "templates/edit.html"
  73
+    [account]
  74
+    [:input#Name] (html/set-attr :value (get account :Name))
  75
+    [:input#Industry] (html/set-attr :value (get account :Industry))
  76
+    [:input#TickerSymbol] (html/set-attr :value (get account :TickerSymbol))
  77
+    [:input#id] (html/set-attr :value (get account :Id)))
  78
+
  79
+(html/deftemplate new-account "templates/edit.html"
  80
+    []
  81
+    [:h1#header] (html/set-attr :value "New Account")
  82
+    [:input#Name] (html/set-attr :value "")
  83
+    [:input#Industry] (html/set-attr :value "")
  84
+    [:input#TickerSymbol] (html/set-attr :value "")
  85
+    [:input#id] (html/set-attr :value "")
  86
+    [:input#action] 
  87
+    (html/do->
  88
+        (html/set-attr :value "Create") 
  89
+        (html/set-attr :name "create")))
  90
+
  91
+(html/deftemplate created "templates/created.html"
  92
+    [response]
  93
+    [:span#id] (html/content (:id response)))
  94
+
  95
+(html/deftemplate deleted "templates/deleted.html"
  96
+    [id]
  97
+    [:span#id] (html/content id))
  98
+
  99
+(html/deftemplate updated "templates/updated.html"
  100
+    [id]
  101
+    [:span#id] (html/content id))
  102
+
  103
+(html/deftemplate logout "templates/logout.html"
  104
+    [instance-url]
  105
+    [:iframe#logoutframe] (html/set-attr :src (str instance-url "/secur/logout.jsp")))
  106
+
  107
+(defn render 
  108
+    "Helper function for rendering Enlive output"
  109
+    [t] (apply str t))
  110
+
  111
+; This is the mapping of URL paths to actions
  112
+(defroutes handler
  113
+  (GET "/" 
  114
+    {params :params session :session oauth2 :oauth2} 
  115
+	(let [field-list (if (nil? (:field-list session))
  116
+    	    (filter #(= (:type %) "string") (:fields (describe "Account")))
  117
+    	    (:field-list session))
  118
+	    user-id (if (nil? (:user-id session))
  119
+    	    (id)
  120
+    	    (:user-id session))]
  121
+		{:headers {"Content-type" "text/html; charset=UTF-8"}
  122
+		 :session {:field-list field-list :user-id user-id}
  123
+		 :body (render (index (:display_name user-id) field-list
  124
+	         (get (if 
  125
+	             (nil? (:value params)) 
  126
+	             (query "SELECT Name, Id FROM Account ORDER BY Name LIMIT 20")
  127
+	             (query (str "SELECT Name, Id FROM Account WHERE " (:field params) " LIKE '" (:value params) "%' ORDER BY Name LIMIT 20")))
  128
+			      :records)))}))
  129
+	(GET "/test" 
  130
+    {oauth2 :oauth2} 
  131
+		(:body (oauth2/get 
  132
+			       (str 
  133
+	             (:instance_url (:params oauth2)) 
  134
+	             "/services/data/v24.0/sobjects/Account/0015000000VALDxAAP")
  135
+	           {:oauth2 oauth2})))
  136
+  (GET "/test2" 
  137
+    {oauth2 :oauth2} 
  138
+ 	  (with-out-str 
  139
+      (pprint (retrieve "Account" "0015000000VALDxAAP"))))
  140
+  (GET "/logout" 
  141
+	  {oauth2 :oauth2}
  142
+	  {:oauth2 nil
  143
+	   :body (render (logout (:instance_url (:params oauth2))))})
  144
+  (GET "/detail" 
  145
+	{params :params oauth2 :oauth2} 
  146
+	{:headers {"Content-type" "text/html; charset=UTF-8"}
  147
+	 :body 
  148
+		(let [account (retrieve
  149
+		    "Account" 
  150
+		    (:id params) 
  151
+		    "Name,Industry,TickerSymbol")] 
  152
+			(render (detail account)))})
  153
+  (POST "/action"
  154
+  	{params :params oauth2 :oauth2} 
  155
+  	(cond
  156
+          (not (nil? (:new params))) 
  157
+          {:headers {"Content-type" "text/html; charset=UTF-8"}
  158
+      	 :body (render (new-account))}
  159
+          (not (nil? (:delete params))) 
  160
+          {:headers {"Content-type" "text/html; charset=UTF-8"}
  161
+      	 :body (do (delete 
  162
+     		    "Account" 
  163
+     		    (:id params))
  164
+     			(render (deleted (:id params))))}
  165
+          (not (nil? (:edit params))) 
  166
+          {:headers {"Content-type" "text/html; charset=UTF-8"}
  167
+        	 :body 
  168
+        		(let [account (retrieve "Account" (:id params) "Name,Industry,TickerSymbol")] 
  169
+        			(render (edit account)))}))
  170
+  (POST "/account"
  171
+		{params :params oauth2 :oauth2} 
  172
+		(cond
  173
+      (not (nil? (:create params))) 
  174
+        {:headers {"Content-type" "text/html; charset=UTF-8"}
  175
+    	   :body (let [response (create "Account" (dissoc params :create :id))] 
  176
+   			(render (created response)))}
  177
+      (not (nil? (:update params))) 
  178
+        {:headers {"Content-type" "text/html; charset=UTF-8"}
  179
+      	 :body (do (update "Account" (:id params) (dissoc params :update :id))  
  180
+  			(render (updated (:id params))))}))
  181
+  (GET "/exclude1" []
  182
+		{:headers {"Content-type" "text/html; charset=UTF-8"}
  183
+		 :body "excluded1"})
  184
+  (GET "/exclude2" []
  185
+		{:headers {"Content-type" "text/html; charset=UTF-8"}
  186
+		 :body "excluded2"})
  187
+  (route/files "/" {:root "www/public"})
  188
+  (route/not-found "Page not found"))
  189
+
  190
+; Set up the middleware
  191
+(def app 
  192
+	(-> handler 
  193
+		(oauth2-ring/wrap-oauth2 force-com-oauth2)
  194
+		wrap-session 
  195
+		wrap-keyword-params
  196
+		wrap-params))
  197
+
  198
+(defn -main []
  199
+  (let [port (Integer/parseInt (get (System/getenv) "PORT" "8080"))]
  200
+	  (if-let [ssl-port-str (System/getenv "SSL_PORT")]
  201
+	    (jetty/run-jetty app {:join? false :ssl? true :port port :ssl-port (Integer/parseInt ssl-port-str)
  202
+	                            :keystore (System/getenv "KEYSTORE")
  203
+	                            :key-password (System/getenv "KEY_PASSWORD")})
  204
+	    (jetty/run-jetty app {:port port}))))
112  src/clojure_force/force.clj
... ...
@@ -0,0 +1,112 @@
  1
+(ns clojure-force.force
  2
+    "Force.com API built on treasure.core"
  3
+		(:use [clj-oauth2.client :only [request]])
  4
+    (:require [ring.util.codec :as codec]
  5
+	            [clojure.data.json :as json]))
  6
+
  7
+; Macros currently get oauth2 token from current scope
  8
+; Can get oauth2 token from session - (:treajure.core/oauth2 ~'session)
  9
+
  10
+(defn force-request
  11
+		[url opts]
  12
+		(do 
  13
+			 (when (:trace-messages (:oauth2 opts)) 
  14
+				 (println (str "clojure-force.force.force-request url: " url))
  15
+			   (println (str "clojure-force.force.force-request body: " (:body opts))))
  16
+			 (let [raw-response (request (merge {:url url :throw-exceptions false :content-type :json :accept :json} opts))]
  17
+					(when (:trace-messages (:oauth2 opts)) 
  18
+					  (println (str "clojure-force.force.force-request raw-response: " raw-response)))
  19
+					(let [json-response (:body raw-response)]
  20
+					     (let [response (if (or (nil? json-response) (== (count json-response) 0)) nil (json/read-json json-response))]
  21
+						   (when (:trace-messages (:oauth2 opts)) 
  22
+							   (println (str "clojure-force.force.force-request cooked response: " response)))
  23
+						   response)))))
  24
+      
  25
+(defmacro id
  26
+    []
  27
+    `(id-helper ~'oauth2))
  28
+
  29
+(defn id-helper
  30
+  "Given an oauth2 response, return the id"
  31
+  [oauth2]
  32
+  (force-request
  33
+      (:id (:params oauth2))
  34
+      {:method :get :oauth2 oauth2}))
  35
+
  36
+(defmacro describe
  37
+    [obj-type]
  38
+    `(describe-helper ~'oauth2 ~obj-type))
  39
+
  40
+(defn describe-helper
  41
+  "Given an oauth2 response, and object type, return the sobject metadata"
  42
+  [oauth2 obj-type]
  43
+  (force-request
  44
+      (str (:instance_url (:params oauth2)) "/services/data/v24.0/sobjects/" obj-type "/describe/")
  45
+      {:method :get :oauth2 oauth2}))
  46
+                      
  47
+(defmacro query
  48
+  [query]
  49
+  `(query-helper ~'oauth2 ~query))
  50
+
  51
+(defn query-helper
  52
+    "Given an oauth2 response, execute the supplied query and return the response"
  53
+    [oauth2 query]
  54
+    (force-request
  55
+        (str (:instance_url (:params oauth2)) "/services/data/v24.0/query?q=" (codec/url-encode query))
  56
+        {:method :get :oauth2 oauth2}))
  57
+
  58
+(defmacro create
  59
+  [obj-type fields]
  60
+  `(create-helper ~'oauth2 ~obj-type ~fields))
  61
+
  62
+(defn create-helper 
  63
+    "Given an oauth2 response, object type and fields, create the sobject, return status"
  64
+    [oauth2 obj-type fields]
  65
+    (force-request (str (:instance_url (:params oauth2)) "/services/data/v24.0/sobjects/" obj-type)
  66
+        {:method :post 
  67
+	       :oauth2 oauth2 
  68
+	       :body (json/json-str fields)}))
  69
+            			  
  70
+(defmacro retrieve
  71
+    ([obj-type id]
  72
+    `(retrieve-helper ~'oauth2 ~obj-type ~id))
  73
+    ([obj-type id field-list]
  74
+    `(retrieve-helper ~'oauth2 ~obj-type ~id ~field-list)))
  75
+
  76
+(defn retrieve-helper
  77
+    "Given an oauth2 response, object type, id and optional comma-separated field list, return the sobject"
  78
+    ([oauth2 obj-type id]
  79
+    (force-request
  80
+        (str (:instance_url (:params oauth2)) "/services/data/v24.0/sobjects/" obj-type "/" id)
  81
+        {:method :get :oauth2 oauth2}))
  82
+    ; TODO - make field list a vector?
  83
+    ([oauth2 obj-type id field-list]
  84
+    (force-request
  85
+        (str (:instance_url (:params oauth2)) "/services/data/v24.0/sobjects/" obj-type "/" id "?fields=" field-list)
  86
+        {:method :get :oauth2 oauth2})))
  87
+
  88
+(defmacro update
  89
+  [obj-type id fields]
  90
+  `(update-helper ~'oauth2 ~obj-type ~id ~fields))
  91
+
  92
+(defn update-helper 
  93
+    "Given an oauth2 response, object type and fields, update the sobject"
  94
+    ; Note - Java HttpURLConnection does not support PATCH (at least as of 
  95
+    ; Java SE 6), so use the _HttpMethod workaround
  96
+    [oauth2 obj-type id fields]
  97
+    (force-request
  98
+        (str (:instance_url (:params oauth2)) "/services/data/v24.0/sobjects/" obj-type "/" id "?_HttpMethod=PATCH")
  99
+        {:method :post 
  100
+	       :oauth2 oauth2 
  101
+	       :body (json/json-str fields)}))
  102
+
  103
+(defmacro delete
  104
+    [obj-type id]
  105
+    `(delete-helper ~'oauth2 ~obj-type ~id))
  106
+
  107
+(defn delete-helper
  108
+    "Given an oauth2 response, object type and id, delete the sobject"
  109
+    [oauth2 obj-type id]
  110
+    (force-request
  111
+      (str (:instance_url (:params oauth2)) "/services/data/v24.0/sobjects/" obj-type "/" id)
  112
+      {:method :delete :oauth2 oauth2}))
170  src/sample/app.clj
... ...
@@ -1,170 +0,0 @@
1  
-(ns sample.app
2  
-  (:use (ring.adapter jetty)
3  
-        (ring.middleware session)
4  
-        (compojure core)
5  
-        (treajure core force))
6  
-  (:require [compojure.route :as route]
7  
-			[net.cgrand.enlive-html :as html]))
8  
-
9  
-; Options for treajure
10  
-(def oauth-params {:login-uri (get (System/getenv) "LOGIN_URI" "https://login.salesforce.com")
11  
-                   :redirect-uri (System/getenv "REDIRECT_URI") ; TODO - figure out default
12  
-                   :client-id (System/getenv "CLIENT_ID")
13  
-                   :client-secret (System/getenv "CLIENT_SECRET")
14  
-                   :trace-messages (Boolean/valueOf (get (System/getenv) "DEBUG" "false"))
15  
-                   })
16  
-
17  
-; HTML templating stuff                  
18  
-(def *field-sel* [:select#field :> html/first-child])
19  
-
20  
-(html/defsnippet field-model "templates/index.html" *field-sel*
21  
-  [{name :name label :label}]
22  
-  [:option] (html/do->
23  
-        (html/content label)
24  
-        (html/set-attr :value name)))
25  
-
26  
-(def *link-sel* [:table.accountlist :> html/first-child])
27  
-    
28  
-(html/defsnippet link-model "templates/index.html" *link-sel*
29  
-  [{name :Name id :Id}]
30  
-  [:a] (html/do->
31  
-        (html/content name)
32  
-        (html/set-attr :href (str "detail?id=" id))))
33  
-    
34  
-(html/deftemplate index "templates/index.html"
35  
-    [display-name fields accounts]
36  
-    [:span#displayname] (html/content display-name)
37  
-    [:select#field] (html/content (map field-model fields))
38  
-    [:table.accountlist] (html/content (map link-model accounts)))
39  
-
40  
-(html/deftemplate detail "templates/detail.html"
41  
-    [account]
42  
-    [:td#accountname] (html/content (get account :Name))
43  
-    [:a#industry] (html/do->
44  
-                    (html/content (get account :Industry))
45  
-                    (html/set-attr :href (str "/?field=Industry&value=" (get account :Industry) "&search=Search")))
46  
-    [:td#tickersymbol] (html/content (get account :TickerSymbol))
47  
-    [:input#id] (html/set-attr :value (get account :Id)))
48  
-
49  
-(html/deftemplate edit "templates/edit.html"
50  
-    [account]
51  
-    [:input#Name] (html/set-attr :value (get account :Name))
52  
-    [:input#Industry] (html/set-attr :value (get account :Industry))
53  
-    [:input#TickerSymbol] (html/set-attr :value (get account :TickerSymbol))
54  
-    [:input#id] (html/set-attr :value (get account :Id)))
55  
-
56  
-(html/deftemplate new-account "templates/edit.html"
57  
-    []
58  
-    [:h1#header] (html/set-attr :value "New Account")
59  
-    [:input#Name] (html/set-attr :value "")
60  
-    [:input#Industry] (html/set-attr :value "")
61  
-    [:input#TickerSymbol] (html/set-attr :value "")
62  
-    [:input#id] (html/set-attr :value "")
63  
-    [:input#action] 
64  
-    (html/do->
65  
-        (html/set-attr :value "Create") 
66  
-        (html/set-attr :name "create")))
67  
-
68  
-(html/deftemplate created "templates/created.html"
69  
-    [response]
70  
-    [:span#id] (html/content (get response :id)))
71  
-
72  
-(html/deftemplate deleted "templates/deleted.html"
73  
-    [id]
74  
-    [:span#id] (html/content id))
75  
-
76  
-(html/deftemplate updated "templates/updated.html"
77  
-    [id]
78  
-    [:span#id] (html/content id))
79  
-
80  
-(html/deftemplate logout "templates/logout.html"
81  
-    [_])
82  
-
83  
-(defn render 
84  
-    "Helper function for rendering Enlive output"
85  
-    [t] (apply str t))
86  
-
87  
-; This is the mapping of URL paths to actions
88  
-(defroutes main-routes
89  
-  (GET "/" 
90  
-    {params :params session :session oauth :oauth} 
91  
-	(let [field-list (if (nil? (:field-list session))
92  
-    	    (filter #(= (:type %) "string") (get (describe "Account") :fields))
93  
-    	    (:field-list session))
94  
-	    user-id (if (nil? (:user-id session))
95  
-    	    (id)
96  
-    	    (:user-id session))]
97  
-		{:headers {"Content-type" "text/html; charset=UTF-8"}
98  
-		 :session {:field-list field-list :user-id user-id}
99  
-		 :body (render (index (:display_name user-id) field-list
100  
-	         (get (if 
101  
-	             (nil? (params "value")) 
102  
-	             (query (str "SELECT Name, Id FROM Account ORDER BY Name LIMIT 20"))
103  
-	             (query (str "SELECT Name, Id FROM Account WHERE " (params "field") " LIKE '" (params "value") "%' ORDER BY Name LIMIT 20")))
104  
-			      :records)))}))
105  
-  (GET "/logout" 
106  
-	{params :params oauth :oauth} 
107  
-	{:status 302
108  
-	 :headers {"Content-type" "text/html; charset=UTF-8", 
109  
-	    "Location" "https://superpat-developer-edition.my.salesforce.com/secur/logout.jsp"}
110  
-	 :oauth nil})
111  
-  (GET "/detail" 
112  
-	{params :params oauth :oauth} 
113  
-	{:headers {"Content-type" "text/html; charset=UTF-8"}
114  
-	 :body 
115  
-		(let [account (retrieve
116  
-		    "Account" 
117  
-		    (params "id") 
118  
-		    "Name,Industry,TickerSymbol")] 
119  
-			(render (detail account)))})
120  
-  (POST "/action"
121  
-  	{params :params oauth :oauth} 
122  
-  	(cond
123  
-          (not (nil? (params "new"))) 
124  
-          {:headers {"Content-type" "text/html; charset=UTF-8"}
125  
-      	 :body (render (new-account))}
126  
-          (not (nil? (params "delete"))) 
127  
-          {:headers {"Content-type" "text/html; charset=UTF-8"}
128  
-      	 :body (do (delete 
129  
-     		    "Account" 
130  
-     		    (params "id"))
131  
-     			(render (deleted (params "id"))))}
132  
-          (not (nil? (params "edit"))) 
133  
-          {:headers {"Content-type" "text/html; charset=UTF-8"}
134  
-        	 :body 
135  
-        		(let [account (retrieve 
136  
-        		    "Account" 
137  
-        		    (params "id") 
138  
-        		    "Name,Industry,TickerSymbol")] 
139  
-        			(render (edit account)))}))
140  
-  (POST "/account"
141  
-	{params :params oauth :oauth} 
142  
-	(cond
143  
-        (not (nil? (params "create"))) 
144  
-        {:headers {"Content-type" "text/html; charset=UTF-8"}
145  
-    	 :body (let [response (create 
146  
-   		    "Account" 
147  
-   		    ; TODO - compose this map dynamically?
148  
-   		    {:Name (params "Name") :Industry (params "Industry") :TickerSymbol (params "TickerSymbol")})] 
149  
-   			(render (created response)))}
150  
-        (not (nil? (params "update"))) 
151  
-        {:headers {"Content-type" "text/html; charset=UTF-8"}
152  
-      	 :body (do (update
153  
-  		    "Account" 
154  
-  		    (params "id")
155  
-   		    ; TODO - compose this map dynamically?
156  
-   		    {:Name (params "Name") :Industry (params "Industry") :TickerSymbol (params "TickerSymbol")})  
157  
-  			(render (updated (params "id"))))}))
158  
-  (route/files "/" {:root "www/public"})
159  
-  (route/not-found "Page not found"))
160  
-
161  
-; This is the magic - wrap our routes with the treajure middleware
162  
-(wrap! main-routes (:oauth oauth-params)) 
163  
-
164  
-(defn -main []
165  
-  (let [port (Integer/parseInt (get (System/getenv) "PORT" "8080"))]
166  
-  (if-let [ssl-port (and (System/getenv "SSL_PORT") (Integer/parseInt (System/getenv "SSL_PORT")))]
167  
-    (run-jetty main-routes {:join? false :ssl? true :port port :ssl-port ssl-port
168  
-                            :keystore (System/getenv "KEYSTORE")
169  
-                            :key-password (System/getenv "KEY_PASSWORD")})
170  
-    (run-jetty main-routes {:port port}))))
2  src/templates/created.html
@@ -3,7 +3,7 @@
3 3
         <title>Created Account</title>
4 4
 		<link href="style.css" rel="stylesheet" type="text/css" />
5 5
     </head>
6  
-    <body>
  6
+    <body onLoad="setTimeout(function(){window.location = '/';}, 3000)">
7 7
 		<p>Created <span id="id">000000000000000001</span>
8 8
 		<p><a href="/">Home</a></p>
9 9
 	</body>
2  src/templates/deleted.html
@@ -3,7 +3,7 @@
3 3
         <title>Deleted Account</title>
4 4
 		<link href="style.css" rel="stylesheet" type="text/css" />
5 5
     </head>
6  
-    <body>
  6
+    <body onLoad="setTimeout(function(){window.location = '/';}, 3000)">
7 7
 		<p>Deleted <span id="id">000000000000000001</span>
8 8
 		<p><a href="/">Home</a></p>
9 9
 	</body>
1  src/templates/index.html
... ...
@@ -1,3 +1,4 @@
  1
+<!DOCTYPE html>
1 2
 <html>
2 3
     <head>
3 4
         <title>Account List</title>
1  src/templates/logout.html
@@ -7,6 +7,7 @@
7 7
 		<p>Logged out</p>
8 8
 		<p><a href="/">Home</a></p>
9 9
 		<iframe 
  10
+		  id="logoutframe"
10 11
 		  style="display:none;" 
11 12
 		  src="https://login.salesforce.com/secur/logout.jsp"/>
12 13
 	</body>
4  src/templates/updated.html
... ...
@@ -1,9 +1,9 @@
1 1
 <html>
2 2
     <head>
3  
-        <title>Created Account</title>
  3
+        <title>Updated Account</title>
4 4
 		<link href="style.css" rel="stylesheet" type="text/css" />
5 5
     </head>
6  
-    <body>
  6
+    <body onLoad="setTimeout(function(){window.location = '/';}, 3000)">
7 7
 		<p>Updated <span id="id">000000000000000001</span>
8 8
 		<p><a href="/">Home</a></p>
9 9
 	</body>
135  src/treajure/core.clj
... ...
@@ -1,135 +0,0 @@
1  
-(ns treajure.core
2  
-    "OAuth Ring Middleware. This should be fairly generic OAuth 2 draft 10."
3  
-    (:require [ring.middleware.params :as params]
4  
-              [ring.middleware.session :as session]
5  
-              [ring.util.codec :as codec]
6  
-    	      [clojure.contrib.http.agent :as http-agent]
7  
-    		  [clojure.contrib.java-utils :as java-utils]
8  
-              [clojure.contrib.json :as json]))
9  
-
10  
-(defn- get-oauth-service-url 
11  
-  "Get the OAuth authentication service url to which the user should be
12  
-  redirected to authenticate and authorize the app."
13  
-  [oauth-params]
14  
-  (str 
15  
-  	(:login-uri oauth-params) "/services/oauth2/authorize?response_type=code" 
16  
-  	"&client_id=" (:client-id oauth-params)
17  
-  	"&redirect_uri=" (codec/url-encode (:redirect-uri oauth-params))))
18  
-
19  
-(defn- refresh-oauth 
20  
-    "Do the OAuth refresh flow and return a map comprising a merge of the new
21  
-    access token etc with the old client id, secret etc"
22  
-    [oauth]
23  
-    (when (:trace-messages oauth) 
24  
-        (println (str "Sending OAuth request for refresh token " (:refresh-token oauth))))
25  
-	(let [response 	
26  
-		(json/read-json (http-agent/string
27  
-    		(http-agent/http-agent (str (:login-uri oauth) "/services/oauth2/token")
28  
-    		  :method "POST" 
29  
-    		  :body (str
30  
-    		    "grant_type=refresh_token" 
31  
-    		    "&client_id=" (:client-id oauth) 
32  
-    		    "&client_secret=" (:client-secret oauth) 
33  
-    		    "&refresh_token=" (:refresh_token oauth)))))]
34  
-            (when (:trace-messages oauth) 
35  
-                (println (str "OAuth GET response is " response)))
36  
-        	(merge oauth response)))
37  
-
38  
-(defn- get-oauth 
39  
-    "Exchange an OAuth authorization code for an OAuth response containing the
40  
-    instance url, access token etc and the incoming configuration parameters"
41  
-    [oauth-params code]
42  
-    (when (:trace-messages oauth-params) 
43  
-        (println (str "Sending OAuth request for code " code)))
44  
-	(let [response 	
45  
-		(json/read-json (http-agent/string
46  
-    		(http-agent/http-agent (str (:login-uri oauth-params) "/services/oauth2/token")
47  
-    		  :method "POST" 
48  
-    		  :body (str
49  
-    		 	"code=" code
50  
-    		    "&grant_type=authorization_code" 
51  
-    		    "&client_id=" (:client-id oauth-params) 
52  
-    		    "&client_secret=" (:client-secret oauth-params) 
53  
-    		    "&redirect_uri=" (:redirect-uri oauth-params)))))]
54  
-            (when (:trace-messages oauth-params) 
55  
-                (println (str "OAuth GET response is " response)))
56  
-        	(merge oauth-params response)))
57  
-
58  
-(defn raw-oauth-request
59  
-    "Low level OAuth request function"
60  
-    ([oauth url] (raw-oauth-request oauth url "GET" nil false))
61  
-    ([oauth url method] (raw-oauth-request oauth url method nil false))
62  
-    ([oauth url method body] (raw-oauth-request oauth url method body false))
63  
-    ([oauth url method body retry]
64  
-        (when (:trace-messages @oauth) 
65  
-            (println (str "Sending OAuth " method 
66  
-                " Access token " (:access_token @oauth) 
67  
-                " url " url 
68  
-                (if (nil? body) " no body" (str " body " body)))))
69  
-        (let [http-agnt (http-agent/http-agent url
70  
-          		  :method method
71  
-          		  :headers {"Authorization" (str "OAuth " (:access_token @oauth)) "Content-Type" "application/json"}
72  
-          		  :body (if (nil? body) 
73  
-          		    nil
74  
-          		    (json/json-str body)))
75  
-              json-response  (http-agent/string http-agnt)
76  
-          	  status (http-agent/status http-agnt)
77  
-          	  response (if (or (nil? json-response) (== (count json-response) 0)) nil (json/read-json json-response))]
78  
-          	(if (and (= status 401) (not retry))
79  
-          	  (do
80  
-                (when (:trace-messages @oauth) 
81  
-                  (println "Refreshing token"))
82  
-          	    (swap! oauth refresh-oauth)
83  
-          	    ; Try again, setting retry to true
84  
-          	    (raw-oauth-request oauth url method body true))
85  
-          	  (do 
86  
-          	    (when (:trace-messages @oauth) 
87  
-                  (println (str "OAuth " method " status is " status (when (not (nil? response)) (str " response is " response)))))
88  
-                response)))))
89  
-
90  
-(defn oauth-request
91  
-    "Low level OAuth request function - prepends instance URL"
92  
-    ([oauth url] (raw-oauth-request oauth (str (:instance_url @oauth) url) "GET" nil))
93  
-    ([oauth url method] (raw-oauth-request oauth (str (:instance_url @oauth) url) method nil))
94  
-    ([oauth url method body] (raw-oauth-request oauth (str (:instance_url @oauth) url) method body)))
95  
-
96  
-(defn wrap-oauth
97  
-  "Handles the OAuth protocol."
98  
-  [handler oauth-params]
99  
-  (params/wrap-params
100  
-    (session/wrap-session
101  
-        (fn [request]
102  
-            ; Is the request uri path the same as the redirect URI path?
103  
-            (if (= (:uri request) (.getPath (java-utils/as-url (:redirect-uri oauth-params))))
104  
-            	; We should have an authorization code - get the access token 
105  
-            	; and put it in the session, along with the list of string 
106  
-            	; fields for Account
107  
-                {:status 302
108  
-            	:headers {"Location" "/"}
109  
-            	:session (let [oauth (get-oauth oauth-params ((:params request) "code"))]
110  
-            	            ; ::oauth is an atom so we can modify it when we
111  
-            	            ; refresh the access token
112  
-            	            {::oauth (atom oauth)})}
113  
-             	(let [oauth (::oauth (:session request))]
114  
-             		(if (nil? oauth) 
115  
-             			; Redirect to OAuth authentication/authorization
116  
-             			{:status 302 :headers {"Location" (get-oauth-service-url oauth-params)}}
117  
-             			; Put the OAuth response on the request and invoke
118  
-             			; handler
119  
-             			(if-let [response (handler (assoc request :oauth oauth))]
120  
-             			    (if-let [session (response :session)]
121  
-                 			    (if-let [new-oauth (find response :oauth)]
122  
-                 			        ; Handler has put data in the session, and set oauth
123  
-                 			        ; merge it all together
124  
-                 			        (assoc response :session (merge (response :session) {::oauth (:oauth response)}))
125  
-                 			        ; Handler has put data in the session - add 
126  
-                 			        ; the oauth data to it
127  
-                 			        (assoc response :session (merge (response :session) {::oauth oauth})))
128  
-                 			    (if-let [new-oauth (find response :oauth)]
129  
-                 			        ; Handler has set oauth, but not changed session - merge
130  
-                 			        ; the new oauth into the session from the request
131  
-                 			        (assoc response :session (merge (request :session) {::oauth (:oauth response)}))
132  
-                 			        ; No change to session - our oauth data will
133  
-                 			        ; be fine. If we were to set it here, we would 
134  
-                 			        ; wipe out the existing session state!
135  
-                 			        response))))))))))
114  src/treajure/force.clj
... ...
@@ -1,114 +0,0 @@
1  
-(ns treajure.force
2  
-    "Force.com API built on treasure.core"
3  
-    (:require [ring.util.codec :as codec])
4  
-    (:use (treajure core)))
5  
-
6  
-; Macros currently get oauth token from current scope
7  
-; Can get oauth token from session - (:treajure.core/oauth ~'session)
8  
-
9  
-(defmacro id
10  
-    []
11  
-    `(id-helper ~'oauth))
12  
-
13  
-(defn id-helper
14  
-  "Given an OAuth response, return the id"
15  
-  [oauth]
16  
-  (raw-oauth-request 
17  
-      oauth
18  
-      (:id @oauth)))
19  
-      
20  
-(defmacro describe
21  
-    [obj-type]
22  
-    `(describe-helper ~'oauth ~obj-type))
23  
-
24  
-(defn describe-helper
25  
-  "Given an OAuth response, and object type, return the sobject metadata"
26  
-  [oauth obj-type]
27  
-  (oauth-request 
28  
-      oauth
29  
-      (str "/services/data/v22.0/sobjects/" obj-type "/describe/")))
30  
-                      
31  
-(defmacro query
32  
-  [query]
33  
-  `(query-helper ~'oauth ~query))
34  
-
35  
-(defn query-helper
36  
-    "Given an OAuth response, execute the supplied query and return the response"
37  
-    [oauth query]
38  
-    (oauth-request 
39  
-        oauth
40  
-        (str "/services/data/v22.0/query?q=" (codec/url-encode query))))
41  
-
42  
-(defmacro create
43  
-  [obj-type fields]
44  
-  `(create-helper ~'oauth ~obj-type ~fields))
45  
-
46  
-(defn create-helper 
47  
-    "Given an OAuth response, object type and fields, create the sobject, return status"
48  
-    [oauth obj-type fields]
49  
-    (oauth-request 
50  
-        oauth
51  
-        (str "/services/data/v22.0/sobjects/" obj-type)
52  
-        "POST"
53  
-        fields))
54  
-            			  
55  
-(defmacro retrieve
56  
-    ([obj-type id]
57  
-    `(retrieve-helper ~'oauth ~obj-type ~id))
58  
-    ([obj-type id field-list]
59  
-    `(retrieve-helper ~'oauth ~obj-type ~id ~field-list)))
60  
-
61  
-(defn retrieve-helper
62  
-    "Given an OAuth response, object type, id and optional comma-separated field list, return the sobject"
63  
-    ([oauth obj-type id]
64  
-    (oauth-request 
65  
-        oauth
66  
-        (str "/services/data/v22.0/sobjects/" obj-type "/" id)))
67  
-    ; TODO - make field list a vector?
68  
-    ([oauth obj-type id field-list]
69  
-    (oauth-request 
70  
-        oauth
71  
-        (str "/services/data/v22.0/sobjects/" obj-type "/" id "?fields=" field-list))))
72  
-
73  
-(defmacro update
74  
-  [obj-type id fields]
75  
-  `(update-helper ~'oauth ~obj-type ~id ~fields))
76  
-
77  
-(defn update-helper 
78  
-    "Given an OAuth response, object type and fields, update the sobject"
79  
-    ; Note - Java HttpURLConnection does not support PATCH (at least as of 
80  
-    ; Java SE 6), so use the _HttpMethod workaround
81  
-    [oauth obj-type id fields]
82  
-    (oauth-request 
83  
-        oauth
84  
-        (str "/services/data/v22.0/sobjects/" obj-type "/" id "?_HttpMethod=PATCH")
85  
-        "POST"
86  
-        fields))
87  
-
88  
-(defmacro upsert
89  
-  [obj-type externalIdField externalId fields]
90  
-  `(upsert-helper ~'oauth ~obj-type ~externalIdField ~externalId ~fields))
91  
-
92  
-(defn upsert-helper 
93  
-    "Given an OAuth response, object type, external id field, external id and 
94  
-    fields, upsert the sobject"
95  
-    ; Note - Java HttpURLConnection does not support PATCH (at least as of 
96  
-    ; Java SE 6), so use the _HttpMethod workaround
97  
-    [oauth obj-type externalIdField externalId fields]
98  
-    (oauth-request 
99  
-        oauth
100  
-        (str "/services/data/v22.0/sobjects/" obj-type "/" externalIdField "/" externalId "?_HttpMethod=PATCH")
101  
-        "POST"
102  
-        fields))
103  
-
104  
-(defmacro delete
105  
-    [obj-type id]
106  
-    `(delete-helper ~'oauth ~obj-type ~id))
107  
-
108  
-(defn delete-helper
109  
-    "Given an OAuth response, object type and id, delete the sobject"
110  
-    [oauth obj-type id]
111  
-    (oauth-request 
112  
-      oauth
113  
-      (str "/services/data/v22.0/sobjects/" obj-type "/" id)
114  
-      "DELETE"))

0 notes on commit 4235dda

Please sign in to comment.
Something went wrong with that request. Please try again.