001/*
002 * Copyright (c) 2010-2023 Mark Allen, Norbert Bartels.
003 *
004 * Permission is hereby granted, free of charge, to any person obtaining a copy
005 * of this software and associated documentation files (the "Software"), to deal
006 * in the Software without restriction, including without limitation the rights
007 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
008 * copies of the Software, and to permit persons to whom the Software is
009 * furnished to do so, subject to the following conditions:
010 *
011 * The above copyright notice and this permission notice shall be included in
012 * all copies or substantial portions of the Software.
013 *
014 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
015 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
016 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
017 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
018 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
019 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
020 * THE SOFTWARE.
021 */
022package com.restfb;
023
024import static com.restfb.util.StringUtils.isBlank;
025import static com.restfb.util.StringUtils.trimToEmpty;
026import static java.lang.String.format;
027
028import java.io.IOException;
029import java.util.ArrayList;
030import java.util.List;
031import java.util.Optional;
032
033import com.restfb.util.StringUtils;
034
035/**
036 * Specifies how a class that sends {@code HTTP} requests to the Facebook API endpoint must operate.
037 * 
038 * @author <a href="http://restfb.com">Mark Allen</a>
039 */
040public interface WebRequestor {
041  /**
042   * Encapsulates an HTTP response body and status code.
043   * 
044   * @author <a href="http://restfb.com">Mark Allen</a>
045   */
046  class Response {
047    /**
048     * HTTP response status code (e.g. 200).
049     */
050    private final Integer statusCode;
051
052    /**
053     * HTTP response body as text.
054     */
055    private final String body;
056
057    /**
058     * Creates a response with the given HTTP status code and response body as text.
059     * 
060     * @param statusCode
061     *          The HTTP status code of the response.
062     * @param body
063     *          The response body as text.
064     */
065    public Response(Integer statusCode, String body) {
066      this.statusCode = statusCode;
067      this.body = trimToEmpty(body);
068    }
069
070    /**
071     * Gets the HTTP status code.
072     * 
073     * @return The HTTP status code.
074     */
075    public Integer getStatusCode() {
076      return statusCode;
077    }
078
079    /**
080     * Gets the HTTP response body as text.
081     * 
082     * @return The HTTP response body as text.
083     */
084    public String getBody() {
085      return body;
086    }
087
088    /**
089     * @see java.lang.Object#toString()
090     */
091    @Override
092    public String toString() {
093      if (isBlank(getBody())) {
094        return format("HTTP status code %d and an empty response body.", getStatusCode());
095      }
096      return format("HTTP status code %d and response body: %s", getStatusCode(), getBody());
097    }
098  }
099
100  /**
101   * encapsulates the HTTP Request configuration
102   */
103  class Request {
104
105    private final String url;
106
107    private final Optional<String> headerAccessToken;
108
109    private String parameters;
110
111    private Body body;
112
113    private List<BinaryAttachment> binaryAttachments;
114
115    /**
116     * Simple http request with url and a header access token
117     * 
118     * @param url
119     *          the endpoint the request ist directed to
120     * @param headerAccessToken
121     *          the HTTP header access token (may be {@code null})
122     */
123    public Request(String url, String headerAccessToken) {
124      this(url, headerAccessToken, null);
125    }
126
127    /**
128     * Simple http request with url and a header access token
129     *
130     * @param url
131     *          the endpoint the request ist directed to
132     * @param headerAccessToken
133     *          the HTTP header access token (may be {@code null})
134     * @param parameters
135     *          the query parameter string
136     */
137    public Request(String url, String headerAccessToken, String parameters) {
138      this(url, headerAccessToken, parameters, null);
139    }
140
141    /**
142     * Simple http request with url and a header access token
143     *
144     * @param url
145     *          the endpoint the request ist directed to
146     * @param headerAccessToken
147     *          the HTTP header access token (may be {@code null})
148     * @param parameters
149     *          the query parameter string
150     * @param attachments
151     *          list of binary attachments
152     */
153    public Request(String url, String headerAccessToken, String parameters, List<BinaryAttachment> attachments) {
154      this.url = url;
155      this.headerAccessToken = Optional.ofNullable(headerAccessToken);
156      this.parameters = parameters;
157      setBinaryAttachments(attachments);
158    }
159
160    public String getUrl() {
161      return url;
162    }
163
164    public String getHeaderAccessToken() {
165      return headerAccessToken.orElse(null);
166    }
167
168    public boolean hasHeaderAccessToken() {
169      return headerAccessToken.isPresent();
170    }
171
172    public String getParameters() {
173      return parameters;
174    }
175
176    public List<BinaryAttachment> getBinaryAttachments() {
177      return Optional.ofNullable(binaryAttachments).orElse(new ArrayList<>());
178    }
179
180    public void setBinaryAttachments(List<BinaryAttachment> binaryAttachments) {
181      this.binaryAttachments = Optional.ofNullable(binaryAttachments).orElse(new ArrayList<>());
182    }
183
184    public String getFullUrl() {
185      if (!StringUtils.isBlank(parameters)) {
186        if (url != null && url.contains("?")) {
187          return url + "&" + parameters;
188        }
189        return url + "?" + parameters;
190      }
191      return url;
192    }
193
194    @Override
195    public String toString() {
196      return format("Request to url %s with parameters %s. Header access token: %b", getUrl(), getParameters(),
197        hasHeaderAccessToken());
198    }
199
200    public void setBody(Body body) {
201      this.body = body;
202    }
203
204    public Body getBody() {
205      return body;
206    }
207
208    public boolean hasBody() {
209      return body != null;
210    }
211  }
212
213  /**
214   * Given a Facebook API endpoint URL, execute a {@code GET} against it.
215   * 
216   * @param request
217   *          The request data for the {@code GET} request
218   * @return HTTP response data.
219   * @throws IOException
220   *           If an error occurs while performing the {@code GET} operation.
221   * @since 1.5
222   */
223  Response executeGet(Request request) throws IOException;
224
225  /**
226   * Given a Facebook API endpoint URL and parameter string, execute a {@code POST} to the endpoint URL.
227   * 
228   * @param request
229   *          The request data used for the {@code POST} request.
230   * @return HTTP response data.
231   * @throws IOException
232   *           If an error occurs while performing the {@code POST}.
233   */
234  Response executePost(Request request) throws IOException;
235
236  /**
237   * Given a Facebook API endpoint URL and parameter string, execute a {@code DELETE} to the endpoint URL.
238   * 
239   * @param request
240   *          The request data used for the {@code DELETE} request.
241   * @return HTTP response data.
242   * @throws IOException
243   *           If an error occurs while performing the {@code DELETE}.
244   */
245  Response executeDelete(Request request) throws IOException;
246
247  /**
248   * Provides access to the facebook header information.
249   * 
250   * The fields <code>x-fb-rev</code>, <code>x-fb-trace-id</code> and <code>x-fb-debug</code> are checked and returned
251   * in a single container of the type {@link DebugHeaderInfo}
252   * 
253   * @return container with the explained facebook debug header information
254   */
255  DebugHeaderInfo getDebugHeaderInfo();
256}