11package info .unterrainer .commons .restclient ;
22
3+ import java .io .UnsupportedEncodingException ;
4+ import java .net .URLEncoder ;
5+ import java .util .ArrayList ;
36import java .util .HashMap ;
7+ import java .util .List ;
48import java .util .Map ;
9+ import java .util .stream .Collectors ;
10+
11+ import org .mapstruct .ap .internal .util .Strings ;
512
613import com .fasterxml .jackson .core .type .TypeReference ;
714import com .fasterxml .jackson .databind .JavaType ;
1017import info .unterrainer .commons .restclient .RestClient .HttpGetCall ;
1118import lombok .AccessLevel ;
1219import lombok .RequiredArgsConstructor ;
20+ import lombok .extern .slf4j .Slf4j ;
1321
22+ @ Slf4j
1423@ RequiredArgsConstructor (access = AccessLevel .PACKAGE )
1524public abstract class BaseBuilder <T , R extends BaseBuilder <T , R >> {
1625
@@ -23,36 +32,121 @@ protected enum Retry {
2332 protected final RestClient client ;
2433 protected final Class <?> type ;
2534
26- protected String url ;
35+ protected List < String > url = new ArrayList <>() ;
2736 protected JavaType javaType ;
2837 protected TypeReference <?> typeReference ;
2938 protected Map <String , String > headers = new HashMap <>();
39+ protected Map <String , String > parameters = new HashMap <>();
3040 protected Retry retry = Retry .ONCE ;
3141
42+ /**
43+ * Add a string to an URL.
44+ * <p>
45+ * The URL will be composed of all the parts you add.<br>
46+ * The parts will be checked for trailing and leading slashes, which will be
47+ * removed before adding the URL-part (separated by slashes).<br>
48+ * Null values are ignored. So to add an empty URL-part that should result in
49+ * something like this: 'here/we//go' (between here and go), just add an empty
50+ * string.
51+ *
52+ * @param urlPart the part of the URL to add
53+ * @return a {@link BaseBuilder} to provide a fluent interface.
54+ */
3255 @ SuppressWarnings ("unchecked" )
33- public R url (final String url ) {
34- this .url = url ;
56+ public R addUrl (final String urlPart ) {
57+ if (urlPart != null )
58+ url .add (urlPart );
3559 return (R ) this ;
3660 }
3761
62+ /**
63+ * Sets the return-type to be of {@link ListJson} with the generic type you
64+ * provided when starting this builder.
65+ *
66+ * @return a {@link BaseBuilder} to provide a fluent interface.
67+ */
3868 @ SuppressWarnings ("unchecked" )
3969 public R isListJson () {
4070 javaType = client .jsonMapper .getTypeFactory ().constructParametricType (ListJson .class , type );
4171 return (R ) this ;
4272 }
4373
74+ /**
75+ * Adds a header to the call.
76+ * <p>
77+ * All the headers are saved to a map and added later on, when you make the
78+ * call.<br>
79+ * Input containing null as key or value will be ignored.
80+ *
81+ * @param key the key of the header-field to add
82+ * @param value the value of the header-field to add
83+ * @return a {@link BaseBuilder} to provide a fluent interface.
84+ */
4485 @ SuppressWarnings ("unchecked" )
4586 public R addHeader (final String key , final String value ) {
46- headers .put (key , value );
87+ if (key != null && value != null )
88+ headers .put (key , value );
89+ return (R ) this ;
90+ }
91+
92+ /**
93+ * Add a query-parameter to the call.
94+ * <p>
95+ * All the parameters are saved in a map and added later on, when you make the
96+ * call.<br>
97+ * The parameters will be checked for trailing and leading '&' and '?', which
98+ * will be removed before adding it to the map.<br>
99+ * Input containing null as key or value will be ignored.
100+ *
101+ * @param key the key of the parameter to add
102+ * @param value the value of the parameter to add
103+ * @return a {@link BaseBuilder} to provide a fluent interface.
104+ */
105+ @ SuppressWarnings ("unchecked" )
106+ public R addParam (final String key , final String value ) {
107+ if (key != null && value != null ) {
108+ String k = cutLeadingTrailing ("&" , key );
109+ k = cutLeadingTrailing ("?" , k );
110+ try {
111+ k = URLEncoder .encode (k , "UTF-8" );
112+ } catch (UnsupportedEncodingException e ) {
113+ log .error (String .format ("The given key-value %s could not be URL-encoded." , k ), e );
114+ }
115+
116+ String v = cutLeadingTrailing ("&" , value );
117+ v = cutLeadingTrailing ("?" , v );
118+ try {
119+ v = URLEncoder .encode (v , "UTF-8" );
120+ } catch (UnsupportedEncodingException e ) {
121+ log .error (String .format ("The given parameter-value %s could not be URL-encoded." , v ), e );
122+ }
123+ parameters .put (k , v );
124+ }
47125 return (R ) this ;
48126 }
49127
128+ /**
129+ * By specifying this, you tell the client to retry this call for some time, if
130+ * it fails (see {@link RestClient#retryShort(HttpGetCall)}).
131+ * <p>
132+ * The default is to just try it once and then give up and return an error.
133+ *
134+ * @return a {@link BaseBuilder} to provide a fluent interface.
135+ */
50136 @ SuppressWarnings ("unchecked" )
51137 public R retryShort () {
52138 retry = Retry .SHORT ;
53139 return (R ) this ;
54140 }
55141
142+ /**
143+ * By specifying this, you tell the client to retry this call for quite some
144+ * time, if it fails (see {@link RestClient#retryEnduring(HttpGetCall)}).
145+ * <p>
146+ * The default is to just try it once and then give up and return an error.
147+ *
148+ * @return a {@link BaseBuilder} to provide a fluent interface.
149+ */
56150 @ SuppressWarnings ("unchecked" )
57151 public R retryEnduring () {
58152 retry = Retry .ENDURING ;
@@ -63,7 +157,21 @@ public R retryEnduring() {
63157
64158 protected abstract HttpGetCall <T > provideJavaTypeCall (String url , JavaType javaType , Map <String , String > headers );
65159
160+ /**
161+ * Execute the call.
162+ *
163+ * @return the return value of type you specified when you created this builder.
164+ */
66165 public T execute () {
166+ String url = Strings .join (this .url .stream ().map (e -> cutLeadingTrailing ("/" , e )).collect (Collectors .toList ()),
167+ "/" );
168+
169+ String params = Strings .join (
170+ parameters .entrySet ().stream ().map (e -> e .getKey () + "=" + e .getValue ()).collect (Collectors .toList ()),
171+ "&" );
172+ if (!params .isBlank ())
173+ url += "?" + params ;
174+
67175 switch (retry ) {
68176 case SHORT :
69177 return client .retryShort (provide (url , type , javaType , headers ));
@@ -81,4 +189,13 @@ private HttpGetCall<T> provide(final String url, final Class<?> type, final Java
81189 return provideJavaTypeCall (url , javaType , headers );
82190 return provideCall (url , (Class <T >) type , headers );
83191 }
192+
193+ private String cutLeadingTrailing (final String s , final String input ) {
194+ String u = input ;
195+ while (u .startsWith (s ))
196+ u = u .substring (1 );
197+ while (u .endsWith (s ))
198+ u = u .substring (0 , u .length () - 2 );
199+ return u ;
200+ }
84201}
0 commit comments