Skip to content

Commit

Permalink
Make a specific Driver class and 'jdbc:looker:' protocol support
Browse files Browse the repository at this point in the history
  • Loading branch information
tjbanghart committed Aug 8, 2023
1 parent 0463fdf commit 761d6d6
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 53 deletions.
15 changes: 1 addition & 14 deletions core/src/main/java/org/apache/calcite/avatica/remote/Driver.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@
import org.apache.calcite.avatica.DriverVersion;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.UnregisteredDriver;
import org.apache.calcite.avatica.remote.looker.LookerRemoteMeta;
import org.apache.calcite.avatica.remote.looker.LookerRemoteService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -40,8 +38,6 @@
import java.util.Locale;
import java.util.Properties;

import static org.apache.calcite.avatica.remote.Driver.Serialization.LOOKER;

/**
* Avatica Remote JDBC driver.
*/
Expand All @@ -63,8 +59,7 @@ public Driver() {
*/
public enum Serialization {
JSON,
PROTOBUF,
LOOKER;
PROTOBUF;
}

@Override protected String getConnectStringPrefix() {
Expand Down Expand Up @@ -101,10 +96,6 @@ protected DriverVersion createDriverVersion() {
// Create a single Service and set it on the Connection instance
final Service service = createService(connection, config);
connection.setService(service);
// TODO: Perhaps `serialization` is the wrong place to add this.
if (getSerialization(config) == LOOKER) {
return new LookerRemoteMeta(connection, service);
}
return new RemoteMeta(connection, service);
}

Expand Down Expand Up @@ -140,10 +131,6 @@ Service createService(AvaticaConnection connection, ConnectionConfig config) {
case PROTOBUF:
service = new RemoteProtobufService(httpClient, new ProtobufTranslationImpl());
break;
// TODO: Perhaps `serialization` is the wrong place to add this.
case LOOKER:
service = new LookerRemoteService(config.url());
break;
default:
throw new IllegalArgumentException("Unhandled serialization type: " + serializationType);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.calcite.avatica.remote.looker;

import org.apache.calcite.avatica.AvaticaConnection;
import org.apache.calcite.avatica.DriverVersion;
import org.apache.calcite.avatica.Meta;
import org.apache.calcite.avatica.UnregisteredDriver;
import org.apache.calcite.avatica.remote.Service;
import org.apache.calcite.avatica.remote.looker.utils.LookerSdkFactory;

import com.looker.sdk.LookerSDK;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class Driver extends UnregisteredDriver {

static {
new Driver().register();
}

public Driver() {
super();
}

public static final String CONNECT_STRING_PREFIX = "jdbc:looker:";

@Override
protected DriverVersion createDriverVersion() {
return DriverVersion.load(
org.apache.calcite.avatica.remote.Driver.class,
"org-apache-calcite-jdbc.properties",
"Looker JDBC Driver",
"unknown version",
"Looker",
"unknown version");
}

@Override
protected String getConnectStringPrefix() {
return CONNECT_STRING_PREFIX;
}

@Override
public Meta createMeta(AvaticaConnection connection) {
final Service service = new LookerRemoteService();
connection.setService(service);
return new LookerRemoteMeta(connection, service);
}

@Override public Connection connect(String url, Properties info)
throws SQLException {
AvaticaConnection conn = (AvaticaConnection) super.connect(url, info);

if (conn == null) {
// It's not an url for our driver
return null;
}
Service service = conn.getService();
// the `looker` driver should always have a matching Service
assert service instanceof LookerRemoteService;
// create and set LookerSDK for the connection
LookerSDK sdk = LookerSdkFactory.createSdk(conn.config().url(), info);
((LookerRemoteService) service).setSdk(sdk);
return conn;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@
import org.apache.calcite.avatica.Meta.Signature;
import org.apache.calcite.avatica.remote.JsonService;
import org.apache.calcite.avatica.remote.looker.LookerRemoteMeta.LookerFrame;
import org.apache.calcite.avatica.remote.looker.utils.LookerSdkFactory;

import com.looker.sdk.JdbcInterface;
import com.looker.sdk.LookerSDK;
import com.looker.sdk.SqlQuery;
import com.looker.sdk.SqlQueryCreate;

import java.io.IOException;
import java.sql.SQLException;
import java.util.Arrays;

import static org.apache.calcite.avatica.remote.looker.utils.LookerSdkFactory.safeSdkCall;
Expand All @@ -37,23 +34,10 @@
* send Avatica request/responses to a Looker instance via JSON.
*/
public class LookerRemoteService extends JsonService {

private String url;
public LookerSDK sdk;

public LookerRemoteService(String url) {
super();
this.url = url;
}

/**
* Non-overridden {@code apply} methods hit the {@code jdbc_interface} endpoint of the instance.
* This endpoint behaves similarly to a standard Avatica server.
*/
@Override
public String apply(String request) {
JdbcInterface response = safeSdkCall(() -> sdk.jdbc_interface(request));
return response.getResults();
void setSdk(LookerSDK sdk) {
this.sdk = sdk;
}

/**
Expand All @@ -67,6 +51,19 @@ ExecuteResponse lookerExecuteResponse(PrepareAndExecuteRequest request, Signatur
return new ExecuteResponse(Arrays.asList(new ResultSetResponse[]{rs}), false, null);
}

/**
* Handles all non-overridden {@code apply} methods.
*
* Calls the {@code jdbc_interface} endpoint of the instance which behaves similarly to a standard
* Avatica server.
*/
@Override
public String apply(String request) {
assert null != sdk;
JdbcInterface response = safeSdkCall(() -> sdk.jdbc_interface(request));
return response.getResults();
}

/**
* Handles PrepareAndExecuteRequests by preparing a query via {@link LookerSDK#create_sql_query}
* whose response contains a slug. This slug is used to execute the query via
Expand All @@ -77,9 +74,10 @@ ExecuteResponse lookerExecuteResponse(PrepareAndExecuteRequest request, Signatur
*/
@Override
public ExecuteResponse apply(PrepareAndExecuteRequest request) {
assert null != sdk;
// TODO: b/288031194 - Remove this stubbed query once the Looker SQL endpoints exist.
// For dev we first prepare the query to get a signature and then create a query to run.
String prepSql = "SELECT\n" + " (FORMAT_TIMESTAMP('%F %T', `order_items.created_time` )) AS "
String prepSql = "SELECT\n" + " (FORMAT_DATE('%F %T', `order_items.created_date` )) AS "
+ "order_items_created_time, 'AHHHH' as testy, 10000 as num\n"
+ "FROM `thelook`.`order_items`\n" + " AS order_items\n" + "GROUP BY\n" + " 1\n"
+ "ORDER BY\n" + " 1 DESC";
Expand All @@ -97,19 +95,4 @@ public ExecuteResponse apply(PrepareAndExecuteRequest request) {
return lookerExecuteResponse(request, prepare.statement.signature,
LookerFrame.create(query.getSlug()));
}

/**
* Opening a connection initializes a {@link LookerSDK} to communicate with the Looker API.
*/
@Override
public OpenConnectionResponse apply(OpenConnectionRequest request) {
try {
sdk = LookerSdkFactory.createSdk(url, request.info);
return decode(apply(encode(request)), OpenConnectionResponse.class);
} catch (IOException e) {
throw handle(e);
} catch (SQLException e) {
throw LookerSdkFactory.handle(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package org.apache.calcite.avatica.remote.looker.utils;



import com.looker.rtl.AuthSession;
import com.looker.rtl.ConfigurationProvider;
import com.looker.rtl.SDKErrorInfo;
Expand All @@ -31,10 +32,13 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;

import static com.looker.rtl.TransportKt.ok;
import static com.looker.rtl.TransportKt.parseSDKError;

import static org.apache.calcite.avatica.remote.Service.OpenConnectionRequest.serializeProperties;

/**
* Utility class for generating, authenticating, and calling {@link LookerSDK}s.
*/
Expand All @@ -55,7 +59,6 @@ private LookerSdkFactory() {
* Simple functional interface to wrap SDK calls
*/
public interface LookerSDKCall {

SDKResponse call();
}

Expand Down Expand Up @@ -123,7 +126,7 @@ private static AuthSession createAuthSession(String url, Map<String, String> pro
apiConfig.put("client_id", props.get("user"));
apiConfig.put("client_secret", props.get("password"));
} else if (authToken) {
// TODO: Set the token for the session using `session.setAuthToken(AuthToken);`.
// TODO b/295025684: Set the token for the session using `session.setAuthToken(AuthToken);`.
// Doing so will allow us to rely on the same auth session for the stream query call.
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "token " + props.get("token"));
Expand All @@ -150,8 +153,9 @@ private static AuthSession createAuthSession(String url, Map<String, String> pro
* @param url the URL of the Looker instance.
* @param props map of properties for the session.
*/
public static LookerSDK createSdk(String url, Map<String, String> props) throws SQLException {
AuthSession session = createAuthSession(url, props);
public static LookerSDK createSdk(String url, Properties props) throws SQLException {
Map<String, String> stringProps = serializeProperties(props);
AuthSession session = createAuthSession(url, stringProps);
return new LookerSDK(session);
}
}
1 change: 1 addition & 0 deletions core/src/main/resources/META-INF/services/java.sql.Driver
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
org.apache.calcite.avatica.remote.Driver
org.apache.calcite.avatica.remote.looker.Driver
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ private LookerTestCommon() {
private static final String BASE_URL = "https://localhost:19999";
private static final String CLIENT_ID = "f6qG2zPw464yStBrJwrT";
private static final String CLIENT_SECRET = "KQTpGMPp5mWRQy2Mgrs4SdQT";
private static final String URL = "jdbc:avatica:remote:url=" + BASE_URL + ";serialization=LOOKER";
private static final String URL = "jdbc:looker:url=" + BASE_URL;

private static final Properties BASE_PROPS = new Properties();

Expand Down

0 comments on commit 761d6d6

Please sign in to comment.