Fork me on GitHub
#google-cloud
<
2021-02-17
>
domparry06:02:34

I think you need to call .build on your Builder after you set the jobId?

domparry06:02:57

So in your example @oliver.marks:

(.create s
         (-> (query)
             (JobInfo/newBuilder)
             (.setJobId (JobId/of (str (java.util.UUID/randomUUID))))
             (.build)))

domparry06:02:06

Not that I’ve used that before, but I just know from other Java GCP libs that use the builder pattern…

domparry06:02:05

For BQ we eventually opted for using the API’s directly.

domparry06:02:40

But there are other things you’ll need to do then, like parsing the super weird result structure (I can share our bad code if you like)

oly09:02:53

I would be interested, I did consider working direct with the api but had not seen anyone else doing this, all other code seems to work with the java api's I would like to see a gcp equivalent of the congnitech amazon api that would be nice 🙂

oly09:02:31

thanks for the tip, I had missed out the .build step I can now run the queries and gets some results 🙂

domparry09:02:12

I'll share some code when I land.

👍 3
domparry13:02:06

(declare row->clj)

(defn value->clj [field-schema type val]
  (when val
    (case type
      "STRING" val
      "FLOAT" (Float/parseFloat val)
      "INTEGER" (Long/parseLong val)
      "BOOLEAN" (Boolean/parseBoolean val)
      "DATETIME" (time.coerce/to-date (time.coerce/to-date-time val))
      "DATE" (time.coerce/to-date val)
      "TIMESTAMP" (java.util.Date. (-> (Double/parseDouble val)
                                       (.longValue)
                                       (* 1000)))

      "RECORD" (row->clj field-schema val))))
(defn field->clj [field-schema field]
  (let [value (:v field)
        type (:type field-schema)
        mode (:mode field-schema)
        value (if (= mode "REPEATED")
                (map #(value->clj field-schema type (:v %)) value)
                (value->clj field-schema type value))]
    {(keyword (:name field-schema)) value}))

(defn row->clj [row-schema row]
  (into {}
        (map field->clj (:fields row-schema) (:f row))))

(defn bq-response->clj [resp]
  (let [schema (:schema resp)
        rows (:rows resp)]
    (map #(row->clj schema %) rows)))

domparry13:02:17

This can certainly be made better. We haven’t had time to do some much needed refactoring yet though.

domparry13:02:17

used like this:

domparry13:02:32

(defn get-gcp-token []
  (let [credentials-client (-> (GoogleCredential/getApplicationDefault))
        _ (.refreshToken credentials-client)
        token (.getAccessToken credentials-client)]
    token))

(defn fetch-paged-bq-results [project job-id page-token]
  (let [token (get-gcp-token)
        response (-> (client/get (format "" project job-id)
                                 {:query-params {:pageToken page-token}
                                  :oauth-token token
                                  :throw-exceptions false})
                     :body
                     (json/decode true))
        page-token (:pageToken response)]
    (if (:error response)
      nil
      (if page-token
        (concat (fetch-paged-bq-results project (:jobId (:jobReference response)) page-token) (bq-response->clj response))
        (bq-response->clj response)))))

(defn execute-bq-query [project query]
  (let [token (get-gcp-token)
        response (-> (client/post (format "" project)
                                  {:body (json/encode {:query query
                                                       :useLegacySql false
                                                       :maxResults 100000
                                                       :timeoutMs 100000})
                                   :oauth-token token
                                   :throw-exceptions false})
                     :body
                     (json/decode true))
        page-token (:pageToken response)]
    (if (:error response)
      nil
      (if page-token
        (concat (fetch-paged-bq-results project (:jobId (:jobReference response)) page-token) (bq-response->clj response))
        (bq-response->clj response)))))

domparry13:02:36

running locally, you need to have run gcloud auth application-default login

oly14:02:13

@domparry thank you very much for that, looks like it could be made much nicer to work with than the java api

domparry14:02:26

yes. Much more repl friendly….

oly14:02:16

yeah exactly, I had been using this as a reference previously https://github.com/RakutenReady/hugsql-bq but using java

oly14:02:40

If i get my task complete I will try it out and probably use that going forward 🙂

oly14:02:58

@domparry just curious where does this come from ? GoogleCredential/getApplicationDefault is this from java ?

domparry14:02:52

Yes, that’s correct. From here: com.google.api.client.googleapis.auth.oauth2.GoogleCredential

👍 3