package com.thinkbiganalytics.hive.rest.controller;
/*-
* #%L
* thinkbig-thrift-proxy-controller
* %%
* Copyright (C) 2017 ThinkBig Analytics
* %%
* Licensed 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.
* #L%
*/
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.thinkbiganalytics.discovery.schema.DatabaseMetadata;
import com.thinkbiganalytics.discovery.schema.QueryResult;
import com.thinkbiganalytics.discovery.schema.TableSchema;
import com.thinkbiganalytics.hive.service.HiveMetastoreService;
import com.thinkbiganalytics.hive.service.HiveService;
import com.thinkbiganalytics.rest.model.RestResponseStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.dao.DataAccessException;
import java.security.AccessControlException;
import java.sql.SQLException;
import java.util.List;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import io.swagger.annotations.SwaggerDefinition;
import io.swagger.annotations.Tag;
@Api(tags = "Feed Manager - Tables", produces = "application/json")
@Path(HiveRestController.BASE)
@SwaggerDefinition(tags = @Tag(name = "Feed Manager - Tables", description = "access to Hive tables"))
public class HiveRestController {
private static final Logger log = LoggerFactory.getLogger(HiveRestController.class);
public static final String BASE = "/v1/hive";
@Autowired
private Environment env;
@Autowired
private HiveService hiveService;
@Autowired
private HiveMetastoreService hiveMetadataService;
@GET
@Path("/test-connection")
@Produces(MediaType.TEXT_PLAIN)
@ApiOperation("Verifies the connection to Hive.")
@ApiResponses(
@ApiResponse(code = 200, message = "Returns the connection status.", response = Boolean.class)
)
public Response testConnection() {
boolean valid = false;
try {
valid = hiveService.testConnection();
} catch (SQLException e) {
throw new RuntimeException("SQL exception ", e);
}
return Response.ok(valid).build();
}
@GET
@Path("/table-columns")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Lists all columns from all tables.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the list of columns.", response = DatabaseMetadata.class, responseContainer = "List"),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response getTableColumns() {
List<DatabaseMetadata> list;
try {
boolean userImpersonationEnabled = Boolean.valueOf(env.getProperty("hive.userImpersonation.enabled"));
if (userImpersonationEnabled) {
List<String> tables = hiveService.getAllTablesForImpersonatedUser();
list = hiveMetadataService.getTableColumns(tables);
} else {
list = hiveMetadataService.getTableColumns(null);
}
} catch (DataAccessException e) {
log.error("Error Querying Hive Tables for columns from the Metastore ", e);
throw e;
}
return Response.ok(asJson(list)).build();
}
@GET
@Path("/browse/{schema}/{table}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Queries the specified table.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the result.", response = QueryResult.class),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response browseTable(@PathParam("schema") String schema, @PathParam("table") String table, @QueryParam("where") String where, @QueryParam("limit") @DefaultValue("20") Integer limit) {
QueryResult list;
try {
list = hiveService.browse(schema, table, where, limit);
} catch (DataAccessException e) {
log.error("Error Querying Hive Tables for schema: " + schema + ", table: " + table + " where: " + where + ", limit: " + limit, e);
throw e;
}
return Response.ok(asJson(list)).build();
}
@GET
@Path("/query")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Executes a Hive query.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the result.", response = QueryResult.class),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response browseTable(@QueryParam("query") String query) {
QueryResult list;
try {
list = hiveService.browse(query);
} catch (DataAccessException e) {
log.error("Error Querying Hive for query: " + query, e);
throw e;
}
return Response.ok(asJson(list)).build();
}
@GET
@Path("/query-result")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Executes a Hive query.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the result.", response = QueryResult.class),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response queryResult(@QueryParam("query") String query) {
QueryResult list;
try {
list = hiveService.query(query);
} catch (DataAccessException e) {
if (e.getCause().getMessage().contains("HiveAccessControlException Permission denied")) {
throw new AccessControlException("You do not have permission to execute this hive query");
} else {
log.error("Error Querying Hive for query: " + query);
throw e;
}
}
return Response.ok(asJson(list)).build();
}
@GET
@Path("/schemas/{schema}/tables/{table}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Gets the schema of the specified table.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the table schema.", response = TableSchema.class),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response getTableSchema(@PathParam("schema") String schema, @PathParam("table") String table) {
TableSchema tableSchema = hiveService.getTableSchema(schema, table);
return Response.ok(asJson(tableSchema)).build();
}
@GET
@Path("/schemas")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Lists the databases in Hive.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the databases.", response = String.class, responseContainer = "List"),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response getSchemaNames() {
List<String> schemas = hiveService.getSchemaNames();
return Response.ok(asJson(schemas)).build();
}
@GET
@Path("/table-schemas")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Gets the schema of every table.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the table schemas.", response = TableSchema.class, responseContainer = "List"),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response getAllTableSchemas() {
// List<TableSchema> schemas = hiveService.getAllTableSchemas();
List<TableSchema> schemas;
try {
schemas = hiveMetadataService.getTableSchemas();
} catch (DataAccessException e) {
log.error("Error listing Hive Table schemas from the metastore ", e);
throw e;
}
return Response.ok(asJson(schemas)).build();
}
@GET
@Path("/tables")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Lists every table in Hive.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the table names.", response = String.class, responseContainer = "List"),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response getTables() {
List<String> tables;
boolean userImpersonationEnabled = Boolean.valueOf(env.getProperty("hive.userImpersonation.enabled"));
if (userImpersonationEnabled) {
tables = hiveService.getAllTablesForImpersonatedUser();
} else {
try {
tables = hiveMetadataService.getAllTables();
} catch (DataAccessException e) {
log.error("Error listing Hive Tables from the metastore ", e);
throw e;
}
}
return Response.ok(asJson(tables)).build();
}
@GET
@Path("/schemas/{schema}/tables")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation("Lists the tables in the specified database.")
@ApiResponses({
@ApiResponse(code = 200, message = "Returns the table names.", response = String.class, responseContainer = "List"),
@ApiResponse(code = 500, message = "Hive is unavailable.", response = RestResponseStatus.class)
})
public Response getTableNames(@PathParam("schema") String schema) {
boolean userImpersonationEnabled = Boolean.valueOf(env.getProperty("hive.userImpersonation.enabled"));
List<String> tables;
if (userImpersonationEnabled) {
tables = hiveService.getTablesForImpersonatedUser(schema);
} else {
tables = hiveService.getTables(schema);
}
return Response.ok(asJson(tables)).build();
}
private String asJson(Object object) {
String json = null;
ObjectMapper mapper = new ObjectMapper();
try {
json = mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
log.error("Error converting object to JSON String ", e);
}
return json;
}
}