import { APIClient, type APIClientOptions, APIPromise, type APIResponseProps, debug, type FinalRequestOptions, type PromiseOrValue, Stream, } from '@zhengxs/http'; import { type TokenCredential, type TokenResponse } from './credentials/TokenCredential'; import { assertErrCodeOfZero } from './util'; /** * 企业内部应用网关地址 */ const BASE_URL = 'https://api.dingtalk.com/v1.0' as const; export interface IdentityClientOptions extends Partial { credential?: Credential; } export class IdentityClient extends APIClient { private credential?: Credential; constructor(options?: IdentityClientOptions) { const { baseURL = BASE_URL, credential, ...rest } = options || {}; super({ baseURL, ...rest }); this.credential = credential; } getCredential(): Credential { if (!this.credential) { throw new Error(`请先选择设置授权方式`); } return this.credential; } setCredential(credential: Credential) { this.credential = credential; this.credential.setClient(this); } getToken(): Promise { const credential = this.getCredential(); return credential.getToken(); } getTokenValue(): Promise { const credential = this.getCredential(); return credential.getTokenValue(); } request, Rsp>( options: PromiseOrValue>, remainingRetries: number | null = null, ): APIPromise { return new APIPromise( this.makeRequest(options, remainingRetries), dingtalkParseResponse as (props: APIResponseProps) => PromiseOrValue, ); } protected override async authHeaders(): Promise> { const accessToken = await this.getTokenValue(); return { 'x-acs-dingtalk-access-token': accessToken }; } } export async function dingtalkParseResponse(props: APIResponseProps): Promise { const { response } = props; if (props.options.stream) { debug('response', response.status, response.url, response.headers, response.body); // Note: there is an invariant here that isn't represented in the type system // that if you set `stream: true` the response type must also be `Stream` return Stream.fromSSEResponse(response, props.controller) as any; } // fetch refuses to read the body when the status code is 204. if (response.status === 204) { return null as T; } if (props.options.__binaryResponse) { return response as unknown as T; } const contentType = response.headers.get('content-type'); if (contentType?.includes('application/json')) { const json = await response.json(); debug('response', response.status, response.url, response.headers, json); assertErrCodeOfZero(json); return json as T; } const text = await response.text(); debug('response', response.status, response.url, response.headers, text); // TODO handle blob, arraybuffer, other content types, etc. return text as unknown as T; }