Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SQL API added #3619

Merged
merged 5 commits into from
Apr 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2021 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.obiba.opal.core.service;

public class SQLException extends RuntimeException {

public SQLException(String message) {
super(message);
}

public SQLException(Throwable throwable) {
super(throwable.getMessage(), throwable);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2021 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.obiba.opal.core.service;

import java.io.File;

/**
* A service for executing SQL queries on a project's tables.
*/
public interface SQLService extends SystemService {

/**
* Execute a SQL query in the context of a datasource and output result as a JSON object.
*
* @param datasource
* @param query
* @param idName
* @return
*/
File executeToJSON(String datasource, String query, String idName);

/**
* Execute a SQL query in the context of a datasource and dump output to a temporary
* CSV file.
*
* @param datasource
* @param query
* @param idName
* @return
*/
File executeToCSV(String datasource, String query, String idName);

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public enum Permission {
public Iterable<String> convert(String node) {
String[] args = args(node, "/datasource/(.+)");
return Lists.newArrayList(toRest("/datasource/{0}", "*:GET/*", args),
toRest("/datasource/{0}/_sql", "POST:GET", args),
toRest("/identifiers/mappings", "GET"),
toRest("/datasource-plugin", "GET:GET/GET"),
toRest("/project/{0}", "GET:GET", args),
Expand All @@ -66,6 +67,7 @@ public Iterable<String> convert(String node) {
public Iterable<String> convert(String node) {
String[] args = args(node, "/datasource/(.+)");
return Lists.newArrayList(toRest("/datasource/{0}/tables", "GET:GET", args),
toRest("/datasource/{0}/_sql", "POST:GET", args),
toRest("/datasource/{0}/tables", "POST:GET", args),
toRest("/datasource/{0}/tables", "DELETE", args),
toRest("/datasource/{0}/views", "POST:GET", args),
Expand All @@ -91,6 +93,7 @@ public Iterable<String> convert(String node) {
public Iterable<String> convert(String node) {
String[] args = args(node, "/datasource/(.+)");
return Lists.newArrayList(toRest("/datasource/{0}", "GET:GET/GET", args),
toRest("/datasource/{0}/_sql", "POST:GET", args),
toRest("/project/{0}/commands/_analyse", "POST:GET", args),
toRest("/project/{0}/commands/_export", "POST:GET", args),
toRest("/project/{0}/commands/_copy", "POST:GET", args),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ public Iterable<String> convert(String node) {
String[] args = isView ? args(node, "/datasource/(.+)/view/(.+)") : args(node, "/datasource/(.+)/table/(.+)");

List<String> perms = Lists.newArrayList(toRest("/datasource/{0}/table/{1}", "*:GET/*", args),
toRest("/datasource/{0}/_sql", "POST:GET", args),
toRest("/project/{0}/commands/_export", "POST:GET", args),
toRest("/project/{0}/commands/_copy", "POST:GET", args),
toRest("/project/{0}/commands/_refresh", "POST:GET", args),
Expand Down Expand Up @@ -104,6 +105,7 @@ public Iterable<String> convert(String node) {
toRest("/datasource/{0}/table/{1}/index/_search", "GET", args),
toRest("/datasource/{0}/table/{1}/index/_search", "POST", args),
toRest("/datasource/{0}/table/{1}/index/_schema", "GET", args),
toRest("/datasource/{0}/_sql", "POST:GET", args),
toRest("/project/{0}/commands/_analyse", "POST:GET", args),
toRest("/project/{0}/commands/_export", "POST:GET", args),
toRest("/project/{0}/commands/_copy", "POST:GET", args),
Expand Down Expand Up @@ -148,6 +150,7 @@ public Iterable<String> convert(String node) {
}

perms.addAll(Lists.newArrayList(toRest("/datasource/{0}/table/{1}/index", "GET:GET", args),
toRest("/datasource/{0}/_sql", "POST:GET", args),
toRest("/datasource/{0}/table/{1}/index/schedule", "GET:GET", args),
toRest("/files/projects/{0}", "GET:GET/*", args),
toRest("/files/projects/{0}", "POST:GET/*", args),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import com.google.common.eventbus.EventBus;
import com.googlecode.protobuf.format.JsonFormat;
import java.io.BufferedOutputStream;
import java.io.File;
import java.nio.file.Files;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -29,6 +31,7 @@
import org.obiba.opal.core.event.ValueTableAddedEvent;
import org.obiba.opal.core.security.OpalPermissions;
import org.obiba.opal.core.service.OpalGeneralConfigService;
import org.obiba.opal.core.service.SQLService;
import org.obiba.opal.search.IndexManagerConfigurationService;
import org.obiba.opal.search.Schedule;
import org.obiba.opal.web.magma.view.ViewDtos;
Expand Down Expand Up @@ -80,6 +83,9 @@ public class DatasourceResource {
@Autowired
private EventBus eventBus;

@Autowired
private SQLService sqlService;

public void setName(String name) {
this.name = name;
}
Expand Down Expand Up @@ -253,6 +259,38 @@ public Iterable<LocaleDto> getLocales(@QueryParam("locale") String displayLocale
return localeDtos;
}

@POST
@Path("/_sql")
@Consumes(MediaType.TEXT_PLAIN)
@Produces(MediaType.APPLICATION_JSON)
public Response executeSQLToJSON(String query, @QueryParam("id") @DefaultValue("_id") String idName) {
final File output = sqlService.executeToJSON(name, query, idName);
StreamingOutput stream = os -> {
Files.copy(output.toPath(), os);
output.delete();
};

return Response.ok(stream, MediaType.APPLICATION_JSON_TYPE)
.header("Content-Disposition", "attachment; filename=\"" + output.getName() + "\"").build();
// return Response.ok().type(MediaType.APPLICATION_JSON_TYPE)
// .entity(sqlService.executeToJSON(name, query, idName)).build();
}

@POST
@Path("/_sql")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces("text/csv")
public Response executeSQLToCSV(@FormParam("query") String query, @FormParam("id") @DefaultValue("_id") String idName) {
final File output = sqlService.executeToCSV(name, query, idName);
StreamingOutput stream = os -> {
Files.copy(output.toPath(), os);
output.delete();
};

return Response.ok(stream, "text/csv")
.header("Content-Disposition", "attachment; filename=\"" + output.getName() + "\"").build();
}

Datasource getDatasource() {
return MagmaEngine.get().hasDatasource(name)
? MagmaEngine.get().getDatasource(name)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (c) 2020 OBiBa. All rights reserved.
*
* This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.obiba.opal.web.provider;

import com.google.protobuf.GeneratedMessage;
import org.obiba.opal.core.service.SQLException;
import org.obiba.opal.web.magma.ClientErrorDtos;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.ws.rs.BadRequestException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;

import static javax.ws.rs.core.Response.Status.BAD_REQUEST;

@Component
@Provider
public class SQLExceptionMapper extends ErrorDtoExceptionMapper<SQLException> {

private static final Logger log = LoggerFactory.getLogger(SQLExceptionMapper.class);

@Override
protected Response.Status getStatus() {
return BAD_REQUEST;
}

@Override
protected GeneratedMessage.ExtendableMessage<?> getErrorDto(SQLException exception) {
log.debug("SQL exception", exception);
return ClientErrorDtos.getErrorMessage(getStatus(), "SQLError", exception);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class DatasourcePermissionConverterTest
public void testDatasourceAll() {
testConversion("/datasource/patate", DatasourcePermissionConverter.Permission.DATASOURCE_ALL,
"rest:/datasource/patate:*:GET/*",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/identifiers/mappings:GET",
"rest:/datasource-plugin:GET:GET/GET",
"rest:/project/patate:GET:GET",
Expand All @@ -44,6 +45,7 @@ public void testDatasourceAll() {
public void testDatasourceView() {
testConversion("/datasource/patate", DatasourcePermissionConverter.Permission.DATASOURCE_VIEW,
"rest:/datasource/patate:GET:GET/GET",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/project/patate/commands/_analyse:POST:GET",
"rest:/project/patate/commands/_export:POST:GET",
"rest:/project/patate/commands/_copy:POST:GET",
Expand All @@ -60,6 +62,7 @@ public void testDatasourceView() {
public void testCreateTable() {
testConversion("/datasource/patate", DatasourcePermissionConverter.Permission.TABLE_ADD,
"rest:/datasource/patate/tables:GET:GET",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/datasource/patate/tables:POST:GET",
"rest:/datasource/patate/tables:DELETE",
"rest:/datasource/patate/views:POST:GET",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public void testArgs() {
public void testTableAll() {
testConversion("/datasource/patate/table/pwel", TablePermissionConverter.Permission.TABLE_ALL,
"rest:/datasource/patate/table/pwel:*:GET/*",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/project/patate/commands/_export:POST:GET",
"rest:/project/patate/commands/_copy:POST:GET",
"rest:/project/patate/commands/_refresh:POST:GET",
Expand All @@ -51,6 +52,7 @@ public void testTableAll() {
public void testTableAllOnView() {
testConversion("/datasource/patate/view/pwel", TablePermissionConverter.Permission.TABLE_ALL,
"rest:/datasource/patate/table/pwel:*:GET/*",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/project/patate/commands/_export:POST:GET",
"rest:/project/patate/commands/_copy:POST:GET",
"rest:/project/patate/commands/_refresh:POST:GET",
Expand All @@ -75,6 +77,7 @@ public void testTableEdit() {
"rest:/datasource/patate/table/pwel/variables:POST:GET/*",
"rest:/datasource/patate/table/pwel/variables:DELETE:GET",
"rest:/datasource/patate/table/pwel/index:GET:GET",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/datasource/patate/table/pwel/index/schedule:GET:GET",
"rest:/files/projects/patate:GET:GET/*",
"rest:/files/projects/patate:POST:GET/*",
Expand Down Expand Up @@ -102,6 +105,7 @@ public void testTableValuesEdit() {
"rest:/datasource/patate/table/pwel/index/_search:GET",
"rest:/datasource/patate/table/pwel/index/_search:POST",
"rest:/datasource/patate/table/pwel/index/_schema:GET",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/project/patate/commands/_analyse:POST:GET",
"rest:/project/patate/commands/_export:POST:GET",
"rest:/project/patate/commands/_copy:POST:GET",
Expand Down Expand Up @@ -132,6 +136,7 @@ public void testTableValuesEdit() {
"rest:/datasource/patate/table/pwel/variables:POST:GET/*",
"rest:/datasource/patate/table/pwel/variables:DELETE:GET",
"rest:/datasource/patate/table/pwel/index:GET:GET",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/datasource/patate/table/pwel/index/schedule:GET:GET",
"rest:/files/projects/patate:GET:GET/*",
"rest:/files/projects/patate:POST:GET/*",
Expand Down Expand Up @@ -177,6 +182,7 @@ public void testTableValuesOnView() {
"rest:/datasource/patate/table/pwel/index/_search:GET",
"rest:/datasource/patate/table/pwel/index/_search:POST",
"rest:/datasource/patate/table/pwel/index/_schema:GET",
"rest:/datasource/patate/_sql:POST:GET",
"rest:/project/patate/commands/_analyse:POST:GET",
"rest:/project/patate/commands/_export:POST:GET",
"rest:/project/patate/commands/_copy:POST:GET",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -756,7 +756,8 @@ public interface Translations extends Constants {
"NameMustBeUnique", "Name must be unique.",
"GHOrganizationIsRequired", "GitHub user or organization name is required",
"ResourceAssignSuccess", "Resource assignment in R was successful.",
"ResourceAssignFailed", "Resource assignment in R has failed: "
"ResourceAssignFailed", "Resource assignment in R has failed: ",
"SQLError", "{0}"
})
Map<String, String> userMessageMap();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
import org.obiba.opal.web.gwt.app.client.magma.presenter.VariablePresenter;
import org.obiba.opal.web.gwt.app.client.magma.presenter.VariableVcsCommitHistoryPresenter;
import org.obiba.opal.web.gwt.app.client.magma.presenter.VcsCommitHistoryModalPresenter;
import org.obiba.opal.web.gwt.app.client.magma.sql.SQLPresenter;
import org.obiba.opal.web.gwt.app.client.magma.sql.SQLView;
import org.obiba.opal.web.gwt.app.client.magma.table.presenter.TablePropertiesModalPresenter;
import org.obiba.opal.web.gwt.app.client.magma.table.presenter.ViewModalPresenter;
import org.obiba.opal.web.gwt.app.client.magma.table.presenter.ViewWhereModalPresenter;
Expand Down Expand Up @@ -101,7 +103,7 @@ private void configureMagma() {
VariableTaxonomyModalView.class);
bindPresenterWidget(AddVariablesModalPresenter.class, AddVariablesModalPresenter.Display.class,
AddVariablesModalView.class);

bindPresenterWidget(SQLPresenter.class, SQLPresenter.Display.class, SQLView.class);
}

private void configureDatasource() {
Expand Down
Loading