tryitout.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  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. document.querySelector('#example-requests-' + endpointId).hidden = false;
  33. document.querySelector('#example-responses-' + endpointId).hidden = false;
  34. }
  35. function makeAPICall(method, path, body, query, headers) {
  36. console.log({path, body, query, headers});
  37. if (!(body instanceof FormData)) {
  38. body = JSON.stringify(body)
  39. }
  40. const url = new URL(window.baseUrl + '/' + path.replace(/^\//, ''));
  41. // We need this function because if you try to set an array or object directly to a URLSearchParams object,
  42. // you'll get [object Object] or the array.toString()
  43. function addItemToSearchParamsObject(key, value, searchParams) {
  44. if (Array.isArray(value)) {
  45. value.forEach((v, i) => {
  46. // Append {filters: [first, second]} as filters[0]=first&filters[1]second
  47. addItemToSearchParamsObject(key + '[' + i + ']', v, searchParams);
  48. })
  49. } else if (typeof value === 'object' && value !== null) {
  50. Object.keys(value).forEach((i) => {
  51. // Append {filters: {name: first}} as filters[name]=first
  52. addItemToSearchParamsObject(key + '[' + i + ']', value[i], searchParams);
  53. });
  54. } else {
  55. searchParams.append(key, value);
  56. }
  57. }
  58. Object.keys(query)
  59. .forEach(key => addItemToSearchParamsObject(key, query[key], url.searchParams));
  60. return fetch(url, {
  61. method,
  62. headers,
  63. body: method === 'GET' ? undefined : body,
  64. })
  65. .then(response => Promise.all([response.status, response.text(), response.headers]));
  66. }
  67. function hideCodeSamples(endpointId) {
  68. document.querySelector('#example-requests-' + endpointId).hidden = true;
  69. document.querySelector('#example-responses-' + endpointId).hidden = true;
  70. }
  71. function handleResponse(endpointId, response, status, headers) {
  72. hideCodeSamples(endpointId);
  73. // Hide error views
  74. document.querySelector('#execution-error-' + endpointId).hidden = true;
  75. const responseContentEl = document.querySelector('#execution-response-content-' + endpointId);
  76. // Prettify it if it's JSON
  77. let isJson = false;
  78. try {
  79. const jsonParsed = JSON.parse(response);
  80. if (jsonParsed !== null) {
  81. isJson = true;
  82. response = JSON.stringify(jsonParsed, null, 4);
  83. }
  84. } catch (e) {
  85. }
  86. responseContentEl.textContent = response === '' ? '<Empty response>' : response;
  87. isJson && window.hljs.highlightBlock(responseContentEl);
  88. const statusEl = document.querySelector('#execution-response-status-' + endpointId);
  89. statusEl.textContent = ` (${status})`;
  90. document.querySelector('#execution-results-' + endpointId).hidden = false;
  91. statusEl.scrollIntoView({behavior: "smooth", block: "center"});
  92. }
  93. function handleError(endpointId, err) {
  94. hideCodeSamples(endpointId);
  95. // Hide response views
  96. document.querySelector('#execution-results-' + endpointId).hidden = true;
  97. // Show error views
  98. let errorMessage = err.message || err;
  99. errorMessage += "\n\nTip: Check that you're properly connected to the network.";
  100. errorMessage += "\nIf you're a maintainer of ths API, verify that your API is running and you've enabled CORS.";
  101. errorMessage += "\nYou can check the Dev Tools console for debugging information.";
  102. document.querySelector('#execution-error-message-' + endpointId).textContent = errorMessage;
  103. const errorEl = document.querySelector('#execution-error-' + endpointId);
  104. errorEl.hidden = false;
  105. errorEl.scrollIntoView({behavior: "smooth", block: "center"});
  106. }
  107. async function executeTryOut(endpointId, form) {
  108. const executeBtn = document.querySelector(`#btn-executetryout-${endpointId}`);
  109. executeBtn.textContent = "⏱ Sending...";
  110. executeBtn.scrollIntoView({behavior: "smooth", block: "center"});
  111. let body;
  112. let setter;
  113. if (form.dataset.hasfiles === "0") {
  114. body = {};
  115. setter = (name, value) => _.set(body, name, value);
  116. } else {
  117. body = new FormData();
  118. setter = (name, value) => body.append(name, value);
  119. }
  120. const bodyParameters = form.querySelectorAll('input[data-component=body]');
  121. bodyParameters.forEach(el => {
  122. let value = el.value;
  123. if (el.type === 'file' && el.files[0]) {
  124. setter(el.name, el.files[0]);
  125. return;
  126. }
  127. if (el.type !== 'radio') {
  128. if (value === "" && el.required === false) {
  129. // Don't include empty optional values in the request
  130. return;
  131. }
  132. setter(el.name, value);
  133. return;
  134. }
  135. if (el.checked) {
  136. value = (value === 'false') ? false : true;
  137. setter(el.name, value);
  138. }
  139. });
  140. const query = {};
  141. const queryParameters = form.querySelectorAll('input[data-component=query]');
  142. queryParameters.forEach(el => _.set(query, el.name, el.value));
  143. let path = form.dataset.path;
  144. const urlParameters = form.querySelectorAll('input[data-component=url]');
  145. urlParameters.forEach(el => (path = path.replace(new RegExp(`\\{${el.name}\\??}`), el.value)));
  146. const headers = JSON.parse(form.dataset.headers);
  147. // Check for auth param that might go in header
  148. if (form.dataset.authed === "1") {
  149. const authHeaderEl = form.querySelector('input[data-component=header]');
  150. if (authHeaderEl) headers[authHeaderEl.name] = authHeaderEl.dataset.prefix + authHeaderEl.value;
  151. }
  152. // When using FormData, the browser sets the correct content-type + boundary
  153. if (body instanceof FormData) {
  154. delete headers['Content-Type'];
  155. }
  156. makeAPICall(form.dataset.method, path, body, query, headers)
  157. .then(([responseStatus, responseContent, responseHeaders]) => {
  158. handleResponse(endpointId, responseContent, responseStatus, responseHeaders)
  159. })
  160. .catch(err => {
  161. console.log("Error while making request: ", err);
  162. handleError(endpointId, err);
  163. })
  164. .finally(() => {
  165. executeBtn.textContent = "Send Request 💥";
  166. });
  167. }