1 module vksdk.client.AbstractQueryBuilder;
2 
3 import std.conv;
4 import std.uri;
5 
6 import vksdk.client.ApiRequest;
7 import vksdk.client.Lang;
8 import vksdk.client.VkApiClient;
9 import vksdk.queries.EnumParam;
10 
11 /**
12  * Query builder for API request
13  */
14 abstract class AbstractQueryBuilder(T, R) : ApiRequest!R {
15 
16     private string[string] params;
17 
18     private string method;
19 
20     /**
21      * Creates a AbstractQueryBuilder instance that can be used to build api request with various parameters
22      *
23      * @param client VK API client
24      * @param method method name
25      * @param type   type of method response
26      */
27     this(VkApiClient client, string method) {
28         super(client.getApiEndpoint() ~ method, client.getTransportClient());
29         this.method = method;
30         setVersion(client.getVersion);
31     }
32 
33     /**
34      * Creates a AbstractQueryBuilder instance that can be used to build api request with various parameters
35      *
36      * @param client   VK API client
37      * @param endpoint API endpoint
38      * @param method   method name
39      * @param type     type of method response
40      */
41     this(VkApiClient client, string endpoint, string method) {
42         super(endpoint ~ method, client.getTransportClient());
43         setVersion(client.getVersion);
44     }
45 
46     /**
47      * Convert boolean value to integer flag
48      *
49      * @param param value
50      * @return integer flag
51      */
52     private static string boolAsParam(bool param) {
53         return param ? "1" : "0";
54     }
55 
56     /**
57      * Build request parameter map to query
58      *
59      * @param params parameters
60      * @return string query
61      */
62     private static string mapToGetString(string[string] params) {
63 		string result;
64 		
65 		foreach (key, value; params) {
66 			if (result == null) {
67 				result = "";
68 			} else {
69 				result ~= "&";
70 			}
71 			
72 			result ~= key ~ "=" ~ value != null ? value : "";
73 		}
74 		
75         return result;
76     }
77 
78     /**
79      * Encode request data
80      *
81      * @param data request data
82      * @return encoded data
83      */
84     private static string escape(string data) {
85         return encode(data);
86     }
87 
88     /**
89      * Set access token
90      *
91      * @param value access token
92      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
93      */
94     protected T accessToken(string value) {
95         return unsafeParam("access_token", value);
96     }
97 
98     /**
99      * Set lang
100      *
101      * @param value lang
102      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
103      */
104     T lang(Lang value) {
105         return unsafeParam("lang", value.getValue());
106     }
107 
108     /**
109      * Set version
110      *
111      * @param value version
112      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
113      */
114     protected T setVersion(string value) {
115         return unsafeParam("v", value);
116     }
117 
118 
119     /**
120      * Set captcha sid
121      *
122      * @param value captcha sid
123      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
124      */
125     T captchaSid(string value) {
126         return unsafeParam("captcha_sid", value);
127     }
128 
129     /**
130      * Set captcha key
131      *
132      * @param value captcha key
133      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
134      */
135     T captchaKey(string value) {
136         return unsafeParam("captcha_key", value);
137     }
138 
139     /**
140      * Set confirmation
141      *
142      * @param value confirm
143      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
144      */
145     T confirm(bool value) {
146         return unsafeParam("confirm", value);
147     }
148 
149 
150     /**
151      * Set parameter
152      *
153      * @param key   name of parameter
154      * @param value value of parameter
155      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
156      */
157     T unsafeParam(string key, string value) {
158         params[key] = value;
159         return getThis();
160     }
161 
162     /**
163      * Set parameter
164      *
165      * @param key   name of parameter
166      * @param value value of parameter
167      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
168      */
169     T unsafeParam(string key, int value) {
170         return unsafeParam(key, to!string(value));
171     }
172 
173     /**
174      * Set parameter
175      *
176      * @param key   name of parameter
177      * @param value value of parameter
178      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
179      */
180     T unsafeParam(string key, bool value) {
181         return unsafeParam(key, boolAsParam(value));
182     }
183 
184     /**
185      * Set parameter
186      *
187      * @param key   name of parameter
188      * @param value value of parameter
189      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
190      */
191     T unsafeParam(string key, T[] values) {
192 		string resValue = null;
193 		foreach (value; values) {
194 			if (resValue == null) {
195 				resValue = "";
196 			} else {
197 				resValue ~= ",";
198 			}
199 			
200 			resValue ~= value.toString;
201 		}
202 		
203         return unsafeParam(key, resValue);
204     }
205 
206     /**
207      * Set parameter
208      *
209      * @param key   name of parameter
210      * @param value value of parameter
211      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
212      */
213     T unsafeParam(A...)(string key, A values) pure @safe nothrow {
214 		A[] resValue;
215 		foreach (value; values) {
216 			resValue ~= value;
217 		}
218 		
219         return unsafeParam(key, resValue);
220     }
221 
222     /**
223      * Set parameter
224      *
225      * @param key   name of parameter
226      * @param value value of parameter
227      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
228      */
229     T unsafeParam(string key, int[] values) {
230 		string resValue = null;
231 		foreach (value; values) {
232 			if (resValue == null) {
233 				resValue = "";
234 			} else {
235 				resValue ~= ",";
236 			}
237 			
238 			resValue ~= value;
239 		}
240 		
241         return unsafeParam(key, resValue);
242     }
243 
244     /**
245      * Set parameter
246      *
247      * @param key   name of parameter
248      * @param value value of parameter
249      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
250      */
251     T unsafeParam(string key, double value) {
252         return unsafeParam(key, to!string(value));
253     }
254 
255     /**
256      * Set parameter
257      *
258      * @param key   name of parameter
259      * @param value value of parameter
260      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
261      */
262     T unsafeParam(string key, EnumParam value) {
263         return unsafeParam(key, value.getValue());
264     }
265 
266     /**
267      * Set parameter
268      *
269      * @param key    name of parameter
270      * @param fields value of parameter
271      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
272      */
273     T unsafeParam(A...)(string key, EnumParam[] fields) {
274 		String resValue = null;
275 		foreach (field; fields) {
276 			if (resValue == null) {
277 				resValue = "";
278 			} else {
279 				resValue ~= ",";
280 			}
281 			
282 			resValue ~= field.getValue;
283 		}
284 		
285         return unsafeParam(key, resValue);
286     }
287 
288     /**
289      * Set parameter
290      *
291      * @param key    name of parameter
292      * @param fields value of parameter
293      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
294      */
295     T unsafeParam(string key, EnumParam[] fields) {
296 		string resValue = null;
297 		foreach (field; fields) {
298 			if (resValue == null) {
299 				resValue = "";
300 			} else {
301 				resValue ~= ",";
302 			}
303 			
304 			resValue ~= field.getValue;
305 		}
306 		
307         return unsafeParam(key, resValue);
308     }
309 
310     override
311     protected string getBody() {
312         return mapToGetString(build());
313     }
314 
315     /**
316      * Get reference to this object
317      *
318      * @return a reference to this {@code AbstractQueryBuilder} object to fulfill the "Builder" pattern.
319      */
320     protected abstract T getThis();
321 
322     /**
323      * Get list of required parameter names
324      *
325      * @return list of names
326      */
327     protected abstract string[] essentialKeys();
328 
329     /**
330      * Get map of parameter values
331      *
332      * @return map of values
333      */
334     string[string] build() {
335 		import std.algorithm;
336 		import std.conv;
337 
338 		// TODO remove
339 		// if (!params.keySet().containsAll(essentialKeys())) {
340 		
341 		if (params.keys.all!(a => essentialKeys().canFind(a))) {
342 			throw new Exception("Not all the keys are passed: essential keys are " ~to!string(essentialKeys()));
343 		}
344 		//all!"a > 0"([1, 2, 3, 4]) 
345 		//if (essentialKeys().all(a => params.canFind(a))) {
346 		//	throw new Exception("Not all the keys are passed: essential keys are " ~ essentialKeys());
347 		//}
348 
349         return params.dup;
350     }
351 
352     /**
353      * Get method name
354      *
355      * @return method name
356      */
357     string getMethod() {
358         return method;
359     }
360 }