Skip to content

Commit 74beab3

Browse files
committed
init
1 parent 8ef26f6 commit 74beab3

File tree

5 files changed

+225
-0
lines changed

5 files changed

+225
-0
lines changed

project.clj

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
(defproject metabase/dynamodb-driver "0.1.0-SNAPSHOT"
2+
:min-lein-version "2.5.0"
3+
:dependencies [[com.amazonaws/aws-java-sdk-dynamodb "1.11.563"]]
4+
5+
:jvm-opts
6+
["-XX:+IgnoreUnrecognizedVMOptions"]
7+
:prep-tasks ["javac" "compile"]
8+
9+
:profiles
10+
{:provided
11+
{:dependencies
12+
[[org.clojure/clojure "1.10.0"]
13+
[metabase-core "1.0.0-SNAPSHOT"]]}
14+
15+
:dev [:project/dev :profiles/dev]
16+
:profiles/dev {:main ^:skip-aot metabase.driver.dynamodb
17+
18+
}
19+
:project/dev {:dependencies
20+
[[eftest "0.5.7"]]
21+
:source-paths ["dev/src"]
22+
:resource-paths ["dev/resources"]}
23+
:uberjar
24+
{:auto-clean true
25+
:aot :all
26+
:omit-source true
27+
:javac-options ["-target" "1.8"]
28+
:target-path "target/%s"
29+
:uberjar-name "dynamodb.metabase-driver.jar"}})

resources/metabase-plugin.yaml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
info:
2+
name: Metabase DynamoDB Driver
3+
version: 0.1.0-SNAPSHOT
4+
description: Allows Metabase to connect to Amazon DynamoDB
5+
driver:
6+
name: dynamodb
7+
lazy-load: true
8+
display-name: DynamoDB
9+
connection-properties:
10+
- name: endpoint
11+
display-name: Endpoint
12+
- name: region-id
13+
display-name: Region ID
14+
- name: access-key-id
15+
display-name: Access Key ID
16+
required: false
17+
- name: secret-access-key
18+
display-name: Secret Access Key
19+
required: false
20+
init:
21+
- step: load-namespace
22+
namespace: metabase.driver.dynamodb

src/metabase/driver/dynamodb.clj

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
(ns metabase.driver.dynamodb
2+
(:refer-clojure :exclude [second])
3+
(:require [metabase.driver :as driver]
4+
[metabase.driver.common :as driver.common]
5+
[metabase.util
6+
[date :as du]]
7+
[metabase.query-processor.store :as qp.store]
8+
[metabase.driver.dynamodb
9+
[query-processor :as qp]
10+
[util :refer [with-dynamodb-client]]]))
11+
12+
(driver/register! :dynamodb)
13+
14+
(defmethod driver/display-name :dynamodb [_]
15+
"DynamoDB")
16+
17+
(defmethod driver/can-connect? :dynamodb [_ details]
18+
true)
19+
20+
(defmethod driver/describe-database :dynamodb [_ database]
21+
(with-dynamodb-client [_ database]
22+
{:tables (set (for [tname (qp/list-tables)]
23+
{:schema nil, :name tname}))}))
24+
25+
(defmethod driver/process-query-in-context :dynamodb [_ qp]
26+
(fn [{database-id :database, :as query}]
27+
(with-dynamodb-client [_ (qp.store/database)]
28+
(qp query))))
29+
30+
(defmethod driver/describe-table :dynamodb [_ database {table-name :name}]
31+
(with-dynamodb-client [_ database]
32+
{:schema nil
33+
:name table-name
34+
:fields (set (qp/describe-table table-name))}))
35+
36+
(defmethod driver/mbql->native :dynamodb [_ query]
37+
(qp/mbql->native query))
38+
39+
(defmethod driver/execute-query :dynamodb [_ query]
40+
(qp/execute-query query))
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
(ns metabase.driver.dynamodb.query-processor
2+
(:require [clojure.string :as str]
3+
[metabase.mbql
4+
[schema :as mbql.s]
5+
[util :as mbql.u]]
6+
[metabase.models.field :refer [Field]]
7+
[metabase.query-processor
8+
[interface :as i]
9+
[store :as qp.store]]
10+
[metabase.driver.dynamodb.util :refer [*dynamodb-client*]])
11+
(:import metabase.models.field.FieldInstance))
12+
13+
(def ^:dynamic ^:private *query* nil)
14+
15+
(defn list-tables []
16+
(-> (.listTables *dynamodb-client*)
17+
(.getTableNames)))
18+
19+
(defn- dynamodb-type->base-type [attr-type]
20+
(case attr-type
21+
"N" :type/Decimal
22+
"S" :type/Text
23+
"BOOL" :type/Boolean
24+
:type/*))
25+
26+
(defn describe-table [table]
27+
(let [table-desc (-> (.describeTable *dynamodb-client* table)
28+
(.getTable))]
29+
(println "describe-table" table-desc)
30+
(for [attribute-def (.getAttributeDefinitions table-desc)]
31+
{:name (.getAttributeName attribute-def)
32+
:database-type (.getAttributeType attribute-def)
33+
:base-type (dynamodb-type->base-type (.getAttributeType attribute-def))})) )
34+
35+
;;
36+
;;
37+
;;
38+
(defmulti ^:private ->rvalue
39+
"Format this `Field` or value for use as the right hand value of an expression, e.g. by adding `$` to a `Field`'s
40+
name"
41+
{:arglists '([x])}
42+
mbql.u/dispatch-by-clause-name-or-class)
43+
44+
(defmulti ^:private ->lvalue
45+
"Return an escaped name that can be used as the name of a given Field."
46+
{:arglists '([field])}
47+
mbql.u/dispatch-by-clause-name-or-class)
48+
49+
(defmulti ^:private ->initial-rvalue
50+
"Return the rvalue that should be used in the *initial* projection for this `Field`."
51+
{:arglists '([field])}
52+
mbql.u/dispatch-by-clause-name-or-class)
53+
54+
55+
(defn- field->name
56+
"Return a single string name for FIELD. For nested fields, this creates a combined qualified name."
57+
^String [^FieldInstance field, ^String separator]
58+
(if-let [parent-id (:parent_id field)]
59+
(str/join separator [(field->name (qp.store/field parent-id) separator)
60+
(:name field)])
61+
(:name field)))
62+
63+
(defmethod ->lvalue (class Field) [this] (field->name this "___"))
64+
(defmethod ->initial-rvalue (class Field) [this] (str \$ (field->name this ".")))
65+
(defmethod ->rvalue (class Field) [this] (str \$ (->lvalue this)))
66+
67+
(defmethod ->lvalue :field-id [[_ field-id]] (->lvalue (qp.store/field field-id)))
68+
(defmethod ->initial-rvalue :field-id [[_ field-id]] (->initial-rvalue (qp.store/field field-id)))
69+
(defmethod ->rvalue :field-id [[_ field-id]] (->rvalue (qp.store/field field-id)))
70+
71+
(defn- handle-fields [{:keys [fields]} pipeline-ctx]
72+
(println "handle-fields" fields)
73+
(if-not (seq fields)
74+
pipeline-ctx
75+
(let [new-projections (for [field fields]
76+
[(->lvalue field) (->rvalue field)])]
77+
(-> pipeline-ctx
78+
(assoc :projections (map (comp keyword first) new-projections))
79+
(update :query conj (into {} new-projections))))))
80+
81+
(defn mbql->native [{{source-table-id :source-table} :query, :as query}]
82+
(let [{source-table-name :name} (qp.store/table source-table-id)]
83+
(binding [*query* query]
84+
(println "mbql->native:" query)
85+
{:projections nil
86+
:query (reduce (fn [pipeline-ctx f]
87+
(f (:query query) pipeline-ctx))
88+
{:projections [], :query []}
89+
[handle-fields])
90+
:collection nil
91+
:mbql? true})))
92+
93+
(defn execute-query
94+
[{{:keys [collection query mbql? projections]} :native}]
95+
(println "execute-query:" query)
96+
{:rows []})

src/metabase/driver/dynamodb/util.clj

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
(ns metabase.driver.dynamodb.util
2+
(:import [com.amazonaws.services.dynamodbv2 AmazonDynamoDBClientBuilder AmazonDynamoDB]
3+
[com.amazonaws.client.builder AwsClientBuilder AwsClientBuilder$EndpointConfiguration]
4+
[com.amazonaws.auth AWSStaticCredentialsProvider BasicAWSCredentials]
5+
6+
))
7+
8+
(def ^:dynamic ^AmazonDynamoDB *dynamodb-client* nil)
9+
10+
(defn -with-dynamodb-client
11+
[f database]
12+
(let [builder (AmazonDynamoDBClientBuilder/standard)
13+
region-id (get-in database [:details :region-id])
14+
access-key-id (get-in database [:details :access-key-id])
15+
secret-access-key (get-in database [:details :secret-access-key])]
16+
17+
(if-let [endpoint (get-in database [:details :endpoint])]
18+
(.withEndpointConfiguration builder (AwsClientBuilder$EndpointConfiguration. endpoint region-id))
19+
(when region-id
20+
(.withRegion builder region-id)))
21+
22+
(when (and access-key-id secret-access-key)
23+
(.withCredentials builder (AWSStaticCredentialsProvider. (BasicAWSCredentials. access-key-id secret-access-key))))
24+
25+
(when-let [client (.build builder)]
26+
(try
27+
(binding [*dynamodb-client* client]
28+
(f *dynamodb-client*))
29+
(finally (.shutdown client))))))
30+
31+
(defmacro with-dynamodb-client
32+
"Open a new DynamoDB client"
33+
[[binding database] & body]
34+
`(let [f# (fn [~binding]
35+
~@body)]
36+
(if *dynamodb-client*
37+
(f# *dynamodb-client*)
38+
(-with-dynamodb-client f# ~database))))

0 commit comments

Comments
 (0)