View Javadoc
1   /*
2    * This file is part of Waarp Project (named also Waarp or GG).
3    *
4    *  Copyright (c) 2019, Waarp SAS, and individual contributors by the @author
5    *  tags. See the COPYRIGHT.txt in the distribution for a full listing of
6    * individual contributors.
7    *
8    *  All Waarp Project is free software: you can redistribute it and/or
9    * modify it under the terms of the GNU General Public License as published by
10   * the Free Software Foundation, either version 3 of the License, or (at your
11   * option) any later version.
12   *
13   * Waarp is distributed in the hope that it will be useful, but WITHOUT ANY
14   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
15   * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
16   *
17   *  You should have received a copy of the GNU General Public License along with
18   * Waarp . If not, see <http://www.gnu.org/licenses/>.
19   */
20  
21  package org.waarp.openr66.protocol.http.restv2.dbhandlers;
22  
23  import com.fasterxml.jackson.databind.node.ArrayNode;
24  import com.fasterxml.jackson.databind.node.ObjectNode;
25  import io.cdap.http.HttpResponder;
26  import io.netty.handler.codec.http.DefaultHttpHeaders;
27  import io.netty.handler.codec.http.HttpMethod;
28  import io.netty.handler.codec.http.HttpRequest;
29  import org.joda.time.DateTime;
30  import org.waarp.common.database.data.AbstractDbData;
31  import org.waarp.common.json.JsonHandler;
32  import org.waarp.common.role.RoleDefault.ROLE;
33  import org.waarp.common.utility.ParametersChecker;
34  import org.waarp.openr66.dao.DAOFactory;
35  import org.waarp.openr66.dao.Filter;
36  import org.waarp.openr66.dao.TransferDAO;
37  import org.waarp.openr66.dao.exception.DAOConnectionException;
38  import org.waarp.openr66.database.data.DbTaskRunner;
39  import org.waarp.openr66.pojo.Transfer;
40  import org.waarp.openr66.protocol.http.restv2.converters.TransferConverter;
41  import org.waarp.openr66.protocol.http.restv2.errors.RestError;
42  import org.waarp.openr66.protocol.http.restv2.errors.RestErrorException;
43  import org.waarp.openr66.protocol.http.restv2.utils.JsonUtils;
44  import org.waarp.openr66.protocol.http.restv2.utils.RestUtils;
45  
46  import javax.ws.rs.Consumes;
47  import javax.ws.rs.DefaultValue;
48  import javax.ws.rs.GET;
49  import javax.ws.rs.InternalServerErrorException;
50  import javax.ws.rs.OPTIONS;
51  import javax.ws.rs.POST;
52  import javax.ws.rs.Path;
53  import javax.ws.rs.QueryParam;
54  import java.util.ArrayList;
55  import java.util.List;
56  
57  import static io.netty.handler.codec.http.HttpResponseStatus.*;
58  import static javax.ws.rs.core.HttpHeaders.*;
59  import static javax.ws.rs.core.MediaType.*;
60  import static org.waarp.openr66.dao.database.DBTransferDAO.*;
61  import static org.waarp.openr66.protocol.http.restv2.RestConstants.*;
62  import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetLogsParams.*;
63  import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetTransfersParams.STATUS;
64  import static org.waarp.openr66.protocol.http.restv2.RestConstants.GetTransfersParams.*;
65  import static org.waarp.openr66.protocol.http.restv2.errors.RestErrors.*;
66  
67  /**
68   * This is the {@link AbstractRestDbHandler} handling all requests made on the
69   * transfer collection REST entry
70   * point.
71   */
72  @Path(TRANSFERS_HANDLER_URI)
73  public class TransfersHandler extends AbstractRestDbHandler {
74  
75    /**
76     * The content of the 'Allow' header sent when an 'OPTIONS' request is made
77     * on the handler.
78     */
79    private static final io.netty.handler.codec.http.HttpHeaders OPTIONS_HEADERS;
80  
81    static {
82      OPTIONS_HEADERS = new DefaultHttpHeaders();
83      final List<HttpMethod> allow = new ArrayList<HttpMethod>();
84      allow.add(HttpMethod.GET);
85      allow.add(HttpMethod.POST);
86      allow.add(HttpMethod.OPTIONS);
87      OPTIONS_HEADERS.add(ALLOW, allow);
88    }
89  
90    /**
91     * Initializes the handler with the given CRUD mask.
92     *
93     * @param crud the CRUD mask for this handler
94     */
95    public TransfersHandler(final byte crud) {
96      super(crud);
97    }
98  
99    /**
100    * Method called to obtain a list of transfer entry matching the different
101    * filters given as parameters of the
102    * query. The response is sent as a JSON array containing all the requested
103    * entries, unless an unexpected
104    * error prevents it or if the request is invalid.
105    *
106    * @param request the HttpRequest made on the resource
107    * @param responder the HttpResponder which sends the reply to the
108    *     request
109    * @param limitStr maximum number of entries allowed in the
110    *     response
111    * @param offsetStr index of the first accepted entry in the list
112    *     of all valid answers
113    * @param orderStr the criteria used to sort the entries and the
114    *     way of ordering
115    * @param ruleID filter transfers that use this rule
116    * @param partner filter transfers that have this partner
117    * @param statusStr filter transfers currently in one of these
118    *     statuses
119    * @param filename filter transfers of a particular file
120    * @param startTrans lower bound for the transfers' starting date
121    * @param stopTrans upper bound for the transfers' starting date
122    * @param followId the followId to find, should be the only one, except
123    *     LIMIT, OFFSET, ORDER
124    * @param countOrder if true is specified, it turns out to be a "count"
125    *     request only
126    */
127   @GET
128   @Consumes(APPLICATION_FORM_URLENCODED)
129   @RequiredRole(ROLE.READONLY)
130   public final void filterTransfer(final HttpRequest request,
131                                    final HttpResponder responder,
132                                    @QueryParam(LIMIT) @DefaultValue("20")
133                                    final String limitStr,
134                                    @QueryParam(OFFSET) @DefaultValue("0")
135                                    final String offsetStr,
136                                    @QueryParam(ORDER) @DefaultValue("ascId")
137                                    final String orderStr,
138                                    @QueryParam(RULE_ID) @DefaultValue("")
139                                    final String ruleID,
140                                    @QueryParam(PARTNER) @DefaultValue("")
141                                    final String partner,
142                                    @QueryParam(STATUS) @DefaultValue("")
143                                    final String statusStr,
144                                    @QueryParam(FILENAME) @DefaultValue("")
145                                    final String filename,
146                                    @QueryParam(START_TRANS) @DefaultValue("")
147                                    final String startTrans,
148                                    @QueryParam(STOP_TRANS) @DefaultValue("")
149                                    final String stopTrans,
150                                    @QueryParam(FOLLOW_ID) @DefaultValue("")
151                                    final String followId,
152                                    @QueryParam(COUNT_ORDER) @DefaultValue("")
153                                    final String countOrder) {
154     checkSanity(limitStr, offsetStr, orderStr, ruleID, partner, statusStr,
155                 filename, startTrans, stopTrans, followId, countOrder);
156     final ArrayList<RestError> errors = new ArrayList<RestError>();
157 
158     boolean count = false;
159     try {
160       if (ParametersChecker.isNotEmpty(countOrder) &&
161           RestUtils.stringToBoolean(countOrder)) {
162         count = true;
163       }
164     } catch (final Exception ignore) {
165       // Ignore
166     }
167     int limit = 20;
168     try {
169       limit = Integer.parseInt(limitStr);
170     } catch (final NumberFormatException e) {
171       errors.add(ILLEGAL_PARAMETER_VALUE(LIMIT, limitStr));
172     }
173     int offset = 0;
174     try {
175       offset = Integer.parseInt(offsetStr);
176     } catch (final NumberFormatException e) {
177       errors.add(ILLEGAL_PARAMETER_VALUE(OFFSET, offsetStr));
178     }
179     TransferConverter.Order order = TransferConverter.Order.ascId;
180     try {
181       order = TransferConverter.Order.valueOf(orderStr);
182     } catch (final IllegalArgumentException e) {
183       errors.add(ILLEGAL_PARAMETER_VALUE(ORDER, orderStr));
184     }
185 
186     final List<Filter> filters = new ArrayList<Filter>();
187     if (ParametersChecker.isNotEmpty(startTrans, stopTrans)) {
188       try {
189         filters.add(new Filter(TRANSFER_START_FIELD, Filter.BETWEEN,
190                                DateTime.parse(startTrans),
191                                DateTime.parse(stopTrans)));
192       } catch (final IllegalArgumentException e) {
193         errors.add(ILLEGAL_PARAMETER_VALUE(START, startTrans));
194         errors.add(ILLEGAL_PARAMETER_VALUE(STOP, stopTrans));
195       }
196     } else if (ParametersChecker.isNotEmpty(startTrans)) {
197       try {
198         final DateTime lowerDate = DateTime.parse(startTrans);
199         filters.add(new Filter(TRANSFER_START_FIELD, ">=", lowerDate));
200       } catch (final IllegalArgumentException e) {
201         errors.add(ILLEGAL_PARAMETER_VALUE(START, startTrans));
202       }
203     } else if (ParametersChecker.isNotEmpty(stopTrans)) {
204       try {
205         final DateTime upperDate = DateTime.parse(stopTrans);
206         filters.add(new Filter(TRANSFER_START_FIELD, "<=", upperDate));
207       } catch (final IllegalArgumentException e) {
208         errors.add(ILLEGAL_PARAMETER_VALUE(STOP, stopTrans));
209       }
210     }
211     if (ParametersChecker.isNotEmpty(ruleID)) {
212       filters.add(new Filter(ID_RULE_FIELD, "=", ruleID));
213     }
214     if (ParametersChecker.isNotEmpty(partner)) {
215       filters.add(new Filter(REQUESTED_FIELD, "=", partner));
216     }
217     if (ParametersChecker.isNotEmpty(filename)) {
218       filters.add(new Filter(FILENAME_FIELD, "=", filename));
219     }
220     if (ParametersChecker.isNotEmpty(statusStr)) {
221       try {
222         final int status_nbr =
223             AbstractDbData.UpdatedInfo.valueOf(statusStr).ordinal();
224         filters.add(new Filter(UPDATED_INFO_FIELD, "=", status_nbr));
225       } catch (final IllegalArgumentException e) {
226         errors.add(ILLEGAL_PARAMETER_VALUE(STATUS, statusStr));
227       }
228     }
229     if (ParametersChecker.isNotEmpty(followId)) {
230       filters.add(DbTaskRunner.getFollowIdFilter(followId));
231     }
232 
233     if (!errors.isEmpty()) {
234       throw new RestErrorException(errors);
235     }
236 
237     TransferDAO transferDAO = null;
238     final ObjectNode responseObject = JsonHandler.createObjectNode();
239     if (count) {
240       long nbCount = -1;
241       try {
242         transferDAO = DAO_FACTORY.getTransferDAO();
243         nbCount = transferDAO.count(filters);
244       } catch (final DAOConnectionException e) {
245         throw new InternalServerErrorException(e);
246       } finally {
247         DAOFactory.closeDAO(transferDAO);
248       }
249       responseObject.put("totalResults", nbCount);
250     } else {
251       List<Transfer> transferList;
252       try {
253         transferDAO = DAO_FACTORY.getTransferDAO();
254         transferList =
255             transferDAO.find(filters, order.column, order.ascend, limit,
256                              offset);
257       } catch (final DAOConnectionException e) {
258         throw new InternalServerErrorException(e);
259       } finally {
260         DAOFactory.closeDAO(transferDAO);
261       }
262       final ArrayNode resultList = responseObject.putArray("results");
263       for (final Transfer transfer : transferList) {
264         resultList.add(TransferConverter.transferToNode(transfer));
265       }
266       responseObject.put("totalResults", transferList.size());
267     }
268     final String responseText = JsonUtils.nodeToString(responseObject);
269     responder.sendJson(OK, responseText);
270   }
271 
272   /**
273    * Method called to create a new transfer on the server. The reply will
274    * contain the created entry in JSON
275    * format, unless an unexpected error prevents it or if the request is
276    * invalid.
277    *
278    * @param request the HttpRequest made on the resource
279    * @param responder the HttpResponder which sends the reply to the
280    *     request
281    */
282   @POST
283   @Consumes(APPLICATION_JSON)
284   @RequiredRole(ROLE.TRANSFER)
285   public final void createTransfer(final HttpRequest request,
286                                    final HttpResponder responder) {
287     final ObjectNode requestObject = JsonUtils.deserializeRequest(request);
288     checkSanity(requestObject);
289     final Transfer transfer =
290         TransferConverter.nodeToNewTransfer(requestObject);
291 
292     TransferDAO transferDAO = null;
293     try {
294       transferDAO = DAO_FACTORY.getTransferDAO();
295       transferDAO.insert(transfer);
296     } catch (final DAOConnectionException e) {
297       throw new InternalServerErrorException(e);
298     } finally {
299       DAOFactory.closeDAO(transferDAO);
300     }
301 
302     final ObjectNode responseObject =
303         TransferConverter.transferToNode(transfer);
304     final String responseText = JsonUtils.nodeToString(responseObject);
305     final DefaultHttpHeaders headers = new DefaultHttpHeaders();
306     headers.add(CONTENT_TYPE, APPLICATION_JSON);
307     headers.add("transfer-uri", TRANSFERS_HANDLER_URI + transfer.getId() + '_' +
308                                 transfer.getRequested());
309 
310     responder.sendString(CREATED, responseText, headers);
311   }
312 
313   /**
314    * Method called to get a list of all allowed HTTP methods on this entry
315    * point. The HTTP methods are sent as
316    * an array in the reply's headers.
317    *
318    * @param request the HttpRequest made on the resource
319    * @param responder the HttpResponder which sends the reply to the
320    *     request
321    */
322   @OPTIONS
323   @Consumes(WILDCARD)
324   @RequiredRole(ROLE.NOACCESS)
325   public final void options(final HttpRequest request,
326                             final HttpResponder responder) {
327     responder.sendStatus(OK, OPTIONS_HEADERS);
328   }
329 }