package org.rakam.server.http;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.handler.codec.http.cookie.Cookie;
import java.lang.reflect.Type;
import java.util.List;
import java.util.function.Function;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
public interface IRequestParameter<T>
{
T extract(ObjectNode node, RakamHttpRequest request);
class HeaderParameter<T>
implements IRequestParameter
{
public final String name;
public final boolean required;
private final Function<String, T> mapper;
HeaderParameter(String name, boolean required, Function<String, T> mapper)
{
this.name = name;
this.required = required;
this.mapper = mapper;
}
@Override
public T extract(ObjectNode node, RakamHttpRequest request)
{
String value = request.headers().get(name);
if (value == null && required) {
throw new HttpRequestException("'" + name + "' header parameter is required.", BAD_REQUEST);
}
return mapper.apply(value);
}
}
class CookieParameter
implements IRequestParameter<String>
{
public final String name;
public final boolean required;
CookieParameter(String name, boolean required)
{
this.name = name;
this.required = required;
}
@Override
public String extract(ObjectNode node, RakamHttpRequest request)
{
for (Cookie cookie : request.cookies()) {
if (name.equals(cookie.name())) {
// TODO fixme: the value of cookie parameter always must be String.
return cookie.value();
}
}
if (required) {
throw new HttpRequestException("'" + name + "' cookie is required.", BAD_REQUEST);
}
return null;
}
}
class QueryParameter<T>
implements IRequestParameter<T>
{
public final String name;
public final boolean required;
private final Function<String, T> mapper;
QueryParameter(String name, boolean required, Function<String, T> mapper)
{
this.name = name;
this.required = required;
this.mapper = mapper;
}
@Override
public T extract(ObjectNode node, RakamHttpRequest request)
{
List<String> strings = request.params().get(name);
if ((strings == null || strings.isEmpty())) {
if (required) {
throw new HttpRequestException("'" + name + "' query parameter is required.", BAD_REQUEST);
}
else {
return null;
}
}
return mapper.apply(strings.get(0));
}
}
class BodyParameter
implements IRequestParameter
{
public final String name;
public final JavaType type;
public final boolean required;
private final ObjectMapper mapper;
BodyParameter(ObjectMapper mapper, String name, Type type, boolean required)
{
this.name = name;
this.type = mapper.constructType(type);
this.mapper = mapper;
this.required = required;
}
public Object extract(ObjectNode node, RakamHttpRequest request)
{
JsonNode value = node.get(name);
Object o;
if (value == null) {
o = null;
}
else {
try {
o = mapper.convertValue(value, type);
}
catch (IllegalArgumentException e) {
throw new HttpRequestException(name +
" body parameter cannot be cast to " + type.toString() + ": "+e.getMessage(), BAD_REQUEST);
}
}
if (required && (o == null || o == NullNode.getInstance())) {
throw new HttpRequestException(name + " body parameter is required", BAD_REQUEST);
}
return o;
}
}
class FullBodyParameter
implements IRequestParameter
{
public final JavaType type;
private final ObjectMapper mapper;
FullBodyParameter(ObjectMapper mapper, Type type)
{
this.type = mapper.constructType(type);
this.mapper = mapper;
}
public Object extract(ObjectNode node, RakamHttpRequest request)
{
try {
return mapper.convertValue(node, type);
}
catch (IllegalArgumentException e) {
throw new HttpRequestException(e.getMessage(), BAD_REQUEST);
}
}
}
}