package org.rakam.ui;
import com.google.common.collect.ImmutableMap;
import com.google.inject.name.Named;
import io.netty.handler.codec.http.HttpResponseStatus;
import org.rakam.analysis.JDBCPoolDataSource;
import org.rakam.ui.report.Report;
import org.rakam.ui.user.WebUserService;
import org.rakam.util.AlreadyExistsException;
import org.rakam.util.JsonHelper;
import org.rakam.util.NotExistsException;
import org.rakam.util.RakamException;
import org.skife.jdbi.v2.DBI;
import org.skife.jdbi.v2.Handle;
import org.skife.jdbi.v2.exceptions.UnableToExecuteStatementException;
import org.skife.jdbi.v2.tweak.ResultSetMapper;
import javax.inject.Inject;
import java.util.List;
import java.util.Map;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
public class JDBCReportMetadata implements ReportMetadata {
private final DBI dbi;
private ResultSetMapper<Report> mapper = (index, r, ctx) -> {
Report report = new Report(r.getString(2), r.getString(3), r.getString(4), r.getString(5),
JsonHelper.read(r.getString(6), Map.class),
r.getString(7) == null ? ImmutableMap.of() : JsonHelper.read(r.getString(7), Map.class),
r.getBoolean(8));
if(r.getMetaData().getColumnCount() >= 8) {
report.setPermission(r.getBoolean(9));
}
if(r.getMetaData().getColumnCount() == 9 && r.getObject(9) != null) {
report.setUserId(r.getInt(9));
}
return report;
};
@Inject
public JDBCReportMetadata(@Named("ui.metadata.jdbc") JDBCPoolDataSource dataSource) {
dbi = new DBI(dataSource);
}
public List<Report> getReports(Integer requestedUserId, int project) {
try (Handle handle = dbi.open()) {
return handle.createQuery("SELECT reports.project_id, reports.slug, reports.category, reports.name, reports.query, reports.options, reports.query_options, reports.shared, reports.user_id FROM reports " +
" WHERE reports.project_id = :project " +
" ORDER BY reports.created_at")
.bind("project", project)
.map(mapper)
.list();
}
}
public void delete(Integer userId, int project, String slug) {
try (Handle handle = dbi.open()) {
int execute = handle.createStatement("DELETE FROM reports WHERE project_id = :project AND slug = :slug" +
" AND (:user is null or user_id = :user)")
.bind("project", project)
.bind("slug", slug)
.bind("user", userId).execute();
if(execute == 0) {
throw new NotExistsException("Report");
}
}
}
public void save(Integer userId, int project, Report report) {
try (Handle handle = dbi.open()) {
handle.createStatement("INSERT INTO reports (project_id, slug, category, name, query, options, user_id, query_options) VALUES (:project, :slug, :category, :name, :query, :options, :user, :query_options)")
.bind("project", project)
.bind("name", report.name)
.bind("query", report.query)
.bind("slug", report.slug)
.bind("user", userId)
.bind("category", report.category)
.bind("query_options", JsonHelper.encode(report.queryOptions))
.bind("shared", report.shared)
.bind("options", JsonHelper.encode(report.options))
.execute();
} catch (UnableToExecuteStatementException e) {
try {
get(null, project, report.slug);
} catch (NotExistsException ex) {
throw e;
}
throw new AlreadyExistsException(String.format("Report '%s'", report.slug), BAD_REQUEST);
}
}
public Report get(Integer requestedUserId, int project, String slug) {
try (Handle handle = dbi.open()) {
Report report = handle.createQuery("SELECT r.project_id, r.slug, r.category, r.name, query, r.options, r.query_options, r.shared, r.user_id FROM reports r " +
" LEFT JOIN web_user_api_key permission ON (permission.project_id = r.project_id)" +
" WHERE r.project_id = :project AND r.slug = :slug AND (" +
"((permission.master_key IS NOT NULL OR r.shared OR r.user_id = :requestedUser)))")
.bind("project", project)
.bind("requestedUser", requestedUserId)
.bind("slug", slug).map(mapper).first();
if(report == null) {
throw new NotExistsException("Report");
}
return report;
}
}
public Report update(Integer userId, int project, Report report) {
try (Handle handle = dbi.open()) {
int execute = handle.createStatement("UPDATE reports SET name = :name, query = :query, category = :category, options = :options WHERE project_id = :project AND slug = :slug")
.bind("project", project)
.bind("name", report.name)
.bind("query", report.query)
.bind("category", report.category)
.bind("slug", report.slug)
.bind("user", userId)
.bind("options", JsonHelper.encode(report.options, false))
.execute();
if (execute == 0) {
throw new RakamException("Report does not exist", BAD_REQUEST);
}
}
return report;
}
}