· 9 years ago · Jan 23, 2017, 12:06 AM
1/*
2 * Copyright (C) 2016 Saurabh Rane
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.fatsecret.platform.services;
17
18import java.io.UnsupportedEncodingException;
19import java.net.URLEncoder;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.List;
23import java.util.Random;
24
25import javax.crypto.Mac;
26import javax.crypto.SecretKey;
27import javax.crypto.spec.SecretKeySpec;
28
29/**
30 * This class helps in building requests for sending them to the fatsecret rest api
31 *
32 * @author Saurabh Rane
33 * @version 1.0
34 */
35public class RequestBuilder {
36 /** A value FatSecret API issues to you which helps this API identify you */
37 final private String APP_KEY;
38
39 /** A secret FatSecret API issues to you which helps this API establish that it really is you */
40 final private String APP_SECRET;
41
42 /**
43 * Request URL
44 * <p>
45 * The URL to make API calls is http://platform.fatsecret.com/rest/server.api
46 */
47 final private String APP_URL = "http://platform.fatsecret.com/rest/server.api";
48
49 /**
50 * The signature method allowed by FatSecret API
51 * <p>
52 * They only support "HMAC-SHA1"
53 */
54 final private String APP_SIGNATURE_METHOD = "HmacSHA1";
55
56 /**
57 * The HTTP Method supported by FatSecret API
58 * <p>
59 * This API only supports GET method
60 */
61 final private String HTTP_METHOD = "GET";
62
63
64 /**
65 * Constructor to set values for APP_KEY and APP_SECRET
66 *
67 * @param APP_KEY a value FatSecret API issues to you which helps this API identify you
68 * @param APP_SECRET a secret FatSecret API issues to you which helps this API establish that it really is you
69 */
70 public RequestBuilder(String APP_KEY, String APP_SECRET) {
71 this.APP_KEY = APP_KEY;
72 this.APP_SECRET = APP_SECRET;
73 }
74
75 /**
76 * Returns randomly generated nonce value for calling the request.
77 *
78 * @return the randomly generated value for nonce.
79 */
80 public String nonce() {
81 Random r = new Random();
82 StringBuffer n = new StringBuffer();
83 for (int i = 0; i < r.nextInt(8) + 2; i++) {
84 n.append(r.nextInt(26) + 'a');
85 }
86 return n.toString();
87 }
88
89 /**
90 * Returns all the oauth parameters and other parameters.
91 *
92 * @return an array of parameter values as "key=value" pair
93 */
94 public String[] generateOauthParams() {
95 String[] a = {
96 "oauth_consumer_key=" + APP_KEY,
97 "oauth_signature_method=HMAC-SHA1",
98 "oauth_timestamp=" + new Long(System.currentTimeMillis() / 1000).toString(),
99 "oauth_nonce=" + nonce(),
100 "oauth_version=1.0",
101 "format=json"
102 };
103 return a;
104 }
105
106 /**
107 * Returns the string generated using params and separator
108 *
109 * @param params an array of parameter values as "key=value" pair
110 * @param separator a separator for joining
111 *
112 * @return the string by appending separator after each parameter from params except the last.
113 */
114 public String join(String[] params, String separator) {
115 StringBuffer b = new StringBuffer();
116 for (int i = 0; i < params.length; i++) {
117 if (i > 0) {
118 b.append(separator);
119 }
120 b.append(params[i]);
121 }
122 return b.toString();
123 }
124
125 /**
126 * Returns string generated using params and "&" for signature base and normalized parameters
127 *
128 * @param params an array of parameter values as "key=value" pair
129 * @return the string by appending separator after each parameter from params except the last.
130 */
131 public String paramify(String[] params) {
132 String[] p = Arrays.copyOf(params, params.length);
133 Arrays.sort(p);
134 return join(p, "&");
135 }
136
137 /**
138 * Returns the percent-encoded string for the given url
139 *
140 * @param url URL which is to be encoded using percent-encoding
141 * @return the encoded url
142 */
143 public String encode(String url) {
144 if (url == null)
145 return "";
146
147 try {
148 return URLEncoder.encode(url, "utf-8")
149 .replace("+", "%20")
150 .replace("!", "%21")
151 .replace("*", "%2A")
152 .replace("\\", "%27")
153 .replace("(", "%28")
154 .replace(")", "%29");
155 }
156 catch (UnsupportedEncodingException wow) {
157 throw new RuntimeException(wow.getMessage(), wow);
158 }
159 }
160
161 /**
162 * Returns the signature generated using signature base as text and consumer secret as key
163 *
164 * @param method http method
165 * @param uri request URL - http://platform.fatsecret.com/rest/server.api (Always remains the same)
166 * @param params an array of parameter values as "key=value" pair
167 * @return oauth_signature which will be added to request for calling fatsecret api
168 */
169 public String sign(String method, String uri, String[] params) throws UnsupportedEncodingException {
170 String encodedURI = encode(uri);
171 String encodedParams = encode(paramify(params));
172
173 String[] p = {method, encodedURI, encodedParams};
174
175 String text = join(p, "&");
176 String key = APP_SECRET + "&";
177 SecretKey sk = new SecretKeySpec(key.getBytes(), APP_SIGNATURE_METHOD);
178 String sign = "";
179 try {
180 Mac m = Mac.getInstance(APP_SIGNATURE_METHOD);
181 m.init(sk);
182 sign = encode(new String(Base64.encode(m.doFinal(text.getBytes()), Base64.DEFAULT)).trim());
183 } catch(java.security.NoSuchAlgorithmException e) {
184 } catch(java.security.InvalidKeyException e) {
185 }
186 return sign;
187 }
188
189 /**
190 * Returns the rest url which will be sent to fatsecret platform server for searching food items based on search terms and page number
191 *
192 * @param query search terms for querying food items
193 * @param pageNumber page Number to search the food items
194 * @return rest url which will be sent to fatsecret platform server for searching food items
195 */
196 public String buildFoodsSearchUrl(String query, int pageNumber) throws Exception {
197 List<String> params = new ArrayList<String>(Arrays.asList(generateOauthParams()));
198 String[] template = new String[1];
199 params.add("method=foods.search");
200 params.add("max_results=50");
201 params.add("page_number=" + pageNumber);
202 params.add("search_expression=" + encode(query));
203 params.add("oauth_signature=" + sign(HTTP_METHOD, APP_URL, params.toArray(template)));
204
205 return APP_URL + "?" + paramify(params.toArray(template));
206 }
207
208 /**
209 * Returns the rest url which will be sent to fatsecret platform server for searching unique food item
210 *
211 * @param id the unique food identifier
212 * @return rest url which will be sent to fatsecret platform server for searching unique food item
213 */
214 public String buildFoodSearchUrl(Long id) throws Exception {
215 List<String> params = new ArrayList<String>(Arrays.asList(generateOauthParams()));
216 String[] template = new String[1];
217 params.add("method=food.get");
218 params.add("food_id=" + id);
219 params.add("oauth_signature=" + sign(HTTP_METHOD, APP_URL, params.toArray(template)));
220
221 return APP_URL + "?" + paramify(params.toArray(template));
222 }
223
224 /**
225 * Returns the rest url which will be sent to fatsecret platform server for searching recipes
226 *
227 * @param query search terms for querying recipes
228 * @param pageNumber page Number to search the recipes
229 * @return rest url which will be sent to fatsecret platform server for searching recipes
230 */
231 public String buildRecipesSearchUrl(String query, int pageNumber) throws Exception {
232 List<String> params = new ArrayList<String>(Arrays.asList(generateOauthParams()));
233 String[] template = new String[1];
234 params.add("method=recipes.search");
235 params.add("max_results=50");
236 params.add("page_number=" + pageNumber);
237 params.add("search_expression=" + encode(query));
238 params.add("oauth_signature=" + sign(HTTP_METHOD, APP_URL, params.toArray(template)));
239
240 return APP_URL + "?" + paramify(params.toArray(template));
241 }
242
243 /**
244 * Returns the rest url which will be sent to fatsecret platform server for searching unique recipe
245 *
246 * @param id the unique recipe identifier
247 * @return rest url which will be sent to fatsecret platform server for searching unique recipe
248 */
249 public String buildRecipeSearchUrl(Long id) throws Exception {
250 List<String> params = new ArrayList<String>(Arrays.asList(generateOauthParams()));
251 String[] template = new String[1];
252 params.add("method=recipe.get");
253 params.add("recipe_id=" + id);
254 params.add("oauth_signature=" + sign(HTTP_METHOD, APP_URL, params.toArray(template)));
255
256 return APP_URL + "?" + paramify(params.toArray(template));
257 }
258}