Skip to content
This repository has been archived by the owner on May 20, 2021. It is now read-only.

Commit

Permalink
Merge pull request #14 from airbnb/api-updates
Browse files Browse the repository at this point in the history
[api] API updates
  • Loading branch information
andykram committed Nov 19, 2014
2 parents f2241bc + ef9517d commit 01d75e6
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 10 deletions.
6 changes: 6 additions & 0 deletions src/main/java/com/airbnb/airpal/AirpalApplication.java
Expand Up @@ -6,9 +6,12 @@
import com.airbnb.airpal.resources.ExecuteResource;
import com.airbnb.airpal.resources.HealthResource;
import com.airbnb.airpal.resources.PingResource;
import com.airbnb.airpal.resources.QueriesResource;
import com.airbnb.airpal.resources.QueryResource;
import com.airbnb.airpal.resources.SessionResource;
import com.airbnb.airpal.resources.TablesResource;
import com.airbnb.airpal.resources.UserResource;
import com.airbnb.airpal.resources.UsersResource;
import com.airbnb.airpal.resources.sse.SSEEventSourceServlet;
import com.google.common.collect.ImmutableList;
import com.google.inject.Guice;
Expand Down Expand Up @@ -72,6 +75,9 @@ public void run(AirpalConfiguration config,

environment.jersey().register(injector.getInstance(ExecuteResource.class));
environment.jersey().register(injector.getInstance(QueryResource.class));
environment.jersey().register(injector.getInstance(QueriesResource.class));
environment.jersey().register(injector.getInstance(UserResource.class));
environment.jersey().register(injector.getInstance(UsersResource.class));
environment.jersey().register(injector.getInstance(TablesResource.class));
environment.jersey().register(injector.getInstance(HealthResource.class));
environment.jersey().register(injector.getInstance(PingResource.class));
Expand Down
Expand Up @@ -11,7 +11,11 @@ public interface JobHistoryStore

public List<Job> getRecentlyRun(long maxResults, Table table1, Table... otherTables);

public List<Job> getRecentlyRun(long maxResults, List<Table> tables);
public List<Job> getRecentlyRun(long maxResults, Iterable<Table> tables);

public List<Job> getRecentlyRunForUser(String user, long maxResults);

public List<Job> getRecentlyRunForUser(String user, long maxResults, Iterable<Table> tables);

public void addRun(Job job);
}
Expand Up @@ -105,7 +105,7 @@ public List<Job> getRecentlyRun(long maxResults, Table table1, Table... otherTab
}

@Override
public List<Job> getRecentlyRun(long maxResults, List<Table> tables)
public List<Job> getRecentlyRun(long maxResults, Iterable<Table> tables)
{
try {
String tablesClause = format("WHERE %s", Util.getTableCondition(tables));
Expand All @@ -116,6 +116,30 @@ public List<Job> getRecentlyRun(long maxResults, List<Table> tables)
}
}

@Override
public List<Job> getRecentlyRunForUser(String user, long maxResults)
{
try {
String tablesClause = format("WHERE user = %s", user);
return getJobs(maxResults, 1, tablesClause);
} catch (Exception e) {
log.error("Caught exception during getRecentlyRun", e);
return Collections.emptyList();
}
}

@Override
public List<Job> getRecentlyRunForUser(String user, long maxResults, Iterable<Table> tables)
{
try {
String tablesClause = format("WHERE user = %s AND (%s)", user, Util.getTableCondition(tables));
return getJobs(maxResults, 1, tablesClause);
} catch (Exception e) {
log.error("Caught exception during getRecentlyRun", e);
return Collections.emptyList();
}
}

@Override
public void addRun(Job job)
{
Expand Down
16 changes: 13 additions & 3 deletions src/main/java/com/airbnb/airpal/presto/PartitionedTable.java
@@ -1,12 +1,12 @@
package com.airbnb.airpal.presto;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.experimental.Wither;

import javax.annotation.Nullable;

import java.util.List;

Expand Down Expand Up @@ -123,4 +123,14 @@ public static PartitionedTable valueOf(String s)
throw new IllegalArgumentException("Table identifier parts not found.");
}
}

public static class PartitionedTableToTable implements Function<PartitionedTable, Table>
{
@Nullable
@Override
public Table apply(PartitionedTable input)
{
return input;
}
}
}
91 changes: 91 additions & 0 deletions src/main/java/com/airbnb/airpal/resources/QueriesResource.java
@@ -0,0 +1,91 @@
package com.airbnb.airpal.resources;

import com.airbnb.airpal.api.Job;
import com.airbnb.airpal.api.JobState;
import com.airbnb.airpal.core.AuthorizationUtil;
import com.airbnb.airpal.core.store.JobHistoryStore;
import com.airbnb.airpal.presto.PartitionedTable;
import com.airbnb.airpal.presto.Table;
import com.facebook.presto.client.Column;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.inject.Inject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import java.util.Collections;
import java.util.List;

import static com.airbnb.airpal.resources.QueryResource.JOB_ORDERING;

@Path("/api/queries")
@Produces({MediaType.APPLICATION_JSON})
public class QueriesResource
{
private final JobHistoryStore jobHistoryStore;

@Inject
public QueriesResource(JobHistoryStore jobHistoryStore)
{
this.jobHistoryStore = jobHistoryStore;
}

@GET
public Response getQueries(
@QueryParam("results") int numResults,
@QueryParam("table") List<PartitionedTable> tables)
{
Subject subject = SecurityUtils.getSubject();
Iterable<Job> recentlyRun;
int results = Optional.of(numResults).or(200);

if (tables.size() < 1) {
recentlyRun = jobHistoryStore.getRecentlyRun(results);
} else {
recentlyRun = jobHistoryStore.getRecentlyRun(
results,
Iterables.transform(tables, new PartitionedTable.PartitionedTableToTable()));
}

ImmutableList.Builder<Job> filtered = ImmutableList.builder();
for (Job job : recentlyRun) {
if (job.getTablesUsed().isEmpty() && (job.getState() == JobState.FAILED)) {
filtered.add(job);
continue;
}
for (Table table : job.getTablesUsed()) {
if (AuthorizationUtil.isAuthorizedRead(subject, table)) {
filtered.add(new Job(
job.getUser(),
job.getQuery(),
job.getUuid(),
job.getOutput(),
job.getQueryStats(),
job.getState(),
Collections.<Column>emptyList(),
Collections.<Table>emptySet(),
job.getQueryStartedDateTime(),
job.getError(),
job.getQueryFinishedDateTime()));
}
}
}

List<Job> sortedResult = Ordering
.natural()
.nullsLast()
.onResultOf(JOB_ORDERING)
.reverse()
.immutableSortedCopy(filtered.build());
return Response.ok(sortedResult).build();
}
}
Expand Up @@ -111,7 +111,7 @@ public Response deleteQuery(@PathParam("uuid") UUID uuid)
return Response.status(Response.Status.UNAUTHORIZED).build();
}

private static Function<Job, DateTime> JOB_ORDERING = new Function<Job, DateTime>() {
public static Function<Job, DateTime> JOB_ORDERING = new Function<Job, DateTime>() {
@Nullable
@Override
public DateTime apply(@Nullable Job input)
Expand Down
53 changes: 53 additions & 0 deletions src/main/java/com/airbnb/airpal/resources/UserResource.java
@@ -0,0 +1,53 @@
package com.airbnb.airpal.resources;

import com.airbnb.airpal.core.AirpalUser;
import com.airbnb.airpal.core.AuthorizationUtil;
import lombok.Value;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/api/user")
@Produces(MediaType.APPLICATION_JSON)
public class UserResource
{
@GET
public Response getUserInfo()
{
Subject subject = SecurityUtils.getSubject();
AirpalUser user = subject.getPrincipals().oneByType(AirpalUser.class);

if (user == null) {
return Response.status(Response.Status.FORBIDDEN).build();
} else {
return Response.ok(
new UserInfo(
user.getUserName(),
new ExecutionPermissions(
AuthorizationUtil.isAuthorizedWrite(subject, "hive", "airpal", "any"),
true,
user.getAccessLevel())
)).build();
}
}

@Value
private static class UserInfo
{
private final String name;
private final ExecutionPermissions executionPermissions;
}

@Value
public static class ExecutionPermissions
{
private final boolean canCreateTable;
private final boolean canCreateCsv;
private final String accessLevel;
}
}
117 changes: 117 additions & 0 deletions src/main/java/com/airbnb/airpal/resources/UsersResource.java
@@ -0,0 +1,117 @@
package com.airbnb.airpal.resources;

import com.airbnb.airpal.api.Job;
import com.airbnb.airpal.api.JobState;
import com.airbnb.airpal.core.AirpalUser;
import com.airbnb.airpal.core.AuthorizationUtil;
import com.airbnb.airpal.core.store.JobHistoryStore;
import com.airbnb.airpal.presto.PartitionedTable;
import com.airbnb.airpal.presto.PartitionedTable.PartitionedTableToTable;
import com.airbnb.airpal.presto.Table;
import com.airbnb.airpal.resources.UserResource.ExecutionPermissions;
import com.facebook.presto.client.Column;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Ordering;
import com.google.inject.Inject;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;

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 java.util.Collections;
import java.util.List;

import static com.airbnb.airpal.resources.QueryResource.JOB_ORDERING;

@Path("/api/users/{id}")
@Produces(MediaType.APPLICATION_JSON)
public class UsersResource
{
private final JobHistoryStore jobHistoryStore;

@Inject
public UsersResource(JobHistoryStore jobHistoryStore)
{
this.jobHistoryStore = jobHistoryStore;
}

@GET
@Path("permissions")
public Response getUserPermissions(@PathParam("id") String userId)
{
Subject subject = SecurityUtils.getSubject();
AirpalUser user = subject.getPrincipals().oneByType(AirpalUser.class);

if (user == null) {
return Response.status(Response.Status.FORBIDDEN).build();
} else {
return Response.ok(
new ExecutionPermissions(
AuthorizationUtil.isAuthorizedWrite(subject, "hive", "airpal", "any"),
true,
user.getAccessLevel())
).build();
}
}

@GET
@Path("queries")
public Response getUserQueries(
@PathParam("id") String userId,
@QueryParam("results") int numResults,
@QueryParam("table") List<PartitionedTable> tables)
{
Subject subject = SecurityUtils.getSubject();
Iterable<Job> recentlyRun;
int results = Optional.of(numResults).or(200);

if (tables.size() < 1) {
recentlyRun = jobHistoryStore.getRecentlyRunForUser(userId, results);
} else {
recentlyRun = jobHistoryStore.getRecentlyRunForUser(
userId,
results,
Iterables.transform(tables, new PartitionedTableToTable()));
}

ImmutableList.Builder<Job> filtered = ImmutableList.builder();
for (Job job : recentlyRun) {
if (job.getTablesUsed().isEmpty() && (job.getState() == JobState.FAILED)) {
filtered.add(job);
continue;
}
for (Table table : job.getTablesUsed()) {
if (AuthorizationUtil.isAuthorizedRead(subject, table)) {
filtered.add(new Job(
job.getUser(),
job.getQuery(),
job.getUuid(),
job.getOutput(),
job.getQueryStats(),
job.getState(),
Collections.<Column>emptyList(),
Collections.<Table>emptySet(),
job.getQueryStartedDateTime(),
job.getError(),
job.getQueryFinishedDateTime()));
}
}
}

List<Job> sortedResult = Ordering
.natural()
.nullsLast()
.onResultOf(JOB_ORDERING)
.reverse()
.immutableSortedCopy(filtered.build());
return Response.ok(sortedResult).build();
}
}

0 comments on commit 01d75e6

Please sign in to comment.