• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

odata2ts / http-client / 14086530033

26 Mar 2025 02:59PM UTC coverage: 97.134% (-1.6%) from 98.726%
14086530033

Pull #27

github

web-flow
Merge d6a6dfbf2 into a316f6ce5
Pull Request #27: fix(FetchClient): delete should show error messages

112 of 120 branches covered (93.33%)

Branch coverage included in aggregate %.

193 of 194 relevant lines covered (99.48%)

25.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.29
/packages/fetch/src/FetchClient.ts
1
import { HttpResponseModel, ODataHttpClient } from "@odata2ts/http-client-api";
2
import {
3
  BaseHttpClient,
4
  BaseHttpClientOptions,
5
  HttpMethods,
6
  InternalHttpClientConfig,
7
} from "@odata2ts/http-client-base";
8
import { FetchClientError } from "./FetchClientError";
9
import { FetchRequestConfig, getDefaultConfig, mergeFetchConfig } from "./FetchRequestConfig";
10

11
export interface ClientOptions extends BaseHttpClientOptions {}
12

13
export const DEFAULT_ERROR_MESSAGE = "No error message!";
3✔
14
const FETCH_FAILURE_MESSAGE = "OData request failed entirely: ";
3✔
15
const JSON_RETRIEVAL_FAILURE_MESSAGE = "Retrieving JSON body from OData response failed: ";
3✔
16
const BLOB_RETRIEVAL_FAILURE_MESSAGE = "Retrieving blob from OData response failed: ";
3✔
17
const RESPONSE_FAILURE_MESSAGE = "OData server responded with error: ";
3✔
18

19
function buildErrorMessage(prefix: string, error: any) {
20
  const msg = typeof error === "string" ? error : (error as Error)?.message;
10✔
21
  return prefix + (msg || DEFAULT_ERROR_MESSAGE);
10✔
22
}
23

24
export class FetchClient extends BaseHttpClient<FetchRequestConfig> implements ODataHttpClient<FetchRequestConfig> {
25
  protected readonly config: RequestInit;
26

27
  constructor(config?: FetchRequestConfig, clientOptions?: ClientOptions) {
28
    super(clientOptions);
26✔
29
    this.config = getDefaultConfig(config);
26✔
30
  }
31

32
  protected async executeRequest<ResponseModel>(
33
    method: HttpMethods,
34
    url: string,
35
    data: any,
36
    requestConfig: FetchRequestConfig | undefined = {},
33✔
37
    internalConfig: InternalHttpClientConfig = {},
×
38
  ): Promise<HttpResponseModel<ResponseModel>> {
39
    const { headers, noBodyEvaluation } = internalConfig;
37✔
40
    const { params, ...config } = mergeFetchConfig(this.config, { headers }, requestConfig);
37✔
41
    config.method = method;
37✔
42
    if (typeof data !== "undefined") {
37✔
43
      config.body = internalConfig.dataType === "json" ? JSON.stringify(data) : data;
11✔
44
    }
45
    let finalUrl = url;
37✔
46
    if (params && Object.values(params).length) {
37✔
47
      finalUrl +=
4✔
48
        (url.match(/\?/) ? "&" : "?") +
4✔
49
        // @ts-ignore
50
        new URLSearchParams(params).toString();
51
    }
52

53
    // the actual request
54
    let response: Response;
55
    try {
37✔
56
      response = await fetch(finalUrl, config);
37✔
57
    } catch (fetchError) {
58
      throw new FetchClientError(
2✔
59
        buildErrorMessage(FETCH_FAILURE_MESSAGE, fetchError),
60
        undefined,
61
        undefined,
62
        fetchError as Error,
63
      );
64
    }
65

66
    // error response
67
    if (!response.ok) {
35✔
68
      let responseData;
69
      try {
7✔
70
        responseData = await this.getResponseBody(response, internalConfig);
7✔
71
      } catch (e) {
72
        responseData = undefined;
1✔
73
      }
74
      const errMsg = this.retrieveErrorMessage(responseData);
7✔
75

76
      throw new FetchClientError(
7✔
77
        buildErrorMessage(RESPONSE_FAILURE_MESSAGE, errMsg),
78
        response.status,
79
        this.mapHeaders(response.headers),
80
        new Error(errMsg || DEFAULT_ERROR_MESSAGE),
9✔
81
        responseData,
82
      );
83
    }
84

85
    let responseData;
86
    try {
28✔
87
      responseData = noBodyEvaluation ? undefined : await this.getResponseBody(response, internalConfig);
28!
88
    } catch (error) {
89
      const msg = internalConfig.dataType === "blob" ? BLOB_RETRIEVAL_FAILURE_MESSAGE : JSON_RETRIEVAL_FAILURE_MESSAGE;
1!
90
      throw new FetchClientError(
1✔
91
        buildErrorMessage(msg, error),
92
        response.status,
93
        this.mapHeaders(response.headers),
94
        error as Error,
95
      );
96
    }
97

98
    return {
27✔
99
      status: response.status,
100
      statusText: response.statusText,
101
      headers: this.mapHeaders(response.headers),
102
      data: responseData,
103
    };
104
  }
105

106
  protected async getResponseBody(response: Response, options: InternalHttpClientConfig) {
107
    if (response.status === 204) {
35✔
108
      return undefined;
4✔
109
    }
110
    switch (options.dataType) {
31✔
111
      case "json":
112
        return response.json();
28✔
113
      case "blob":
114
        return response.blob();
2✔
115
      case "stream":
116
        return response.body;
1✔
117
    }
118

119
    return undefined;
×
120
  }
121

122
  protected mapHeaders(headers: Headers): Record<string, string> {
123
    const result: Record<string, string> = {};
35✔
124
    headers.forEach((value, key) => (result[key] = value));
96✔
125

126
    return result;
35✔
127
  }
128
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc