RequestRunner.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. import { Observable } from "rxjs"
  2. import { filter } from "rxjs/operators"
  3. import { chain, right, TaskEither } from "fp-ts/lib/TaskEither"
  4. import { flow, pipe } from "fp-ts/function"
  5. import * as O from "fp-ts/Option"
  6. import * as A from "fp-ts/Array"
  7. import { Environment } from "@hoppscotch/data"
  8. import { runTestScript, TestDescriptor } from "@hoppscotch/js-sandbox"
  9. import { isRight } from "fp-ts/Either"
  10. import {
  11. getCombinedEnvVariables,
  12. getFinalEnvsFromPreRequest,
  13. } from "./preRequest"
  14. import { getEffectiveRESTRequest } from "./utils/EffectiveURL"
  15. import { HoppRESTResponse } from "./types/HoppRESTResponse"
  16. import { createRESTNetworkRequestStream } from "./network"
  17. import { HoppTestData, HoppTestResult } from "./types/HoppTestResult"
  18. import { isJSONContentType } from "./utils/contenttypes"
  19. import { getRESTRequest, setRESTTestResults } from "~/newstore/RESTSession"
  20. import {
  21. environmentsStore,
  22. getCurrentEnvironment,
  23. getEnviroment,
  24. getGlobalVariables,
  25. setGlobalEnvVariables,
  26. updateEnvironment,
  27. } from "~/newstore/environments"
  28. import { TestResult } from "~/../hoppscotch-js-sandbox/lib/test-runner"
  29. const getTestableBody = (
  30. res: HoppRESTResponse & { type: "success" | "fail" }
  31. ) => {
  32. const contentTypeHeader = res.headers.find(
  33. (h) => h.key.toLowerCase() === "content-type"
  34. )
  35. const rawBody = new TextDecoder("utf-8")
  36. .decode(res.body)
  37. .replaceAll("\x00", "")
  38. const x = pipe(
  39. // This pipeline just decides whether JSON parses or not
  40. contentTypeHeader && isJSONContentType(contentTypeHeader.value)
  41. ? O.of(rawBody)
  42. : O.none,
  43. // Try parsing, if failed, go to the fail option
  44. O.chain((body) => O.tryCatch(() => JSON.parse(body))),
  45. // If JSON, return that (get), else return just the body string (else)
  46. O.getOrElse<any | string>(() => rawBody)
  47. )
  48. return x
  49. }
  50. const combineEnvVariables = (env: {
  51. global: Environment["variables"]
  52. selected: Environment["variables"]
  53. }) => [...env.selected, ...env.global]
  54. export const runRESTRequest$ = (): TaskEither<
  55. string | Error,
  56. Observable<HoppRESTResponse>
  57. > =>
  58. pipe(
  59. getFinalEnvsFromPreRequest(
  60. getRESTRequest().preRequestScript,
  61. getCombinedEnvVariables()
  62. ),
  63. chain((envs) => {
  64. const effectiveRequest = getEffectiveRESTRequest(getRESTRequest(), {
  65. name: "Env",
  66. variables: combineEnvVariables(envs),
  67. })
  68. const stream = createRESTNetworkRequestStream(effectiveRequest)
  69. // Run Test Script when request ran successfully
  70. const subscription = stream
  71. .pipe(filter((res) => res.type === "success" || res.type === "fail"))
  72. .subscribe(async (res) => {
  73. if (res.type === "success" || res.type === "fail") {
  74. const runResult = await runTestScript(res.req.testScript, envs, {
  75. status: res.statusCode,
  76. body: getTestableBody(res),
  77. headers: res.headers,
  78. })()
  79. if (isRight(runResult)) {
  80. setRESTTestResults(translateToSandboxTestResults(runResult.right))
  81. setGlobalEnvVariables(runResult.right.envs.global)
  82. if (environmentsStore.value.currentEnvironmentIndex !== -1) {
  83. const env = getEnviroment(
  84. environmentsStore.value.currentEnvironmentIndex
  85. )
  86. updateEnvironment(
  87. environmentsStore.value.currentEnvironmentIndex,
  88. {
  89. name: env.name,
  90. variables: runResult.right.envs.selected,
  91. }
  92. )
  93. }
  94. } else {
  95. setRESTTestResults({
  96. description: "",
  97. expectResults: [],
  98. tests: [],
  99. envDiff: {
  100. global: {
  101. additions: [],
  102. deletions: [],
  103. updations: [],
  104. },
  105. selected: {
  106. additions: [],
  107. deletions: [],
  108. updations: [],
  109. },
  110. },
  111. scriptError: true,
  112. })
  113. }
  114. subscription.unsubscribe()
  115. }
  116. })
  117. return right(stream)
  118. })
  119. )
  120. const getAddedEnvVariables = (
  121. current: Environment["variables"],
  122. updated: Environment["variables"]
  123. ) => updated.filter((x) => current.findIndex((y) => y.key === x.key) === -1)
  124. const getRemovedEnvVariables = (
  125. current: Environment["variables"],
  126. updated: Environment["variables"]
  127. ) => current.filter((x) => updated.findIndex((y) => y.key === x.key) === -1)
  128. const getUpdatedEnvVariables = (
  129. current: Environment["variables"],
  130. updated: Environment["variables"]
  131. ) =>
  132. pipe(
  133. updated,
  134. A.filterMap(
  135. flow(
  136. O.fromPredicate(
  137. (x) => current.findIndex((y) => y.key === x.key) !== -1
  138. ),
  139. O.map((x) => ({
  140. ...x,
  141. previousValue: current.find((y) => x.key === y.key)!.value,
  142. }))
  143. )
  144. )
  145. )
  146. function translateToSandboxTestResults(
  147. testDesc: TestResult & { tests: TestDescriptor }
  148. ): HoppTestResult {
  149. const translateChildTests = (child: TestDescriptor): HoppTestData => {
  150. return {
  151. description: child.descriptor,
  152. expectResults: child.expectResults,
  153. tests: child.children.map(translateChildTests),
  154. }
  155. }
  156. const globals = getGlobalVariables()
  157. const env = getCurrentEnvironment()
  158. return {
  159. description: "",
  160. expectResults: testDesc.tests.expectResults,
  161. tests: testDesc.tests.children.map(translateChildTests),
  162. scriptError: false,
  163. envDiff: {
  164. global: {
  165. additions: getAddedEnvVariables(globals, testDesc.envs.global),
  166. deletions: getRemovedEnvVariables(globals, testDesc.envs.global),
  167. updations: getUpdatedEnvVariables(globals, testDesc.envs.global),
  168. },
  169. selected: {
  170. additions: getAddedEnvVariables(env.variables, testDesc.envs.selected),
  171. deletions: getRemovedEnvVariables(
  172. env.variables,
  173. testDesc.envs.selected
  174. ),
  175. updations: getUpdatedEnvVariables(
  176. env.variables,
  177. testDesc.envs.selected
  178. ),
  179. },
  180. },
  181. }
  182. }