@@ -14,60 +14,77 @@ const headersToObject = (headers?: HeadersInit): Record<string, string> =>
1414 ? Object . fromEntries ( headers )
1515 : headers || { } ;
1616
17- async function fetchJson < T > (
18- account : ResolvedGoogleChatAccount ,
19- url : string ,
20- init : RequestInit ,
21- ) : Promise < T > {
17+ async function withGoogleChatResponse < T > ( params : {
18+ account : ResolvedGoogleChatAccount ;
19+ url : string ;
20+ init ?: RequestInit ;
21+ auditContext : string ;
22+ errorPrefix ?: string ;
23+ handleResponse : ( response : Response ) => Promise < T > ;
24+ } ) : Promise < T > {
25+ const {
26+ account,
27+ url,
28+ init,
29+ auditContext,
30+ errorPrefix = "Google Chat API" ,
31+ handleResponse,
32+ } = params ;
2233 const token = await getGoogleChatAccessToken ( account ) ;
23- const { response : res , release } = await fetchWithSsrFGuard ( {
34+ const { response, release } = await fetchWithSsrFGuard ( {
2435 url,
2536 init : {
2637 ...init ,
2738 headers : {
28- ...headersToObject ( init . headers ) ,
39+ ...headersToObject ( init ? .headers ) ,
2940 Authorization : `Bearer ${ token } ` ,
30- "Content-Type" : "application/json" ,
3141 } ,
3242 } ,
33- auditContext : "googlechat.api.json" ,
43+ auditContext,
3444 } ) ;
3545 try {
36- if ( ! res . ok ) {
37- const text = await res . text ( ) . catch ( ( ) => "" ) ;
38- throw new Error ( `Google Chat API ${ res . status } : ${ text || res . statusText } ` ) ;
46+ if ( ! response . ok ) {
47+ const text = await response . text ( ) . catch ( ( ) => "" ) ;
48+ throw new Error ( `${ errorPrefix } ${ response . status } : ${ text || response . statusText } ` ) ;
3949 }
40- return ( await res . json ( ) ) as T ;
50+ return await handleResponse ( response ) ;
4151 } finally {
4252 await release ( ) ;
4353 }
4454}
4555
46- async function fetchOk (
56+ async function fetchJson < T > (
4757 account : ResolvedGoogleChatAccount ,
4858 url : string ,
4959 init : RequestInit ,
50- ) : Promise < void > {
51- const token = await getGoogleChatAccessToken ( account ) ;
52- const { response : res , release } = await fetchWithSsrFGuard ( {
60+ ) : Promise < T > {
61+ return await withGoogleChatResponse ( {
62+ account ,
5363 url,
5464 init : {
5565 ...init ,
5666 headers : {
5767 ...headersToObject ( init . headers ) ,
58- Authorization : `Bearer ${ token } ` ,
68+ "Content-Type" : "application/json" ,
5969 } ,
6070 } ,
71+ auditContext : "googlechat.api.json" ,
72+ handleResponse : async ( response ) => ( await response . json ( ) ) as T ,
73+ } ) ;
74+ }
75+
76+ async function fetchOk (
77+ account : ResolvedGoogleChatAccount ,
78+ url : string ,
79+ init : RequestInit ,
80+ ) : Promise < void > {
81+ await withGoogleChatResponse ( {
82+ account,
83+ url,
84+ init,
6185 auditContext : "googlechat.api.ok" ,
86+ handleResponse : async ( ) => undefined ,
6287 } ) ;
63- try {
64- if ( ! res . ok ) {
65- const text = await res . text ( ) . catch ( ( ) => "" ) ;
66- throw new Error ( `Google Chat API ${ res . status } : ${ text || res . statusText } ` ) ;
67- }
68- } finally {
69- await release ( ) ;
70- }
7188}
7289
7390async function fetchBuffer (
@@ -76,60 +93,48 @@ async function fetchBuffer(
7693 init ?: RequestInit ,
7794 options ?: { maxBytes ?: number } ,
7895) : Promise < { buffer : Buffer ; contentType ?: string } > {
79- const token = await getGoogleChatAccessToken ( account ) ;
80- const { response : res , release } = await fetchWithSsrFGuard ( {
96+ return await withGoogleChatResponse ( {
97+ account ,
8198 url,
82- init : {
83- ...init ,
84- headers : {
85- ...headersToObject ( init ?. headers ) ,
86- Authorization : `Bearer ${ token } ` ,
87- } ,
88- } ,
99+ init,
89100 auditContext : "googlechat.api.buffer" ,
90- } ) ;
91- try {
92- if ( ! res . ok ) {
93- const text = await res . text ( ) . catch ( ( ) => "" ) ;
94- throw new Error ( `Google Chat API ${ res . status } : ${ text || res . statusText } ` ) ;
95- }
96- const maxBytes = options ?. maxBytes ;
97- const lengthHeader = res . headers . get ( "content-length" ) ;
98- if ( maxBytes && lengthHeader ) {
99- const length = Number ( lengthHeader ) ;
100- if ( Number . isFinite ( length ) && length > maxBytes ) {
101- throw new Error ( `Google Chat media exceeds max bytes (${ maxBytes } )` ) ;
102- }
103- }
104- if ( ! maxBytes || ! res . body ) {
105- const buffer = Buffer . from ( await res . arrayBuffer ( ) ) ;
106- const contentType = res . headers . get ( "content-type" ) ?? undefined ;
107- return { buffer, contentType } ;
108- }
109- const reader = res . body . getReader ( ) ;
110- const chunks : Buffer [ ] = [ ] ;
111- let total = 0 ;
112- while ( true ) {
113- const { done, value } = await reader . read ( ) ;
114- if ( done ) {
115- break ;
101+ handleResponse : async ( res ) => {
102+ const maxBytes = options ?. maxBytes ;
103+ const lengthHeader = res . headers . get ( "content-length" ) ;
104+ if ( maxBytes && lengthHeader ) {
105+ const length = Number ( lengthHeader ) ;
106+ if ( Number . isFinite ( length ) && length > maxBytes ) {
107+ throw new Error ( `Google Chat media exceeds max bytes (${ maxBytes } )` ) ;
108+ }
116109 }
117- if ( ! value ) {
118- continue ;
110+ if ( ! maxBytes || ! res . body ) {
111+ const buffer = Buffer . from ( await res . arrayBuffer ( ) ) ;
112+ const contentType = res . headers . get ( "content-type" ) ?? undefined ;
113+ return { buffer, contentType } ;
119114 }
120- total += value . length ;
121- if ( total > maxBytes ) {
122- await reader . cancel ( ) ;
123- throw new Error ( `Google Chat media exceeds max bytes (${ maxBytes } )` ) ;
115+ const reader = res . body . getReader ( ) ;
116+ const chunks : Buffer [ ] = [ ] ;
117+ let total = 0 ;
118+ while ( true ) {
119+ const { done, value } = await reader . read ( ) ;
120+ if ( done ) {
121+ break ;
122+ }
123+ if ( ! value ) {
124+ continue ;
125+ }
126+ total += value . length ;
127+ if ( total > maxBytes ) {
128+ await reader . cancel ( ) ;
129+ throw new Error ( `Google Chat media exceeds max bytes (${ maxBytes } )` ) ;
130+ }
131+ chunks . push ( Buffer . from ( value ) ) ;
124132 }
125- chunks . push ( Buffer . from ( value ) ) ;
126- }
127- const buffer = Buffer . concat ( chunks , total ) ;
128- const contentType = res . headers . get ( "content-type" ) ?? undefined ;
129- return { buffer, contentType } ;
130- } finally {
131- await release ( ) ;
132- }
133+ const buffer = Buffer . concat ( chunks , total ) ;
134+ const contentType = res . headers . get ( "content-type" ) ?? undefined ;
135+ return { buffer, contentType } ;
136+ } ,
137+ } ) ;
133138}
134139
135140export async function sendGoogleChatMessage ( params : {
@@ -208,34 +213,29 @@ export async function uploadGoogleChatAttachment(params: {
208213 Buffer . from ( footer , "utf8" ) ,
209214 ] ) ;
210215
211- const token = await getGoogleChatAccessToken ( account ) ;
212216 const url = `${ CHAT_UPLOAD_BASE } /${ space } /attachments:upload?uploadType=multipart` ;
213- const { response : res , release } = await fetchWithSsrFGuard ( {
217+ const payload = await withGoogleChatResponse < {
218+ attachmentDataRef ?: { attachmentUploadToken ?: string } ;
219+ } > ( {
220+ account,
214221 url,
215222 init : {
216223 method : "POST" ,
217224 headers : {
218- Authorization : `Bearer ${ token } ` ,
219225 "Content-Type" : `multipart/related; boundary=${ boundary } ` ,
220226 } ,
221227 body,
222228 } ,
223229 auditContext : "googlechat.upload" ,
230+ errorPrefix : "Google Chat upload" ,
231+ handleResponse : async ( response ) =>
232+ ( await response . json ( ) ) as {
233+ attachmentDataRef ?: { attachmentUploadToken ?: string } ;
234+ } ,
224235 } ) ;
225- try {
226- if ( ! res . ok ) {
227- const text = await res . text ( ) . catch ( ( ) => "" ) ;
228- throw new Error ( `Google Chat upload ${ res . status } : ${ text || res . statusText } ` ) ;
229- }
230- const payload = ( await res . json ( ) ) as {
231- attachmentDataRef ?: { attachmentUploadToken ?: string } ;
232- } ;
233- return {
234- attachmentUploadToken : payload . attachmentDataRef ?. attachmentUploadToken ,
235- } ;
236- } finally {
237- await release ( ) ;
238- }
236+ return {
237+ attachmentUploadToken : payload . attachmentDataRef ?. attachmentUploadToken ,
238+ } ;
239239}
240240
241241export async function downloadGoogleChatMedia ( params : {
0 commit comments