1
+ class APICaller {
2
+ static #desc = [
3
+ 'OK' ,
4
+ 'Invalid request' ,
5
+ 'Authorization failed' ,
6
+ 'Request failed' ,
7
+ 'Server error' ,
8
+ 'Invalid server response (JSON error)' ,
9
+ ] ;
10
+ static #isObj( value ) {
11
+ return ( typeof value === 'object' && value !== null ) ;
12
+ }
13
+ #config;
14
+ logging = { } ;
15
+
16
+ constructor ( {
17
+ url,
18
+ headers = null ,
19
+ logging = {
20
+ master : true ,
21
+ collapsed : true ,
22
+ full : false ,
23
+
24
+ params : false ,
25
+ debug : true ,
26
+ meta : false ,
27
+ }
28
+ } = { } ) {
29
+ try {
30
+ url = ( new URL ( url ) ) . href ;
31
+ } catch ( error ) {
32
+ throw new Error ( `Invaild API url` ) ;
33
+ }
34
+ if ( ! url . endsWith ( '/' ) ) {
35
+ url = `${ url } /` ;
36
+ }
37
+ headers ??= { } ;
38
+ if ( ! APICaller . #isObj( headers ) ) {
39
+ throw new Error ( 'Invalid API headers; Expected an object;' ) ;
40
+ }
41
+ this . logging = Object . assign ( { } , this . logging , logging ) ;
42
+ this . #config = {
43
+ url,
44
+ headers
45
+ }
46
+ }
47
+ async coreFetch ( {
48
+ func = '' ,
49
+ params = null ,
50
+ headers = null ,
51
+ logging = { }
52
+ } = { } ) {
53
+ logging = Object . assign ( { } , this . logging , logging ) ;
54
+ headers ??= { } ;
55
+ if ( ! APICaller . #isObj( headers ) ) {
56
+ throw new Error ( 'Invalid API headers; Expected an object;' ) ;
57
+ }
58
+
59
+ let fetched ;
60
+ let ret = {
61
+ status : 0 ,
62
+ value : null ,
63
+ statusDesc : null ,
64
+ headers : null ,
65
+ meta : null ,
66
+ debug : null
67
+ } ;
68
+
69
+ // Create fetch config
70
+ const config = {
71
+ method : 'POST' ,
72
+ mode : 'cors'
73
+ } ;
74
+ // Combine default headers with current headers
75
+ config . headers = Object . assign ( { } , this . #config. headers , headers ) ;
76
+
77
+ // Add params if present
78
+ if ( APICaller . #isObj( params ) ) {
79
+ config . headers [ 'Content-Type' ] = 'application/json' ;
80
+ config . body = JSON . stringify ( params ) ;
81
+ } else {
82
+ config . method = 'GET' ;
83
+ }
84
+
85
+ const url = `${ this . #config. url } ${ func } ` ;
86
+
87
+ // Fetch data
88
+ try {
89
+ // console.log('API CALL', { url, params });
90
+ fetched = await window . fetch ( url , config ) ;
91
+ ret . headers = fetched . headers ;
92
+ } catch ( error ) {
93
+ ret . status = - 1 ;
94
+ ret . statusDesc = 'Network failed' ;
95
+ ret . value ??= error . message ;
96
+ return Promise . resolve ( ret ) ;
97
+ }
98
+ if ( ! fetched . ok ) {
99
+ ret . status = fetched . status ;
100
+ ret . statusDesc = APICaller . #desc[ 3 ] ;
101
+ ret . value = fetched . statusText ;
102
+ return Promise . resolve ( ret ) ;
103
+ }
104
+ fetched = await fetched . text ( ) . catch ( e => e . message ) ;
105
+ try {
106
+ const json = JSON . parse ( fetched ) ;
107
+ if ( ! APICaller . #isObj( json ) ) {
108
+ throw new Error ( 'Not object' ) ;
109
+ }
110
+ fetched = json ;
111
+ } catch ( error ) {
112
+ ret . status = 5 ;
113
+ ret . value = fetched ;
114
+ ret . statusDesc = APICaller . #desc[ 5 ] ;
115
+ return Promise . resolve ( ret ) ;
116
+ }
117
+ ret = { ...ret , ...fetched } ;
118
+
119
+ ret . statusDesc = APICaller . #desc[ ret . status ] ?? 'Unknown' ;
120
+
121
+ if ( logging . master ) {
122
+ if ( logging . full ) {
123
+ logging . collapsed ? console . groupCollapsed ( 'API Call:' , url ) : console . group ( 'API Call:' , url ) ;
124
+ console . log ( 'Request :' , { url, params, headers } ) ;
125
+ console . log ( 'Response:' , ret ) ;
126
+ console . groupEnd ( ) ;
127
+ } else {
128
+ logging . collapsed ? console . groupCollapsed ( 'API Call:' , url ) : console . group ( 'API Call:' , url ) ;
129
+ if ( logging . params ) {
130
+ console . group ( 'Request Params:' , params ) ;
131
+ }
132
+ if ( logging . meta ) {
133
+ console . log ( 'Response Meta:' , ret . meta ) ;
134
+ }
135
+
136
+ console . log ( 'Response Desc:' , ret . statusDesc ) ;
137
+ console . log ( 'Response Value:' , ret . value ) ;
138
+
139
+ if ( logging . debug && ret . debug !== null ) {
140
+ console . log ( 'Debug messages:' ) ;
141
+ if ( ! Array . isArray ( ret . debug ) ) {
142
+ ret . debug = [ ret . debug ] ;
143
+ }
144
+ for ( const msg of ret . debug ) {
145
+ console . log ( msg ) ;
146
+ }
147
+ }
148
+ console . groupEnd ( ) ;
149
+ }
150
+ }
151
+
152
+ return Promise . resolve ( ret ) ;
153
+ }
154
+
155
+ #proxy( ) {
156
+ /*
157
+ syntax:
158
+ this.proxy().funcGroup.funcName(params = null, {
159
+ headers = null,
160
+ logging = {},
161
+ onerror = null
162
+ } = {});
163
+ */
164
+ const list = [ ] ;
165
+ return new Proxy ( function ( ) { } , {
166
+ get : ( dummy , name , pxy ) => {
167
+ list . push ( name ) ;
168
+ return pxy ;
169
+ } ,
170
+ apply : async ( dummy , pxy , args ) => {
171
+ const params = args [ 0 ] ?? null ;
172
+ let opts = {
173
+ headers : null ,
174
+ logging : { } ,
175
+ onerror : null
176
+ } ;
177
+ if ( APICaller . #isObj( args [ 1 ] ) ) {
178
+ opts = Object . assign ( { } , opts , args [ 1 ] ) ;
179
+ }
180
+ const resp = await this . coreFetch ( {
181
+ func : list . join ( '.' ) ,
182
+ params : params ,
183
+ headers : opts . headers ,
184
+ logging : opts . logging
185
+ } ) ;
186
+ if ( resp . status === 0 ) {
187
+ return resp . value ;
188
+ } else {
189
+ if ( typeof opts . onerror === 'function' ) {
190
+ opts . onerror ( resp ) ;
191
+ } else {
192
+ alert ( `${ resp . statusDesc } :\n${ resp . value } ` ) ;
193
+ // App.popupbox({
194
+ // content: resp.value,
195
+ // title: HTML`<svg-icon icon="info"></svg-icon> ${resp.statusDesc}`
196
+ // });
197
+ }
198
+ }
199
+ return null ;
200
+ }
201
+ } ) ;
202
+ }
203
+ get fetch ( ) {
204
+ return this . #proxy( ) ;
205
+ }
206
+ }
207
+
208
+ export default APICaller ;
209
+
210
+ // Example: How to use
211
+
212
+ // const MyApp = new APICaller({
213
+ // url: 'https://api.example.com',
214
+ // });
215
+
216
+ // Default way to call API
217
+ // console.log('Response', await MyApp.fetch.test({
218
+ // 'int': '3123',
219
+ // 'bool': 0
220
+ // }));
221
+
222
+ // Core way to call API
223
+ // console.log('Response', await MyApp.coreFetch({
224
+ // func: 'test.lol',
225
+ // params: {
226
+ // 'int': '3123',
227
+ // 'bool': 0
228
+ // }
229
+ // }).catch(e => {
230
+ // console.log('Response Error', e);
231
+ // return null
232
+ // }));
0 commit comments