TransfersHandler.java
/*
* This file is part of Waarp Project (named also Waarp or GG).
*
* Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
* tags. See the COPYRIGHT.txt in the distribution for a full listing of
* individual contributors.
*
* All Waarp Project is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at your
* option) any later version.
*
* Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with
* Waarp . If not, see <http://www.gnu.org/licenses/>.
*/
package org.waarp.openr66.protocol.http.restv2.dbhandlers;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.cdap.http.HttpResponder;
import io.netty.handler.codec.http.DefaultHttpHeaders;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import org.joda.time.DateTime;
import org.waarp.common.database.data.AbstractDbData;
import org.waarp.common.json.JsonHandler;
import org.waarp.common.role.RoleDefault.ROLE;
import org.waarp.common.utility.ParametersChecker;
import org.waarp.openr66.dao.DAOFactory;
import org.waarp.openr66.dao.Filter;
import org.waarp.openr66.dao.TransferDAO;
import org.waarp.openr66.dao.exception.DAOConnectionException;
import org.waarp.openr66.database.data.DbTaskRunner;
import org.waarp.openr66.pojo.Transfer;
import org.waarp.openr66.protocol.http.restv2.converters.TransferConverter;
import org.waarp.openr66.protocol.http.restv2.errors.RestError;
import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
import org.waarp.openr66.protocol.http.restv2.utils.JsonUtils;
import org.waarp.openr66.protocol.http.restv2.utils.RestUtils;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import java.util.ArrayList;
import java.util.List;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static javax.ws.rs.core.HttpHeaders.*;
import static javax.ws.rs.core.MediaType.*;
import static org.waarp.openr66.dao.database.DBTransferDAO.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetLogsParams.*;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetTransfersParams.STATUS;
import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetTransfersParams.*;
import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
/**
* This is the {@link AbstractRestDbHandler} handling all requests made on the
* transfer collection REST entry
* point.
*/
@Path(TRANSFERS_HANDLER_URI)
public class TransfersHandler extends AbstractRestDbHandler {
/**
* The content of the 'Allow' header sent when an 'OPTIONS' request is made
* on the handler.
*/
private static final io.netty.handler.codec.http.HttpHeaders OPTIONS_HEADERS;
static {
OPTIONS_HEADERS = new DefaultHttpHeaders();
final List<HttpMethod> allow = new ArrayList<HttpMethod>();
allow.add(HttpMethod.GET);
allow.add(HttpMethod.POST);
allow.add(HttpMethod.OPTIONS);
OPTIONS_HEADERS.add(ALLOW, allow);
}
/**
* Initializes the handler with the given CRUD mask.
*
* @param crud the CRUD mask for this handler
*/
public TransfersHandler(final byte crud) {
super(crud);
}
/**
* Method called to obtain a list of transfer entry matching the different
* filters given as parameters of the
* query. The response is sent as a JSON array containing all the requested
* entries, unless an unexpected
* error prevents it or if the request is invalid.
*
* @param request the HttpRequest made on the resource
* @param responder the HttpResponder which sends the reply to the
* request
* @param limitStr maximum number of entries allowed in the
* response
* @param offsetStr index of the first accepted entry in the list
* of all valid answers
* @param orderStr the criteria used to sort the entries and the
* way of ordering
* @param ruleID filter transfers that use this rule
* @param partner filter transfers that have this partner
* @param statusStr filter transfers currently in one of these
* statuses
* @param filename filter transfers of a particular file
* @param startTrans lower bound for the transfers' starting date
* @param stopTrans upper bound for the transfers' starting date
* @param followId the followId to find, should be the only one, except
* LIMIT, OFFSET, ORDER
* @param countOrder if true is specified, it turns out to be a "count"
* request only
*/
@GET
@Consumes(APPLICATION_FORM_URLENCODED)
@RequiredRole(ROLE.READONLY)
public final void filterTransfer(final HttpRequest request,
final HttpResponder responder,
@QueryParam(LIMIT) @DefaultValue("20")
final String limitStr,
@QueryParam(OFFSET) @DefaultValue("0")
final String offsetStr,
@QueryParam(ORDER) @DefaultValue("ascId")
final String orderStr,
@QueryParam(RULE_ID) @DefaultValue("")
final String ruleID,
@QueryParam(PARTNER) @DefaultValue("")
final String partner,
@QueryParam(STATUS) @DefaultValue("")
final String statusStr,
@QueryParam(FILENAME) @DefaultValue("")
final String filename,
@QueryParam(START_TRANS) @DefaultValue("")
final String startTrans,
@QueryParam(STOP_TRANS) @DefaultValue("")
final String stopTrans,
@QueryParam(FOLLOW_ID) @DefaultValue("")
final String followId,
@QueryParam(COUNT_ORDER) @DefaultValue("")
final String countOrder) {
checkSanity(limitStr, offsetStr, orderStr, ruleID, partner, statusStr,
filename, startTrans, stopTrans, followId, countOrder);
final ArrayList<RestError> errors = new ArrayList<RestError>();
boolean count = false;
try {
if (ParametersChecker.isNotEmpty(countOrder) &&
RestUtils.stringToBoolean(countOrder)) {
count = true;
}
} catch (final Exception ignore) {
// Ignore
}
int limit = 20;
try {
limit = Integer.parseInt(limitStr);
} catch (final NumberFormatException e) {
errors.add(ILLEGAL_PARAMETER_VALUE(LIMIT, limitStr));
}
int offset = 0;
try {
offset = Integer.parseInt(offsetStr);
} catch (final NumberFormatException e) {
errors.add(ILLEGAL_PARAMETER_VALUE(OFFSET, offsetStr));
}
TransferConverter.Order order = TransferConverter.Order.ascId;
try {
order = TransferConverter.Order.valueOf(orderStr);
} catch (final IllegalArgumentException e) {
errors.add(ILLEGAL_PARAMETER_VALUE(ORDER, orderStr));
}
final List<Filter> filters = new ArrayList<Filter>();
if (ParametersChecker.isNotEmpty(startTrans, stopTrans)) {
try {
filters.add(new Filter(TRANSFER_START_FIELD, Filter.BETWEEN,
DateTime.parse(startTrans),
DateTime.parse(stopTrans)));
} catch (final IllegalArgumentException e) {
errors.add(ILLEGAL_PARAMETER_VALUE(START, startTrans));
errors.add(ILLEGAL_PARAMETER_VALUE(STOP, stopTrans));
}
} else if (ParametersChecker.isNotEmpty(startTrans)) {
try {
final DateTime lowerDate = DateTime.parse(startTrans);
filters.add(new Filter(TRANSFER_START_FIELD, ">=", lowerDate));
} catch (final IllegalArgumentException e) {
errors.add(ILLEGAL_PARAMETER_VALUE(START, startTrans));
}
} else if (ParametersChecker.isNotEmpty(stopTrans)) {
try {
final DateTime upperDate = DateTime.parse(stopTrans);
filters.add(new Filter(TRANSFER_START_FIELD, "<=", upperDate));
} catch (final IllegalArgumentException e) {
errors.add(ILLEGAL_PARAMETER_VALUE(STOP, stopTrans));
}
}
if (ParametersChecker.isNotEmpty(ruleID)) {
filters.add(new Filter(ID_RULE_FIELD, "=", ruleID));
}
if (ParametersChecker.isNotEmpty(partner)) {
filters.add(new Filter(REQUESTED_FIELD, "=", partner));
}
if (ParametersChecker.isNotEmpty(filename)) {
filters.add(new Filter(FILENAME_FIELD, "=", filename));
}
if (ParametersChecker.isNotEmpty(statusStr)) {
try {
final int status_nbr =
AbstractDbData.UpdatedInfo.valueOf(statusStr).ordinal();
filters.add(new Filter(UPDATED_INFO_FIELD, "=", status_nbr));
} catch (final IllegalArgumentException e) {
errors.add(ILLEGAL_PARAMETER_VALUE(STATUS, statusStr));
}
}
if (ParametersChecker.isNotEmpty(followId)) {
filters.add(DbTaskRunner.getFollowIdFilter(followId));
}
if (!errors.isEmpty()) {
throw new RestErrorException(errors);
}
TransferDAO transferDAO = null;
final ObjectNode responseObject = JsonHandler.createObjectNode();
if (count) {
long nbCount = -1;
try {
transferDAO = DAO_FACTORY.getTransferDAO();
nbCount = transferDAO.count(filters);
} catch (final DAOConnectionException e) {
throw new InternalServerErrorException(e);
} finally {
DAOFactory.closeDAO(transferDAO);
}
responseObject.put("totalResults", nbCount);
} else {
List<Transfer> transferList;
try {
transferDAO = DAO_FACTORY.getTransferDAO();
transferList =
transferDAO.find(filters, order.column, order.ascend, limit,
offset);
} catch (final DAOConnectionException e) {
throw new InternalServerErrorException(e);
} finally {
DAOFactory.closeDAO(transferDAO);
}
final ArrayNode resultList = responseObject.putArray("results");
for (final Transfer transfer : transferList) {
resultList.add(TransferConverter.transferToNode(transfer));
}
responseObject.put("totalResults", transferList.size());
}
final String responseText = JsonUtils.nodeToString(responseObject);
responder.sendJson(OK, responseText);
}
/**
* Method called to create a new transfer on the server. The reply will
* contain the created entry in JSON
* format, unless an unexpected error prevents it or if the request is
* invalid.
*
* @param request the HttpRequest made on the resource
* @param responder the HttpResponder which sends the reply to the
* request
*/
@POST
@Consumes(APPLICATION_JSON)
@RequiredRole(ROLE.TRANSFER)
public final void createTransfer(final HttpRequest request,
final HttpResponder responder) {
final ObjectNode requestObject = JsonUtils.deserializeRequest(request);
checkSanity(requestObject);
final Transfer transfer =
TransferConverter.nodeToNewTransfer(requestObject);
TransferDAO transferDAO = null;
try {
transferDAO = DAO_FACTORY.getTransferDAO();
transferDAO.insert(transfer);
} catch (final DAOConnectionException e) {
throw new InternalServerErrorException(e);
} finally {
DAOFactory.closeDAO(transferDAO);
}
final ObjectNode responseObject =
TransferConverter.transferToNode(transfer);
final String responseText = JsonUtils.nodeToString(responseObject);
final DefaultHttpHeaders headers = new DefaultHttpHeaders();
headers.add(CONTENT_TYPE, APPLICATION_JSON);
headers.add("transfer-uri", TRANSFERS_HANDLER_URI + transfer.getId() + '_' +
transfer.getRequested());
responder.sendString(CREATED, responseText, headers);
}
/**
* Method called to get a list of all allowed HTTP methods on this entry
* point. The HTTP methods are sent as
* an array in the reply's headers.
*
* @param request the HttpRequest made on the resource
* @param responder the HttpResponder which sends the reply to the
* request
*/
@OPTIONS
@Consumes(WILDCARD)
@RequiredRole(ROLE.NOACCESS)
public final void options(final HttpRequest request,
final HttpResponder responder) {
responder.sendStatus(OK, OPTIONS_HEADERS);
}
}