1. Project Clover database Mon Aug 27 2018 22:12:52 BRT
  2. Package net.technearts.rip

File RipResponseBuilder.java

 

Coverage histogram

../../../img/srcFileCovDistChart8.png
21% of files have more coverage

Code metrics

18
108
27
2
418
261
40
0.37
4
13.5
1.48

Classes

Class Line # Actions
OP 36 0 - 0 0
-1.0 -
RipResponseBuilder 43 108 0% 40 43
0.7189542771.9%
 

Contributing tests

This file is covered by 6 tests. .

Source view

1    package net.technearts.rip;
2   
3    import static java.nio.charset.StandardCharsets.UTF_8;
4    import static java.util.Arrays.asList;
5    import static java.util.Optional.ofNullable;
6    import static net.technearts.rip.OP.AND;
7    import static net.technearts.rip.OP.OR;
8    import static org.eclipse.jetty.http.HttpStatus.NOT_FOUND_404;
9    import static org.eclipse.jetty.http.HttpStatus.OK_200;
10   
11    import java.io.IOException;
12    import java.nio.file.Files;
13    import java.nio.file.Path;
14    import java.util.Arrays;
15    import java.util.HashMap;
16    import java.util.LinkedHashMap;
17    import java.util.Map;
18    import java.util.Optional;
19    import java.util.function.BiFunction;
20    import java.util.function.Consumer;
21    import java.util.function.Function;
22    import java.util.function.Predicate;
23   
24    import org.slf4j.Logger;
25    import org.slf4j.LoggerFactory;
26   
27    import net.sf.jmimemagic.Magic;
28    import net.sf.jmimemagic.MagicException;
29    import net.sf.jmimemagic.MagicMatch;
30    import net.sf.jmimemagic.MagicMatchNotFoundException;
31    import net.sf.jmimemagic.MagicParseException;
32    import spark.ModelAndView;
33    import spark.Request;
34    import spark.Response;
35   
 
36    enum OP {
37    AND, OR;
38    }
39   
40    /**
41    * Um construtor de respostas com base no conteúdo do body da requisição http.
42    */
 
43    public class RipResponseBuilder {
44    // TODO passar os três mapas para dentro do RipRoute
45    private static final Map<RipRoute, Map<Predicate<Request>, RipResponse>> CONDITIONS = new LinkedHashMap<>();
46    private static final Map<RipRoute, BiFunction<Request, Response, String>> LOGS = new LinkedHashMap<>();
47    private static final Logger LOG = LoggerFactory
48    .getLogger(RipResponseBuilder.class);
49   
50    private RipRoute route;
51    private Predicate<Request> condition;
52    private OP op = AND;
53   
 
54  9 toggle RipResponseBuilder(final RipRoute route) {
55  9 LOG.info("Criando RipResponseBuilder para requisição {} {}",
56    route.getMethod(), route.getPath());
57  9 this.route = route;
58  9 if (!CONDITIONS.containsKey(route)) {
59  5 CONDITIONS.put(route,
60    new LinkedHashMap<Predicate<Request>, RipResponse>());
61    }
62    // TODO isso deveria estar no RipResponse
63  9 route.route = (req, res) -> {
64  3871 final Optional<Map.Entry<Predicate<Request>, RipResponse>> optional = CONDITIONS
65    .get(route).entrySet().stream()
66    .filter(entry -> entry.getKey().test(req)).findFirst();
67  3969 RipResponse response;
68  3985 String result;
69  3902 if (optional.isPresent()) {
70  3943 response = optional.get().getValue();
71  3930 LOG.debug("Requisição para {}:\n{}", req.pathInfo(), req.body());
72  3946 LOG.debug("Respondendo com \n{}", response.getContent());
73  3915 res.status(response.getStatus());
74  3932 result = response.getContent();
75  3917 if (response.getContentType() == null) {
76  3950 res.header("Content-Type", contentType(result.getBytes(UTF_8)));
77    } else {
78  0 res.header("Content-Type", response.getContentType());
79    }
80    } else {
81  0 res.status(NOT_FOUND_404);
82  0 LOG.warn("Resposta para {} {} não encontrada", route.getMethod(),
83    route.getPath());
84  0 result = "";
85    }
86  3937 ofNullable(LOGS.get(route)).ifPresent(f -> f.apply(req, res));
87  3907 return result;
88    };
89  9 route.templateRoute = (req, res) -> {
90  1 final Optional<Map.Entry<Predicate<Request>, RipResponse>> optional = CONDITIONS
91    .get(route).entrySet().stream()
92    .filter(entry -> entry.getKey().test(req)).findFirst();
93  1 RipResponse response;
94  1 ModelAndView result;
95  1 if (optional.isPresent()) {
96  1 response = optional.get().getValue();
97  1 LOG.debug("Respondendo com \n{}", response.getContent());
98  1 res.status(response.getStatus());
99  1 final Map<String, Object> attributes = new HashMap<>();
100  1 for (final Map.Entry<String, Function<Request, String>> f : response
101    .getAttributes().entrySet()) {
102  2 attributes.put(f.getKey(), f.getValue().apply(req));
103    }
104  1 if (response.getContentType() != null) {
105  0 res.header("Content-Type", response.getContentType());
106    } else {
107  1 res.header("Content-Type", "text/plain");
108    }
109  1 result = new ModelAndView(attributes, response.getContent());
110    } else {
111  0 res.status(NOT_FOUND_404);
112  0 LOG.debug("Resposta para {} {} não encontrada", route.getMethod(),
113    route.getPath());
114  0 result = null;
115    }
116  1 return result;
117    };
118    }
119   
120    /**
121    * Operador lógico E
122    *
123    * @return this
124    */
 
125  1 toggle public RipResponseBuilder and() {
126  1 op = AND;
127  1 return this;
128    }
129   
130    /**
131    * Cria uma resposta utilizando um arquivo de template, substituindo as
132    * variáveis no arquivo pelo resultado de cada aplicação da função.
133    *
134    * O mapa é alterado através de um <code>Consumer</code> para conveniência
135    *
136    * @param template o arquivo de template
137    * @param consumers lista de alterações ao mapa de variáveis X funções
138    */
 
139  0 toggle @SafeVarargs
140    public final void buildResponse(final String template,
141    final Consumer<Map<String, Function<Request, String>>>... consumers) {
142  0 buildResponse(template, OK_200, consumers);
143    }
144   
 
145  1 toggle @SafeVarargs
146    public final void buildResponse(final String template, final int status,
147    final Consumer<Map<String, Function<Request, String>>>... consumers) {
148  1 buildResponse(template, status, null, consumers);
149    }
150   
151    /**
152    * Cria uma resposta utilizando um arquivo de template, substituindo as
153    * variáveis no arquivo pelo resultado de cada aplicação da função.
154    *
155    * O mapa é alterado através de um <code>Consumer</code> para conveniência
156    *
157    * @param template o arquivo de template
158    * @param status o status de retorno
159    * @param consumers lista de alterações ao mapa de variáveis X funções
160    */
 
161  1 toggle @SafeVarargs
162    public final void buildResponse(final String template, final int status,
163    final String contentType,
164    final Consumer<Map<String, Function<Request, String>>>... consumers) {
165  1 final Map<String, Function<Request, String>> attributes = new HashMap<>();
166  1 for (final Consumer<Map<String, Function<Request, String>>> consumer : consumers) {
167  2 consumer.accept(attributes);
168    }
169  1 buildResponse(template, status, contentType, attributes);
170    }
171   
172    /**
173    * Cria uma resposta utilizando um arquivo de template, substituindo as
174    * variáveis no arquivo pelo resultado de cada aplicação da função.
175    *
176    * @param template o arquivo de template
177    * @param status o status de retorno
178    * @param attributes lista de alterações ao mapa de variáveis X funções
179    */
 
180  1 toggle public final void buildResponse(final String template, final int status,
181    final String contentType,
182    final Map<String, Function<Request, String>> attributes) {
183  1 if (condition == null) {
184  1 condition = s -> true;
185    }
186  1 final RipResponse res = new RipResponse(attributes, template, status,
187    contentType);
188  1 CONDITIONS.get(route).put(condition, res);
189  1 route.createTemplateMethod();
190    }
191   
192    /**
193    * Cria uma resposta utilizando um arquivo de template, substituindo as
194    * variáveis no arquivo pelo resultado de cada aplicação da função.
195    *
196    * @param template o arquivo de template
197    * @param attributes lista de alterações ao mapa de variáveis X funções
198    */
 
199  0 toggle public final void buildResponse(final String template,
200    final Map<String, Function<Request, String>> attributes) {
201  0 buildResponse(template, OK_200, null, attributes);
202    }
203   
 
204  0 toggle @SafeVarargs
205    public final void buildResponse(final String template,
206    final String contentType,
207    final Consumer<Map<String, Function<Request, String>>>... consumers) {
208  0 buildResponse(template, OK_200, contentType, consumers);
209    }
210   
 
211  0 toggle public final void buildResponse(final String template,
212    final String contentType,
213    final Map<String, Function<Request, String>> attributes) {
214  0 buildResponse(template, OK_200, contentType, attributes);
215    }
216   
217    /**
218    * Verifica se o body da requisição http contém determinada sequência
219    *
220    * @param content o conteúdo a ser checado no body
221    * @return this
222    */
 
223  5 toggle public RipResponseBuilder contains(final String content) {
224  5 final Predicate<Request> newCondition = req -> req.body().contains(content);
225  5 updateConditions(newCondition);
226  5 return this;
227    }
228   
229    /**
230    * Verifica se o body da requisição http contém todas as sequências informadas
231    *
232    * @param contents os conteúdos a serem checados no body
233    * @return this
234    */
 
235  1 toggle public RipResponseBuilder containsAll(final String... contents) {
236  1 final Predicate<Request> newCondition = req -> Arrays.asList(contents)
237    .stream().allMatch(req.body()::contains);
238  1 updateConditions(newCondition);
239  1 return this;
240    }
241   
242    /**
243    * Verifica se o body da requisição http contém alguma das sequências
244    * informadas
245    *
246    * @param contents os conteúdos a serem checados no body
247    * @return this
248    */
 
249  1 toggle public RipResponseBuilder containsAny(final String... contents) {
250  1 final Predicate<Request> newCondition = req -> Arrays.asList(contents)
251    .stream().anyMatch(req.body()::contains);
252  1 updateConditions(newCondition);
253  1 return this;
254    }
255   
 
256  3935 toggle private String contentType(final byte[] stream) {
257  3929 MagicMatch match = null;
258  3955 try {
259  3975 match = Magic.getMagicMatch(stream, false);
260  3937 if (match.getSubMatches().size() > 0) {
261  0 return match.getSubMatches().toArray()[0].toString();
262    }
263    } catch (MagicParseException | MagicMatchNotFoundException | MagicException
264    | NullPointerException e) {
265  0 return "text/html;charset=utf-8";
266    }
267  3973 return match.getMimeType();
268    }
269   
270    /**
271    * Cria um log dos objetos Request/Response na chamada ao Route. Várias
272    * chamadas ao método apenas substituem o log criado anteriormente.
273    *
274    * @param f a Função que irá retornar a mensagem de log
275    * @return this
276    */
 
277  0 toggle public RipResponseBuilder log(final BiFunction<Request, Response, String> f) {
278  0 LOGS.put(route, f);
279  0 return this;
280    }
281   
282    /**
283    * Verifica se o body da requisição http contém determinada sequência
284    *
285    * @param condition a condição a ser checada
286    * @return this
287    */
 
288  0 toggle public RipResponseBuilder matches(final Predicate<Request> condition) {
289  0 updateConditions(condition);
290  0 return this;
291    }
292   
293    /**
294    * Verifica se o body da requisição http contém todas as sequências informadas
295    *
296    * @param conditions as condições a serem checadas
297    * @return this
298    */
 
299  0 toggle public RipResponseBuilder matchesAll(
300    @SuppressWarnings("unchecked") final Predicate<Request>... conditions) {
301  0 final Predicate<Request> newCondition = asList(conditions).stream()
302    .reduce(req -> true, Predicate::and);
303  0 updateConditions(newCondition);
304  0 return this;
305    }
306   
307    /**
308    * Verifica se o body da requisição http contém alguma das sequências
309    * informadas
310    *
311    * @param conditions as condições a serem checadas
312    * @return this
313    */
 
314  0 toggle public RipResponseBuilder matchesAny(
315    @SuppressWarnings("unchecked") final Predicate<Request>... conditions) {
316  0 final Predicate<Request> newCondition = asList(conditions).stream()
317    .reduce(req -> false, Predicate::or);
318  0 updateConditions(newCondition);
319  0 return this;
320    }
321   
322    /**
323    * Operador lógico OU
324    *
325    * @return this
326    */
 
327  1 toggle public RipResponseBuilder or() {
328  1 op = OR;
329  1 return this;
330    }
331   
332    /**
333    * Cria uma resposta com o conteúdo do arquivo informado. Essa é uma operação
334    * terminal.
335    *
336    * @param withFile o caminho relativo para o arquivo, com raiz em
337    * src/main/resources
338    */
 
339  2 toggle public void respond(final Path withFile) {
340  2 respond(withFile, OK_200);
341    }
342   
343    /**
344    * Cria uma resposta com o conteúdo do arquivo informado, retornando o
345    * <code>status</code> http. Essa é uma operação terminal.
346    *
347    * @param withFile o caminho relativo para o arquivo, com raiz em
348    * src/main/resources
349    * @param status o status de retorno
350    */
 
351  2 toggle public void respond(final Path withFile, final int status) {
352  2 respond(withFile, status, null);
353    }
354   
 
355  2 toggle public void respond(final Path withFile, final int status,
356    final String contentType) {
357  2 try {
358  2 respond(new String(Files.readAllBytes(withFile)), status, contentType);
359    } catch (final IOException e) {
360  0 respond("Arquivo não encontrado.", NOT_FOUND_404);
361    }
362    }
363   
 
364  0 toggle public void respond(final Path withFile, final String contentType) {
365  0 respond(withFile, OK_200, contentType);
366    }
367   
368    /**
369    * Cria uma resposta com o conteúdo do arquivo informado, retornando o
370    * <code>status</code> http. Essa é uma operação terminal.
371    *
372    * @param response o conteúdo do corpo da mensagem de retorno
373    */
 
374  4 toggle public void respond(final String response) {
375  4 respond(response, OK_200);
376    }
377   
378    /**
379    * Cria uma resposta com o conteúdo do arquivo informado, retornando o
380    * <code>status</code> http. Essa é uma operação terminal.
381    *
382    * @param response o conteúdo do corpo da mensagem de retorno
383    * @param status o status de retorno
384    */
 
385  6 toggle public void respond(final String response, final int status) {
386  6 respond(response, status, null);
387    }
388   
 
389  8 toggle public void respond(final String response, final int status,
390    final String contentType) {
391  8 if (condition == null) {
392  3 condition = s -> true;
393    }
394  8 final RipResponse res = new RipResponse(response, status, contentType);
395  8 CONDITIONS.get(route).put(condition, res);
396  8 route.createMethod();
397    }
398   
 
399  0 toggle public void respond(final String response, final String contentType) {
400  0 respond(response, OK_200, contentType);
401    }
402   
 
403  7 toggle private void updateConditions(final Predicate<Request> newCondition) {
404  7 if (condition == null) {
405  5 condition = newCondition;
406    } else {
407  2 switch (op) {
408  1 case OR:
409  1 condition = condition.or(newCondition);
410  1 break;
411  1 case AND:
412  1 condition = condition.and(newCondition);
413  1 break;
414    }
415    }
416    }
417   
418    }