001
002package io.vrap.rmf.base.client;
003
004import java.nio.charset.StandardCharsets;
005import java.util.HashMap;
006import java.util.Map;
007import java.util.Optional;
008import java.util.function.Function;
009
010import org.apache.commons.lang3.builder.EqualsBuilder;
011import org.apache.commons.lang3.builder.HashCodeBuilder;
012import org.apache.commons.lang3.builder.ToStringBuilder;
013
014public class ApiHttpResponse<U> extends Base implements ContextAware<ApiHttpResponse<U>> {
015
016    private int statusCode;
017    private ApiHttpHeaders headers;
018    private final U body;
019    private String message;
020
021    private Map<Object, Object> contextMap = new HashMap<>();
022
023    public ApiHttpResponse(final int statusCode, final ApiHttpHeaders headers, final U body) {
024        this(statusCode, headers, body, null);
025    }
026
027    public ApiHttpResponse(final int statusCode, final ApiHttpHeaders headers, final U body, final String message) {
028        this.statusCode = statusCode;
029        this.headers = Optional.ofNullable(headers).orElse(new ApiHttpHeaders());
030        this.body = body;
031        this.message = message;
032    }
033
034    public ApiHttpResponse(final int statusCode, final ApiHttpHeaders headers, final U body, final String message,
035            final Map<Object, Object> contextMap) {
036        this.statusCode = statusCode;
037        this.headers = Optional.ofNullable(headers).orElse(new ApiHttpHeaders());
038        this.body = body;
039        this.message = message;
040        this.contextMap = contextMap;
041    }
042
043    public ApiHttpResponse(final ApiHttpResponse<U> response) {
044        this.statusCode = response.statusCode;
045        this.headers = response.headers;
046        this.body = response.body;
047        this.message = response.message;
048        this.contextMap = response.contextMap;
049    }
050
051    public Map<Object, Object> getContextMap() {
052        return contextMap;
053    }
054
055    public ApiHttpResponse<U> withContextMap(final Map<Object, Object> contextMap) {
056        ApiHttpResponse<U> response = copy();
057        response.contextMap = new HashMap<>(contextMap);
058
059        return response;
060    }
061
062    @SuppressWarnings("unchecked")
063    public <T> T getContext(Class<T> key) {
064        return (T) contextMap.get(key);
065    }
066
067    public <T> ApiHttpResponse<U> addContext(T value) {
068        ApiHttpResponse<U> response = copy();
069        Map<Object, Object> contextMap = new HashMap<>(response.contextMap);
070        contextMap.put(value.getClass(), value);
071        if (value instanceof ClassReferenceContext) {
072            contextMap.put(((ClassReferenceContext) value).classReference(), value);
073        }
074
075        response.contextMap = contextMap;
076
077        return response;
078    }
079
080    public Object getContext(Object key) {
081        return contextMap.get(key);
082    }
083
084    public ApiHttpResponse<U> addContext(Object key, Object value) {
085        ApiHttpResponse<U> response = copy();
086        Map<Object, Object> contextMap = new HashMap<>(response.contextMap);
087        contextMap.put(key, value);
088        response.contextMap = contextMap;
089
090        return response;
091    }
092
093    public int getStatusCode() {
094        return statusCode;
095    }
096
097    public ApiHttpResponse<U> withStatusCode(final int statusCode) {
098        ApiHttpResponse<U> response = copy();
099        response.statusCode = statusCode;
100
101        return response;
102    }
103
104    public ApiHttpHeaders getHeaders() {
105        return headers;
106    }
107
108    public ApiHttpResponse<U> withHeaders(final ApiHttpHeaders headers) {
109        ApiHttpResponse<U> response = copy();
110        response.headers = headers;
111
112        return response;
113    }
114
115    public U getBody() {
116        return body;
117    }
118
119    public <TBody> ApiHttpResponse<TBody> withBody(final TBody body) {
120        return new ApiHttpResponse<>(this.statusCode, this.headers, body, this.message, this.contextMap);
121    }
122
123    public <TBody> ApiHttpResponse<TBody> withBody(final Function<U, TBody> bodyFn) {
124        return new ApiHttpResponse<>(this.statusCode, this.headers, bodyFn.apply(this.body), this.message,
125            this.contextMap);
126    }
127
128    public String getMessage() {
129        return message;
130    }
131
132    public ApiHttpResponse<U> withMessage(final String message) {
133        ApiHttpResponse<U> response = copy();
134        response.message = message;
135
136        return response;
137    }
138
139    @Override
140    public String toString() {
141        return new ToStringBuilder(this).append("statusCode", statusCode)
142                .append("headers", headers)
143                .append("textInterpretedBody", getSecuredBody())
144                .toString();
145    }
146
147    static String tryToFilter(final String input) {
148        return input.replaceAll("(\"\\w*([Pp]ass|access_token|refresh_token)\\w*\"):\"[^\"]*\"",
149            "$1:\"**removed from output**\"");
150    }
151
152    public Optional<String> getBodyAsString() {
153        if (body instanceof byte[]) {
154            return Optional.of(body).map(b -> tryToFilter(new String((byte[]) b, StandardCharsets.UTF_8)));
155        }
156        else {
157            return Optional.ofNullable(body).map(Object::toString);
158        }
159    }
160
161    public String getSecuredBody() {
162        return getBodyAsString().orElse("empty body");
163    }
164
165    private ApiHttpResponse<U> copy() {
166        return new ApiHttpResponse<>(this);
167    }
168
169    @Override
170    public boolean equals(Object o) {
171        if (this == o)
172            return true;
173
174        if (o == null || getClass() != o.getClass())
175            return false;
176
177        ApiHttpResponse<?> response = (ApiHttpResponse<?>) o;
178
179        return new EqualsBuilder().append(statusCode, response.statusCode)
180                .append(headers, response.headers)
181                .append(body, response.body)
182                .append(message, response.message)
183                .append(contextMap, response.contextMap)
184                .isEquals();
185    }
186
187    @Override
188    public int hashCode() {
189        return new HashCodeBuilder(17, 37).append(statusCode)
190                .append(headers)
191                .append(body)
192                .append(message)
193                .append(contextMap)
194                .toHashCode();
195    }
196}