Giter Site home page Giter Site logo

google-apps-clj's Introduction

google-apps-clj

A Clojure library that wraps the Google Java API for different Google Applications.

This library is partially typed using core.typed. Run lein typed check to type-check the library's code.

Latest test results (Thanks, CircleCI!):

  • develop: Circle CI
  • master: Circle CI

Installing

If you're using Leiningen, just add this to your project.clj:

[google-apps-clj "0.6.1"]

If you are using ClojureScript, there's a dependency conflict (see #22) between Google Closure Compiler and the Google Apps API library , so you'll have to add an exclusion, like so:

[google-apps-clj "0.6.1" :exclusions [com.google.guava/guava-jdk5]]

Check out CHANGELOG.md to see what's been updated lately.

Upgrading from 0.5.3

The sheets v4 ns was substantially revised.

Upgrading from 0.4.x

Major breaking changes were made to the Google Drive code in v0.5.0 which will require updating most of your code that interacts with Google Drive. Please see CHANGELOG.md for details.

Currently supported APIs

  • Google OAuth 2.0
  • Google Drive
  • Google Spreadsheets (v3 and v4)
  • Google Calendar

Usage

In order to use any of these APIs, you must first use the Google OAuth 2.0 library to set up your credentials. All the APIs rely on the credentials received from setting up OAuth 2.0.

Obtaining OAuth 2 Credentials

  1. Log in to Google using the account you wish the credentials to be under

  2. Navigate to the Developer's Console

  3. Create a project and name it appropriately

  4. On the project dashboard, find the "Use Google APIs" card and click the link to "Enable and manage APIs". On the left nav bar, select the credentials tab. You can also go directly to https://console.developers.google.com/apis/credentials?project=your-project-id (substituting in for your-project-id, of course).

  5. Navigate to Credentials and click Create new Client ID under OAuth. Choose "Installed application" and set up a consent screen if necessary.

  6. Create a google-creds.edn file

  7. Copy the Client ID, Client Secret, and Redirect URIs into your google-creds.edn. You will use the data in this file for getting the rest of your credententials and for the other APIs.

  8. Read in your google-creds.edn file like so:

    (edn/read-string (slurp "config/google-creds.edn"))

  9. Call google-apps-clj.credentials/get-auth-map on this, along with the necessary OAuth scopes (eg ["https://www.googleapis.com/auth/drive" "https://docs.google.com/feeds/" "https://spreadsheets.google.com/feeds" "https://www.googleapis.com/auth/calendar"]), and read in data and follow its instructions

  10. Copy the returned data into your google-creds.edn file under the :auth-map key. Reload it into your REPL.

  11. You are now ready to use the other APIs with your credential file

Using Service Account Credentials

Google also supports Service Account credentials for server-to-server API access. The credential is provided as a JSON file containing a private key and some other data.

To obtain a service credential:

  1. Log in to Google using the account you wish the credentials to be under
  2. Navigate to the Developer's Console
  3. Create a project and name it appropriately
  4. Navigate to the API Manager, and enable the APIs you need an disable the default-provided ones you don't need
  5. Navigate to the Permissions > Service Accounts page
  6. Create a new service account, selecting the option to obtain a new private key as JSON.
  7. You should be given a JSON file containing the credential. You have a few options now:
  • Store it anywhere, and set an environment variable (GOOGLE_APPLICATION_CREDENTIALS) to point to it
  • On Mac/Linux, rename and move the JSON file to be ~/.config/gcloud/application_default_credentials.json (making directories as needed)
  • On Windows, rename and move the JSON file to be %APPDATA%\gcloud\application_default_credentials.json (making directories as needed)

Now you can use google-apps-clj.credentials/default-credential to load these credentials, and then pass them into most places that might otherwise expect a google-ctx. For example:

(let [scopes [com.google.api.services.drive.DriveScopes/DRIVE]
      creds (google-apps-clj.credentials/default-credential scopes)]
    (google-apps-clj.google-drive/list-files! creds "FOLDERIDFOLDERIDFOLDERIDFOLD"))

(note in this scenario that the service account user has to be given the appropriate permissions in Drive. The service account user won't show up by name in searches, but it can be added as a viewer/editor using its [email protected] email address as found in the JSON credential file)

Drive API

Supported Functionality
  • Searching
  • Fetching
  • Uploading
  • Updating
  • Deleting
  • Authorizing

Spreadsheet API

Supported Functionality
  • Creating a Worksheet inside of an existing Spreadsheet
  • Finding Spreadsheets by title and id, Worksheets by title and id, Cells by row column notation
  • Editing a Worksheet's rows, columns, and title
  • Editing individual cells in a Worksheet
  • Editing rows in a worksheet
  • Batch-updating large quantity of cells (improves performance)
  • Reading an entire Worksheet's values or headers, or both
  • Overwriting an entire Worksheet with a new set of data (destroys old data)

Calendar API

Supported Functionality
  • Listing all events within a certain date-time range (for user)
  • Listing all events on a given day (for user)
  • Listing upcoming events with a supplied name (for user)
  • Creating an event with a certain time range
  • Creating an all day event

What's Next?

General

  • Consider ditching the baroque Google java library in favor of direct integration with the api using clj-http or the like

Drive API

  • Consider making file maps the unit of work for the command fns
  • Or possibly an Identifiable protocol to allow file ids or maps

Sheets API

  • Consider a cleaner abstraction instead of a grab bag of fns

Calendar API

License

Copyright © SparkFund 2015-2017

Distributed under the Apache License, Version 2.0. See LICENSE.txt for details.

google-apps-clj's People

Contributors

dball avatar jffry avatar k0pak4 avatar takeoutweight avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

google-apps-clj's Issues

Authentication Inconsistent

google-apps-clj.credentials/get-auth-map returns a map with string keys, whereas google-ctx expects keyword keys

Missing functions/docs?

I decided to try out google-drive, and managed to get the "context", but there seem to be some holes in the functionality and/or docs:

How do you list files/folders?
How do you get a parent-folder-id or file-id?

Integration with google sheets started to fail recently

Using latest 0.6.1 and notice that few days ago, posting to gsheets become broken. Accessing spreadsheet by title (or by id) fails with NOT_FOUND even if sheet exists:


  Show: Project-Only All 
  Hide: Clojure Java REPL Tooling Duplicates  (13 frames hidden)

1. Unhandled com.google.gdata.util.ResourceNotFoundException
   Not Found

     HttpGDataRequest.java:  599  com.google.gdata.client.http.HttpGDataRequest/handleErrorResponse
   GoogleGDataRequest.java:  564  com.google.gdata.client.http.GoogleGDataRequest/handleErrorResponse
     HttpGDataRequest.java:  560  com.google.gdata.client.http.HttpGDataRequest/checkResponse
     HttpGDataRequest.java:  538  com.google.gdata.client.http.HttpGDataRequest/execute
   GoogleGDataRequest.java:  536  com.google.gdata.client.http.GoogleGDataRequest/execute
              Service.java: 1352  com.google.gdata.client.Service/getEntry
        GoogleService.java:  581  com.google.gdata.client.GoogleService/getEntry
         google_sheets.clj:   64  google-apps-clj.google-sheets/find-spreadsheet-by-id
         google_sheets.clj:   57  google-apps-clj.google-sheets/find-spreadsheet-by-id
                   api.clj:   77  shapdesk.integrations.gsheets.api/init-gsheet
                   api.clj:   73  shapdesk.integrations.gsheets.api/init-gsheet
                      REPL:  214  shapdesk.integrations.gsheets.api/eval45115
                      REPL:  214  shapdesk.integrations.gsheets.api/eval45115
             Compiler.java: 7177  clojure.lang.Compiler/eval
             Compiler.java: 7132  clojure.lang.Compiler/eval
                  core.clj: 3214  clojure.core/eval
                  core.clj: 3210  clojure.core/eval
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn/fn
                  AFn.java:  152  clojure.lang.AFn/applyToHelper
                  AFn.java:  144  clojure.lang.AFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj: 1973  clojure.core/with-bindings*
                  core.clj: 1973  clojure.core/with-bindings*
               RestFn.java:  425  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   87  nrepl.middleware.interruptible-eval/evaluate/fn
                  main.clj:  437  clojure.main/repl/read-eval-print/fn
                  main.clj:  437  clojure.main/repl/read-eval-print
                  main.clj:  458  clojure.main/repl/fn
                  main.clj:  458  clojure.main/repl
                  main.clj:  368  clojure.main/repl
               RestFn.java:  137  clojure.lang.RestFn/applyTo
                  core.clj:  665  clojure.core/apply
                  core.clj:  660  clojure.core/apply
                regrow.clj:   18  refactor-nrepl.ns.slam.hound.regrow/wrap-clojure-repl/fn
               RestFn.java: 1523  clojure.lang.RestFn/invoke
    interruptible_eval.clj:   84  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:   56  nrepl.middleware.interruptible-eval/evaluate
    interruptible_eval.clj:  152  nrepl.middleware.interruptible-eval/interruptible-eval/fn/fn
                  AFn.java:   22  clojure.lang.AFn/run
               session.clj:  202  nrepl.middleware.session/session-exec/main-loop/fn
               session.clj:  201  nrepl.middleware.session/session-exec/main-loop
                  AFn.java:   22  clojure.lang.AFn/run
               Thread.java:  748  java.lang.Thread/run

Wrong type annotation for `:redirect-uris` in `google-apps-clj.credentials/GoogleCtx`

It is t/Seq but should be the more general t/Seqable (which allows non-seq ordered lists, such as vec)

clojure.lang.ExceptionInfo:
Value does not match schema: (named (named {:redirect-uris (not (instance? clojure.lang.ISeq a-clojure.lang.PersistentVector))} google-apps-clj.credentials/GoogleCtx) [spark.file-interface/load-gdrive-ctx :range])

Uploading small files is slow

Uploading tiny files can be very slow. Here we upload a 1081-byte JSON file, with conversion to a Google Doc explicitly disabled:

(time
  (let [auth (google-apps-clj.credentials/default-credential ["https://www.googleapis.com/auth/drive"])
        folder-id "FOLDERIDFOLDERIDFOLDERIDFOLD"
        raw-req (google-apps-clj.google-drive/upload-file folder-id "test.json" "desc" "text/plain" "/path/to/test.json" false)
        req (assoc raw-req :fields ["id"])]
    (google-apps-clj.google-drive/execute-query! auth req)))

"Elapsed time: 2312.169 msecs"

It seems that by default, the Java client library uses resumable media uploads, which will upload the file using multiple requests: https://developers.google.com/api-client-library/java/google-api-java-client/media-upload#direct

For tiny files, we don't need this. In ee4931b I added an option to support direct media uploads. For this small file I ran some tests both with and without direct upload enabled, to see the response times in ms:

without with
943.888 1027.120
2420.539 1615.611
1102.272 624.711
800.267 625.270
2845.130 668.712
869.747 689.082
753.111 691.276
4449.890 744.944
2416.990 834.301
1096.242 908.461
---------- ---------- -------
1769.808 842.949 mean
1226.861 301.284 stdev

While this is a big improvement, a mean of 842ms to upload a 1081-byte file is still a bit too large and leaves room to improve.

Example?

Would it be possible to add a small usage example? For instance, how to you read a sheet, or write a new line in it?

Thanks!

Error when adding new rows

Hi!
When I try add rows

(def row ["2016-11-24" "text" "text" "" "text" "2" "1" "100" "1" "2" "4" "1"])
(def rows [row])
(google-apps-clj.google-sheets-v4/write-sheet service spreadsheet-id sheet-id rows)

I get the following error

GoogleJsonResponseException 400 Bad Request
{
  "code" : 400,
  "errors" : [ {
    "domain" : "global",
    "message" : "Invalid requests[4].appendCells: GridCoordinate.rowIndex[1] is after last row in grid[0]",
    "reason" : "badRequest"
  } ],
  "message" : "Invalid requests[4].appendCells: GridCoordinate.rowIndex[1] is after last row in grid[0]",
  "status" : "INVALID_ARGUMENT"
}  com.google.api.client.googleapis.json.GoogleJsonResponseException.from (GoogleJsonResponseException.java:146)

What am I doing wrong?

read only some cols

It would be nice to be able to load only certain columns.

(read-worksheet google-ctx spreadsheet-id worksheet-id {:filter-cols ["col name" "col other name"]})

The filter done by google seems better than the filter done by our servers.

Possible?

Jetty library dep from `com.google.gdata/core` can conflict with other libraries

For whatever reason, com.google.gdata/core includes some Jetty stuff which can get in the way and is only there for (apparently) some baked in demo stuff.

Let's add it to the :exclusions so that we aren't re-exporting a needless dependency.

$ lein deps :tree
 [clojure-complete "0.2.4" :exclusions [[org.clojure/clojure]]]
 [com.google.apis/google-api-services-calendar "v3-rev128-1.20.0"]
   [com.google.api-client/google-api-client "1.20.0"]
     [com.google.guava/guava-jdk5 "13.0"]
     [com.google.http-client/google-http-client-jackson2 "1.20.0"]
       [com.fasterxml.jackson.core/jackson-core "2.1.3"]
     [com.google.oauth-client/google-oauth-client "1.20.0"]
       [com.google.code.findbugs/jsr305 "1.3.9"]
       [com.google.http-client/google-http-client "1.20.0"]
         [org.apache.httpcomponents/httpclient "4.0.1"]
           [commons-codec "1.3"]
           [commons-logging "1.1.1"]
           [org.apache.httpcomponents/httpcore "4.0.1"]
 [com.google.apis/google-api-services-drive "v2-rev168-1.20.0"]
 [com.google.gdata/core "1.47.1" :exclusions [[org.apache.httpcomponents/httpclient] [com.google.code.findbugs/jsr305]]]
   [com.google.guava/guava "13.0.1"]
   [com.google.oauth-client/google-oauth-client-jetty "1.11.0-beta"]
     [com.google.oauth-client/google-oauth-client-java6 "1.11.0-beta"]
     [org.mortbay.jetty/jetty "6.1.26"]
       [org.mortbay.jetty/jetty-util "6.1.26"]
       [org.mortbay.jetty/servlet-api "2.5-20081211"]
   [javax.mail/mail "1.4"]
     [javax.activation/activation "1.1"]
 [org.clojure/clojure "1.7.0"]
 [org.clojure/core.typed "0.3.14"]
   [org.clojure/core.typed.rt "0.3.14"]
 [org.clojure/tools.nrepl "0.2.12" :exclusions [[org.clojure/clojure]]]

Unhandled java.net.SocketTimeoutException despite setting :read-timeout and :connect-timeout

Read timeout error for a large sheet (3k records, 120 columns). Setting :read-timeout and :connection-timeout in google-ctx not changing anything. Values set to 560000 but it still times-out after about 15s.

  1. Unhandled java.net.SocketTimeoutException
    Read timed out

     NioSocketImpl.java:  283  sun.nio.ch.NioSocketImpl/timedRead
     NioSocketImpl.java:  309  sun.nio.ch.NioSocketImpl/implRead
     NioSocketImpl.java:  350  sun.nio.ch.NioSocketImpl/read
     NioSocketImpl.java:  803  sun.nio.ch.NioSocketImpl$1/read
            Socket.java:  982  java.net.Socket$SocketInputStream/read
    

SSLSocketInputRecord.java: 469 sun.security.ssl.SSLSocketInputRecord/read
SSLSocketInputRecord.java: 463 sun.security.ssl.SSLSocketInputRecord/readHeader
SSLSocketInputRecord.java: 70 sun.security.ssl.SSLSocketInputRecord/bytesInCompletePacket
SSLSocketImpl.java: 1421 sun.security.ssl.SSLSocketImpl/readApplicationRecord
SSLSocketImpl.java: 1033 sun.security.ssl.SSLSocketImpl$AppInputStream/read
BufferedInputStream.java: 244 java.io.BufferedInputStream/fill
BufferedInputStream.java: 284 java.io.BufferedInputStream/read1
BufferedInputStream.java: 343 java.io.BufferedInputStream/read
HttpClient.java: 754 sun.net.www.http.HttpClient/parseHTTPHeader
HttpClient.java: 689 sun.net.www.http.HttpClient/parseHTTP
HttpURLConnection.java: 1623 sun.net.www.protocol.http.HttpURLConnection/getInputStream0
HttpURLConnection.java: 1528 sun.net.www.protocol.http.HttpURLConnection/getInputStream
HttpURLConnection.java: 527 java.net.HttpURLConnection/getResponseCode
HttpsURLConnectionImpl.java: 308 sun.net.www.protocol.https.HttpsURLConnectionImpl/getResponseCode
NetHttpResponse.java: 37 com.google.api.client.http.javanet.NetHttpResponse/
NetHttpRequest.java: 94 com.google.api.client.http.javanet.NetHttpRequest/execute
HttpRequest.java: 981 com.google.api.client.http.HttpRequest/execute
AbstractGoogleClientRequest.java: 419 com.google.api.client.googleapis.services.AbstractGoogleClientRequest/executeUnparsed
AbstractGoogleClientRequest.java: 352 com.google.api.client.googleapis.services.AbstractGoogleClientRequest/executeUnparsed
AbstractGoogleClientRequest.java: 469 com.google.api.client.googleapis.services.AbstractGoogleClientRequest/execute
google_sheets_v4.clj: 391 google-apps-clj.google-sheets-v4/get-cells
google_sheets_v4.clj: 375 google-apps-clj.google-sheets-v4/get-cells
google_sheets_v4.clj: 410 google-apps-clj.google-sheets-v4/get-cell-values
google_sheets_v4.clj: 404 google-apps-clj.google-sheets-v4/get-cell-values

Conflicts with cljsbuild & advanced optimizations - NoSuchMethodError

Steps to reproduce:

  1. Create new project with lein new figwheel <projectname>
  2. lein cljsbuild once min
  3. Stacktrace:
Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.io.ByteStreams.limit(Ljava/io/InputStream;J)Ljava/io/InputStream;, compiling:(/private/var/folders/rh/9n_lh65n70j80h07nk70f67h0000gn/T/form-init9121652322381704151.clj:1:125)
	at clojure.lang.Compiler.load(Compiler.java:7391)
	at clojure.lang.Compiler.loadFile(Compiler.java:7317)
	at clojure.main$load_script.invokeStatic(main.clj:275)
	at clojure.main$init_opt.invokeStatic(main.clj:277)
	at clojure.main$init_opt.invoke(main.clj:277)
	at clojure.main$initialize.invokeStatic(main.clj:308)
	at clojure.main$null_opt.invokeStatic(main.clj:342)
	at clojure.main$null_opt.invoke(main.clj:339)
	at clojure.main$main.invokeStatic(main.clj:421)
	at clojure.main$main.doInvoke(main.clj:384)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at clojure.lang.Var.invoke(Var.java:383)
	at clojure.lang.AFn.applyToHelper(AFn.java:156)
	at clojure.lang.Var.applyTo(Var.java:700)
	at clojure.main.main(main.java:37)
Caused by: java.lang.NoSuchMethodError: com.google.common.io.ByteStreams.limit(Ljava/io/InputStream;J)Ljava/io/InputStream;
	at com.google.javascript.jscomp.AbstractCommandLineRunner.getBuiltinExterns(AbstractCommandLineRunner.java:486)
	at com.google.javascript.jscomp.CommandLineRunner.getDefaultExterns(CommandLineRunner.java:1482)
	at cljs.closure$load_externs.invokeStatic(closure.clj:327)
	at cljs.closure$load_externs.invoke(closure.clj:292)
	at cljs.closure$optimize.invokeStatic(closure.clj:1265)
	at cljs.closure$optimize.doInvoke(closure.clj:1259)
	at clojure.lang.RestFn.applyTo(RestFn.java:139)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$apply.invoke(core.clj:641)
	at cljs.closure$build.invokeStatic(closure.clj:2020)
	at cljs.closure$build.invoke(closure.clj:1927)
	at cljs.build.api$build.invokeStatic(api.clj:199)
	at cljs.build.api$build.invoke(api.clj:187)
	at cljs.build.api$build.invokeStatic(api.clj:190)
	at cljs.build.api$build.invoke(api.clj:187)
	at cljsbuild.compiler$compile_cljs$fn__24607.invoke(compiler.clj:60)
	at cljsbuild.compiler$compile_cljs.invokeStatic(compiler.clj:59)
	at cljsbuild.compiler$compile_cljs.invoke(compiler.clj:48)
	at cljsbuild.compiler$run_compiler.invokeStatic(compiler.clj:168)
	at cljsbuild.compiler$run_compiler.invoke(compiler.clj:122)
	at user$eval24744$iter__24780__24784$fn__24785$fn__24803.invoke(form-init9121652322381704151.clj:1)
	at user$eval24744$iter__24780__24784$fn__24785.invoke(form-init9121652322381704151.clj:1)
	at clojure.lang.LazySeq.sval(LazySeq.java:40)
	at clojure.lang.LazySeq.seq(LazySeq.java:49)
	at clojure.lang.RT.seq(RT.java:521)
	at clojure.core$seq__4357.invokeStatic(core.clj:137)
	at clojure.core$dorun.invokeStatic(core.clj:3024)
	at clojure.core$doall.invokeStatic(core.clj:3039)
	at clojure.core$doall.invoke(core.clj:3039)
	at user$eval24744.invokeStatic(form-init9121652322381704151.clj:1)
	at user$eval24744.invoke(form-init9121652322381704151.clj:1)
	at clojure.lang.Compiler.eval(Compiler.java:6927)
	at clojure.lang.Compiler.eval(Compiler.java:6917)
	at clojure.lang.Compiler.load(Compiler.java:7379)
	... 14 more
Subprocess failed

Output of lein deps :tree :

 [binaryage/devtools "0.8.2" :scope "test"]
 [cider/cider-nrepl "0.15.0-20161014.121923-2"]
   [org.tcrawley/dynapath "0.2.4" :exclusions [[org.clojure/clojure]]]
 [clojure-complete "0.2.4" :exclusions [[org.clojure/clojure]]]
 [com.cemerick/piggieback "0.2.1" :scope "test"]
 [figwheel-sidecar "0.5.8" :scope "test"]
   [clj-stacktrace "0.2.8" :scope "test"]
   [com.stuartsierra/component "0.3.1" :scope "test"]
     [com.stuartsierra/dependency "0.2.0" :scope "test"]
   [figwheel "0.5.8" :scope "test" :exclusions [[org.clojure/tools.reader]]]
   [hawk "0.2.10" :scope "test" :exclusions [[org.clojure/clojure]]]
     [net.incongru.watchservice/barbary-watchservice "1.0" :scope "test"]
       [net.java.dev.jna/jna "3.2.2" :scope "test"]
   [http-kit "2.2.0" :scope "test"]
   [ring-cors "0.1.8" :scope "test" :exclusions [[ring/ring-core] [org.clojure/clojure]]]
   [ring/ring-core "1.5.0" :scope "test" :exclusions [[org.clojure/tools.reader] [org.clojure/clojure]]]
     [clj-time "0.11.0" :scope "test"]
       [joda-time "2.8.2" :scope "test"]
     [commons-fileupload "1.3.1" :scope "test"]
     [commons-io "2.5" :scope "test"]
     [crypto-equality "1.0.0" :scope "test"]
     [crypto-random "1.2.0" :scope "test"]
     [ring/ring-codec "1.0.1" :scope "test"]
       [commons-codec "1.6"]
   [simple-lein-profile-merge "0.1.4" :scope "test"]
   [strictly-specking-standalone "0.1.1" :scope "test"]
     [net.cgrand/parsley "0.9.3" :scope "test" :exclusions [[org.clojure/clojure]]]
       [net.cgrand/regex "1.1.0" :scope "test"]
     [net.cgrand/sjacket "0.1.1" :scope "test" :exclusions [[org.clojure/clojure] [net.cgrand/parsley]]]
 [google-apps-clj "0.5.2"]
   [com.google.apis/google-api-services-calendar "v3-rev128-1.20.0"]
     [com.google.api-client/google-api-client "1.20.0"]
       [com.google.guava/guava-jdk5 "13.0"]
       [com.google.http-client/google-http-client-jackson2 "1.20.0"]
         [com.fasterxml.jackson.core/jackson-core "2.1.3"]
       [com.google.oauth-client/google-oauth-client "1.20.0"]
         [com.google.http-client/google-http-client "1.20.0"]
           [org.apache.httpcomponents/httpclient "4.0.1"]
             [commons-logging "1.1.1"]
             [org.apache.httpcomponents/httpcore "4.0.1"]
   [com.google.apis/google-api-services-drive "v2-rev168-1.20.0"]
   [com.google.gdata/core "1.47.1" :exclusions [[org.apache.httpcomponents/httpclient] [org.mortbay.jetty/jetty] [com.google.code.findbugs/jsr305]]]
     [com.google.oauth-client/google-oauth-client-jetty "1.11.0-beta"]
       [com.google.oauth-client/google-oauth-client-java6 "1.11.0-beta"]
     [javax.mail/mail "1.4"]
       [javax.activation/activation "1.1"]
   [org.clojure/core.typed "0.3.14"]
     [org.clojure/core.typed.rt "0.3.14"]
 [org.clojure/clojure "1.8.0"]
 [org.clojure/clojurescript "1.9.229"]
   [com.google.javascript/closure-compiler-unshaded "v20160315"]
     [args4j "2.0.26"]
     [com.google.code.findbugs/jsr305 "1.3.9"]
     [com.google.code.gson/gson "2.2.4"]
     [com.google.guava/guava "19.0"]
     [com.google.javascript/closure-compiler-externs "v20160315"]
     [com.google.protobuf/protobuf-java "2.5.0"]
   [org.clojure/data.json "0.2.6"]
   [org.clojure/google-closure-library "0.0-20160609-f42b4a24"]
     [org.clojure/google-closure-library-third-party "0.0-20160609-f42b4a24"]
   [org.clojure/tools.reader "1.0.0-beta3"]
   [org.mozilla/rhino "1.7R5"]
 [org.clojure/core.async "0.2.391" :exclusions [[org.clojure/tools.reader]]]
   [org.clojure/tools.analyzer.jvm "0.6.10"]
     [org.clojure/core.memoize "0.5.9"]
       [org.clojure/core.cache "0.6.5"]
         [org.clojure/data.priority-map "0.0.7"]
     [org.clojure/tools.analyzer "0.6.9"]
     [org.ow2.asm/asm-all "4.2"]
 [org.clojure/tools.nrepl "0.2.12" :exclusions [[org.clojure/clojure]]]

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.