tryitout.js 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. function tryItOut(endpointId) {
  2. document.querySelector(`#btn-tryout-${endpointId}`).hidden = true;
  3. document.querySelector(`#btn-executetryout-${endpointId}`).hidden = false;
  4. document.querySelector(`#btn-canceltryout-${endpointId}`).hidden = false;
  5. // Show all input fields
  6. document.querySelectorAll(`input[data-endpoint=${endpointId}],label[data-endpoint=${endpointId}]`)
  7. .forEach(el => el.hidden = false);
  8. if (document.querySelector(`#form-${endpointId}`).dataset.authed === "1") {
  9. const authElement = document.querySelector(`#auth-${endpointId}`);
  10. authElement && (authElement.hidden = false);
  11. }
  12. // Expand all nested fields
  13. document.querySelectorAll(`#form-${endpointId} details`)
  14. .forEach(el => el.open = true);
  15. }
  16. function cancelTryOut(endpointId) {
  17. document.querySelector(`#btn-tryout-${endpointId}`).hidden = false;
  18. const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`);
  19. executeBtn.hidden = true;
  20. executeBtn.textContent = "Send Request 💥";
  21. document.querySelector(`#btn-canceltryout-${endpointId}`).hidden = true;
  22. // hide inputs
  23. document.querySelectorAll(`input[data-endpoint=${endpointId}],label[data-endpoint=${endpointId}]`)
  24. .forEach(el => el.hidden = true);
  25. document.querySelectorAll(`#form-${endpointId} details`)
  26. .forEach(el => el.open = false);
  27. const authElement = document.querySelector(`#auth-${endpointId}`);
  28. authElement && (authElement.hidden = true);
  29. document.querySelector('#execution-results-' + endpointId).hidden = true;
  30. document.querySelector('#execution-error-' + endpointId).hidden = true;
  31. // Revert to sample code blocks
  32. const query = new URLSearchParams(window.location.search);
  33. const languages = JSON.parse(document.querySelector('body').dataset.languages);
  34. const currentLanguage = languages.find(l => query.has(l)) || languages[0];
  35. let codeblock = getPreviousSiblingUntil(document.querySelector('#form-' + endpointId), 'blockquote,pre', 'h2');
  36. while (codeblock != null) {
  37. if (codeblock.nodeName === 'PRE') {
  38. if (codeblock.querySelector('code.language-' + currentLanguage)) {
  39. codeblock.style.display = 'block';
  40. }
  41. } else {
  42. codeblock.style.display = 'block';
  43. }
  44. codeblock = getPreviousSiblingUntil(codeblock, 'blockquote,pre', 'h2');
  45. }
  46. }
  47. function makeAPICall(method, path, body, query, headers) {
  48. console.log({path, body, query, headers});
  49. if (!(body instanceof FormData)) {
  50. body = JSON.stringify(body)
  51. }
  52. const url = new URL(window.baseUrl + '/' + path.replace(/^\//, ''));
  53. Object.keys(query)
  54. .forEach(key => url.searchParams.append(key, query[key]));
  55. return fetch(url, {
  56. method,
  57. headers,
  58. body: method === 'GET' ? undefined : body,
  59. })
  60. .then(response => Promise.all([response.status, response.text(), response.headers]));
  61. }
  62. function hideCodeSamples(form) {
  63. let codeblock = getPreviousSiblingUntil(form, 'blockquote,pre', 'h2');
  64. while (codeblock != null) {
  65. codeblock.style.display = 'none';
  66. codeblock = getPreviousSiblingUntil(codeblock, 'blockquote,pre', 'h2');
  67. }
  68. }
  69. function handleResponse(form, endpointId, response, status, headers) {
  70. hideCodeSamples(form);
  71. // Hide error views
  72. document.querySelector('#execution-error-' + endpointId).hidden = true;
  73. const responseContentEl = document.querySelector('#execution-response-content-' + endpointId);
  74. // prettify it if it's JSON
  75. let isJson = false;
  76. try {
  77. const jsonParsed = JSON.parse(response);
  78. if (jsonParsed !== null) {
  79. isJson = true;
  80. response = JSON.stringify(jsonParsed, null, 4);
  81. }
  82. } catch (e) {
  83. }
  84. responseContentEl.textContent = response === '' ? '<Empty response>' : response;
  85. isJson && window.hljs.highlightBlock(responseContentEl);
  86. const statusEl = document.querySelector('#execution-response-status-' + endpointId);
  87. statusEl.textContent = ` (${status})`;
  88. document.querySelector('#execution-results-' + endpointId).hidden = false;
  89. statusEl.scrollIntoView({behavior: "smooth", block: "center"});
  90. }
  91. function handleError(form, endpointId, err) {
  92. hideCodeSamples(form);
  93. // Hide response views
  94. document.querySelector('#execution-results-' + endpointId).hidden = true;
  95. // Show error views
  96. let errorMessage = err.message || err;
  97. errorMessage += "\n\nTip: Check that you're properly connected to the network.";
  98. errorMessage += "\nIf you're a maintainer of ths API, verify that your API is running and you've enabled CORS.";
  99. errorMessage += "\nYou can check the Dev Tools console for debugging information.";
  100. document.querySelector('#execution-error-message-' + endpointId).textContent = errorMessage;
  101. const errorEl = document.querySelector('#execution-error-' + endpointId);
  102. errorEl.hidden = false;
  103. errorEl.scrollIntoView({behavior: "smooth", block: "center"});
  104. }
  105. async function executeTryOut(endpointId, form) {
  106. const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`);
  107. executeBtn.textContent = "⏱ Sending...";
  108. executeBtn.scrollIntoView({behavior: "smooth", block: "center"});
  109. let body;
  110. let setter;
  111. if (form.dataset.hasfiles === "0") {
  112. body = {};
  113. setter = (name, value) => _.set(body, name, value);
  114. } else {
  115. body = new FormData();
  116. setter = (name, value) => body.append(name, value);
  117. }
  118. const bodyParameters = form.querySelectorAll('input[data-component=body]');
  119. bodyParameters.forEach(el => {
  120. let value = el.value;
  121. if (el.type === 'file' && el.files[0]) {
  122. setter(el.name, el.files[0]);
  123. return;
  124. }
  125. if (el.type !== 'radio') {
  126. if (value === "" && el.required === false) {
  127. // Don't include empty optional values in the request
  128. return;
  129. }
  130. setter(el.name, value);
  131. return;
  132. }
  133. if (el.checked) {
  134. value = (value === 'false') ? false : true;
  135. setter(el.name, value);
  136. }
  137. });
  138. const query = {};
  139. const queryParameters = form.querySelectorAll('input[data-component=query]');
  140. queryParameters.forEach(el => _.set(query, el.name, el.value));
  141. let path = form.dataset.path;
  142. const urlParameters = form.querySelectorAll('input[data-component=url]');
  143. urlParameters.forEach(el => (path = path.replace(new RegExp(`\\{${el.name}\\??}`), el.value)));
  144. const headers = JSON.parse(form.dataset.headers);
  145. // Check for auth param that might go in header
  146. if (form.dataset.authed === "1") {
  147. const authHeaderEl = form.querySelector('input[data-component=header]');
  148. if (authHeaderEl) headers[authHeaderEl.name] = authHeaderEl.dataset.prefix + authHeaderEl.value;
  149. }
  150. makeAPICall(form.dataset.method, path, body, query, headers)
  151. .then(([responseStatus, responseContent, responseHeaders]) => {
  152. handleResponse(form, endpointId, responseContent, responseStatus, responseHeaders)
  153. })
  154. .catch(err => {
  155. console.log("Error while making request: ", err);
  156. handleError(form, endpointId, err);
  157. })
  158. .finally(() => {
  159. executeBtn.textContent = "Send Request 💥";
  160. });
  161. }
  162. function getPreviousSiblingUntil(elem, siblingSelector, stopSelector) {
  163. let sibling = elem.previousElementSibling;
  164. while (sibling) {
  165. if (sibling.matches(siblingSelector)) return sibling;
  166. if (sibling.matches(stopSelector)) return null;
  167. sibling = sibling.previousElementSibling;
  168. }
  169. }