The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
grafana/public/app/core/utils/fetch.ts

117 lines
3.2 KiB

import { BackendSrvRequest } from '@grafana/runtime';
import omitBy from 'lodash/omitBy';
export const parseInitFromOptions = (options: BackendSrvRequest): RequestInit => {
const method = options.method;
const headers = parseHeaders(options);
const isAppJson = isContentTypeApplicationJson(headers);
const body = parseBody(options, isAppJson);
if (options?.withCredentials) {
return {
method,
headers,
body,
credentials: 'include',
};
}
return {
method,
headers,
body,
};
};
interface HeaderParser {
canParse: (options: BackendSrvRequest) => boolean;
parse: (headers: Headers) => Headers;
}
const defaultHeaderParser: HeaderParser = {
canParse: () => true,
parse: headers => {
const accept = headers.get('accept');
if (accept) {
return headers;
}
headers.set('accept', 'application/json, text/plain, */*');
return headers;
},
};
const parseHeaderByMethodFactory = (methodPredicate: string): HeaderParser => ({
canParse: options => {
const method = options?.method ? options?.method.toLowerCase() : '';
return method === methodPredicate;
},
parse: headers => {
const contentType = headers.get('content-type');
if (contentType) {
return headers;
}
headers.set('content-type', 'application/json');
return headers;
},
});
const postHeaderParser: HeaderParser = parseHeaderByMethodFactory('post');
const putHeaderParser: HeaderParser = parseHeaderByMethodFactory('put');
const headerParsers = [postHeaderParser, putHeaderParser, defaultHeaderParser];
export const parseHeaders = (options: BackendSrvRequest) => {
const headers = options?.headers ? new Headers(options.headers) : new Headers();
const parsers = headerParsers.filter(parser => parser.canParse(options));
const combinedHeaders = parsers.reduce((prev, parser) => {
return parser.parse(prev);
}, headers);
return combinedHeaders;
};
export const isContentTypeApplicationJson = (headers: Headers) => {
if (!headers) {
return false;
}
const contentType = headers.get('content-type');
if (contentType && contentType.toLowerCase() === 'application/json') {
return true;
}
return false;
};
export const parseBody = (options: BackendSrvRequest, isAppJson: boolean) => {
if (!options) {
return options;
}
if (!options.data || typeof options.data === 'string') {
return options.data;
}
return isAppJson ? JSON.stringify(options.data) : new URLSearchParams(options.data);
};
export function serializeParams(data: Record<string, any>): string {
return Object.keys(data)
.map(key => {
const value = data[key];
if (Array.isArray(value)) {
return value.map(arrayValue => `${encodeURIComponent(key)}=${encodeURIComponent(arrayValue)}`).join('&');
}
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
})
.join('&');
}
export const parseUrlFromOptions = (options: BackendSrvRequest): string => {
const cleanParams = omitBy(options.params, v => v === undefined || (v && v.length === 0));
const serializedParams = serializeParams(cleanParams);
return options.params && serializedParams.length ? `${options.url}?${serializedParams}` : options.url;
};