parse.js 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { addQueryParameters } from "./util/add-query-parameters";
  2. import { extractUrlVariableNames } from "./util/extract-url-variable-names";
  3. import { omit } from "./util/omit";
  4. import { parseUrl } from "./util/url-template";
  5. export function parse(options) {
  6. // https://fetch.spec.whatwg.org/#methods
  7. let method = options.method.toUpperCase();
  8. // replace :varname with {varname} to make it RFC 6570 compatible
  9. let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}");
  10. let headers = Object.assign({}, options.headers);
  11. let body;
  12. let parameters = omit(options, [
  13. "method",
  14. "baseUrl",
  15. "url",
  16. "headers",
  17. "request",
  18. "mediaType",
  19. ]);
  20. // extract variable names from URL to calculate remaining variables later
  21. const urlVariableNames = extractUrlVariableNames(url);
  22. url = parseUrl(url).expand(parameters);
  23. if (!/^http/.test(url)) {
  24. url = options.baseUrl + url;
  25. }
  26. const omittedParameters = Object.keys(options)
  27. .filter((option) => urlVariableNames.includes(option))
  28. .concat("baseUrl");
  29. const remainingParameters = omit(parameters, omittedParameters);
  30. const isBinaryRequest = /application\/octet-stream/i.test(headers.accept);
  31. if (!isBinaryRequest) {
  32. if (options.mediaType.format) {
  33. // e.g. application/vnd.github.v3+json => application/vnd.github.v3.raw
  34. headers.accept = headers.accept
  35. .split(/,/)
  36. .map((preview) => preview.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, `application/vnd$1$2.${options.mediaType.format}`))
  37. .join(",");
  38. }
  39. if (options.mediaType.previews.length) {
  40. const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || [];
  41. headers.accept = previewsFromAcceptHeader
  42. .concat(options.mediaType.previews)
  43. .map((preview) => {
  44. const format = options.mediaType.format
  45. ? `.${options.mediaType.format}`
  46. : "+json";
  47. return `application/vnd.github.${preview}-preview${format}`;
  48. })
  49. .join(",");
  50. }
  51. }
  52. // for GET/HEAD requests, set URL query parameters from remaining parameters
  53. // for PATCH/POST/PUT/DELETE requests, set request body from remaining parameters
  54. if (["GET", "HEAD"].includes(method)) {
  55. url = addQueryParameters(url, remainingParameters);
  56. }
  57. else {
  58. if ("data" in remainingParameters) {
  59. body = remainingParameters.data;
  60. }
  61. else {
  62. if (Object.keys(remainingParameters).length) {
  63. body = remainingParameters;
  64. }
  65. else {
  66. headers["content-length"] = 0;
  67. }
  68. }
  69. }
  70. // default content-type for JSON if body is set
  71. if (!headers["content-type"] && typeof body !== "undefined") {
  72. headers["content-type"] = "application/json; charset=utf-8";
  73. }
  74. // GitHub expects 'content-length: 0' header for PUT/PATCH requests without body.
  75. // fetch does not allow to set `content-length` header, but we can set body to an empty string
  76. if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") {
  77. body = "";
  78. }
  79. // Only return body/request keys if present
  80. return Object.assign({ method, url, headers }, typeof body !== "undefined" ? { body } : null, options.request ? { request: options.request } : null);
  81. }