1
+
2
+
3
+ part of devspace;
4
+
5
+ // ignore: camel_case_types
6
+ enum kAuthRequirement
7
+ {
8
+ none,
9
+ required ,
10
+ optional,
11
+ }
12
+
13
+ // ignore: camel_case_types
14
+ enum kHttpMethod
15
+ {
16
+ get ,
17
+ post,
18
+ put,
19
+ delete,
20
+ }
21
+
22
+ class HttpStatusCodeInterpreter {
23
+
24
+ final int statusCode;
25
+
26
+ HttpStatusCodeInterpreter ({
27
+ required this .statusCode,
28
+ });
29
+
30
+ bool get isSuccess => statusCode >= 200 && statusCode < 300 ;
31
+ bool get isUnauthorized => statusCode == 401 ;
32
+
33
+
34
+ }
35
+
36
+ class AuthApiResponse extends HttpStatusCodeInterpreter {
37
+
38
+ final Map <String , dynamic >? body;
39
+
40
+ AuthApiResponse ({
41
+ required int statusCode,
42
+ required this .body,
43
+ }) : super (statusCode: statusCode);
44
+
45
+ bool get hasBody => body != null ;
46
+
47
+ }
48
+
49
+
50
+ typedef GetAuthToken = Future <String ?> Function ();
51
+
52
+ class UserNotAuthorizedException implements Exception {
53
+ final dynamic message;
54
+
55
+ UserNotAuthorizedException ({
56
+ this .message = 'User is not authorized' ,
57
+ });
58
+
59
+ }
60
+
61
+ class AuthApiHttpClient {
62
+
63
+ final String baseUrl;
64
+ final GetAuthToken onGetAuthToken;
65
+
66
+ AuthApiHttpClient ({
67
+ required this .baseUrl,
68
+ required this .onGetAuthToken,
69
+ });
70
+
71
+ Future <AuthApiResponse > post ({
72
+ required String apiPath,
73
+ required Map <String , dynamic > body,
74
+ Map <String , String > additionaHeaders = const {},
75
+ kAuthRequirement authRequirement = kAuthRequirement.required ,
76
+ }) async
77
+ {
78
+ return makeRequest (
79
+ apiPath: apiPath,
80
+ body: body,
81
+ additionaHeaders: additionaHeaders,
82
+ authRequirement: authRequirement,
83
+ method: kHttpMethod.post,
84
+ );
85
+ }
86
+
87
+ Future <AuthApiResponse > get ({
88
+ required String apiPath,
89
+ Map <String , String > additionaHeaders = const {},
90
+ kAuthRequirement authRequirement = kAuthRequirement.required ,
91
+ }) async
92
+ {
93
+ return makeRequest (
94
+ apiPath: apiPath,
95
+ body: null ,
96
+ additionaHeaders: additionaHeaders,
97
+ authRequirement: authRequirement,
98
+ method: kHttpMethod.get ,
99
+ );
100
+ }
101
+
102
+ Future <AuthApiResponse > put ({
103
+ required String apiPath,
104
+ required Map <String , dynamic > body,
105
+ Map <String , String > additionaHeaders = const {},
106
+ kAuthRequirement authRequirement = kAuthRequirement.required ,
107
+ }) async
108
+ {
109
+ return makeRequest (
110
+ apiPath: apiPath,
111
+ body: body,
112
+ additionaHeaders: additionaHeaders,
113
+ authRequirement: authRequirement,
114
+ method: kHttpMethod.put,
115
+ );
116
+ }
117
+
118
+ Future <AuthApiResponse > delete ({
119
+ required String apiPath,
120
+ required Map <String , dynamic > body,
121
+ Map <String , String > additionaHeaders = const {},
122
+ kAuthRequirement authRequirement = kAuthRequirement.required ,
123
+ }) async
124
+ {
125
+ return makeRequest (
126
+ apiPath: apiPath,
127
+ body: body,
128
+ additionaHeaders: additionaHeaders,
129
+ authRequirement: authRequirement,
130
+ method: kHttpMethod.delete,
131
+ );
132
+ }
133
+
134
+
135
+
136
+
137
+
138
+ Future <AuthApiResponse > makeRequest ({
139
+ required String apiPath,
140
+ required Map <String , dynamic >? body,
141
+ Map <String , String > additionaHeaders = const {},
142
+ kAuthRequirement authRequirement = kAuthRequirement.required ,
143
+ required kHttpMethod method,
144
+ }) async
145
+ {
146
+ Dev .log (this , '$method to $apiPath ' , body);
147
+
148
+ try
149
+ {
150
+ Map <String , String > headers = await createHeaders (
151
+ additionalHeaders: additionaHeaders,
152
+ authRequirement: authRequirement
153
+ );
154
+
155
+ String ? jsonBody = body != null ? json.encode (body) : null ;
156
+ final finalUrl = path.join (baseUrl, apiPath);
157
+ final uri = Uri .parse (finalUrl);
158
+
159
+ http.Response ? response;
160
+
161
+ switch (method)
162
+ {
163
+ case kHttpMethod.get :
164
+ response = await http.get (
165
+ uri,
166
+ headers: headers,
167
+ );
168
+ break ;
169
+ case kHttpMethod.post:
170
+ response = await http.post (
171
+ uri,
172
+ headers: headers,
173
+ body: jsonBody,
174
+ );
175
+ break ;
176
+ case kHttpMethod.put:
177
+ response = await http.put (
178
+ uri,
179
+ headers: headers,
180
+ body: jsonBody,
181
+ );
182
+ break ;
183
+ case kHttpMethod.delete:
184
+ response = await http.delete (
185
+ uri,
186
+ headers: headers,
187
+ body: jsonBody,
188
+ );
189
+ break ;
190
+ }
191
+
192
+ if (response.statusCode >= 200 && response.statusCode < 300 )
193
+ {
194
+ final jsonResponse = response.body.isNotEmpty && response.headers['content-type' ]? .contains ('application/json' ) == true
195
+ ? json.decode (response.body)
196
+ : null ;
197
+
198
+ return AuthApiResponse (
199
+ statusCode: response.statusCode,
200
+ body: jsonResponse,
201
+ );
202
+ }
203
+ else
204
+ {
205
+ Dev .logError (this , 'Request failed with status: ${response .statusCode }.' , response);
206
+
207
+ return AuthApiResponse (
208
+ statusCode: response.statusCode,
209
+ body: null ,
210
+ );
211
+ }
212
+ }
213
+ catch (e)
214
+ {
215
+ Dev .logException (this , e, 'Failed $method to $apiPath ' );
216
+
217
+ if (e is UserNotAuthorizedException )
218
+ {
219
+ return AuthApiResponse (
220
+ statusCode: 401 ,
221
+ body: null ,
222
+ );
223
+ }
224
+
225
+ return AuthApiResponse (
226
+ statusCode: 500 ,
227
+ body: null ,
228
+ );
229
+ }
230
+ }
231
+
232
+
233
+
234
+
235
+
236
+
237
+
238
+
239
+
240
+ Future <Map <String , String >> createHeaders ({
241
+ required Map <String , String > additionalHeaders,
242
+ required kAuthRequirement authRequirement,
243
+ }) async
244
+ {
245
+ Map <String , String > headers = {
246
+ 'Content-Type' : 'application/json' ,
247
+ };
248
+
249
+ headers.addAll (additionalHeaders);
250
+
251
+ if (authRequirement != kAuthRequirement.none)
252
+ {
253
+ final token = await onGetAuthToken ();
254
+
255
+ if (token == null )
256
+ {
257
+ if (authRequirement == kAuthRequirement.required )
258
+ {
259
+ throw UserNotAuthorizedException ();
260
+ }
261
+ }
262
+ else
263
+ {
264
+ headers['Authorization' ] = 'Bearer $token ' ;
265
+ }
266
+ }
267
+
268
+
269
+ return headers;
270
+ }
271
+
272
+ }
0 commit comments