RequestRunner.ts 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import { Observable } from "rxjs"
  2. import { filter } from "rxjs/operators"
  3. import { chain, right, TaskEither } from "fp-ts/lib/TaskEither"
  4. import { pipe } from "fp-ts/function"
  5. import * as O from "fp-ts/Option"
  6. import { runTestScript, TestDescriptor } from "@hoppscotch/js-sandbox"
  7. import { isRight } from "fp-ts/Either"
  8. import {
  9. getCombinedEnvVariables,
  10. getFinalEnvsFromPreRequest,
  11. } from "./preRequest"
  12. import { getEffectiveRESTRequest } from "./utils/EffectiveURL"
  13. import { HoppRESTResponse } from "./types/HoppRESTResponse"
  14. import { createRESTNetworkRequestStream } from "./network"
  15. import { HoppTestData, HoppTestResult } from "./types/HoppTestResult"
  16. import { isJSONContentType } from "./utils/contenttypes"
  17. import { getRESTRequest, setRESTTestResults } from "~/newstore/RESTSession"
  18. const getTestableBody = (
  19. res: HoppRESTResponse & { type: "success" | "fail" }
  20. ) => {
  21. const contentTypeHeader = res.headers.find(
  22. (h) => h.key.toLowerCase() === "content-type"
  23. )
  24. const rawBody = new TextDecoder("utf-8")
  25. .decode(res.body)
  26. .replaceAll("\x00", "")
  27. const x = pipe(
  28. // This pipeline just decides whether JSON parses or not
  29. contentTypeHeader && isJSONContentType(contentTypeHeader.value)
  30. ? O.of(rawBody)
  31. : O.none,
  32. // Try parsing, if failed, go to the fail option
  33. O.chain((body) => O.tryCatch(() => JSON.parse(body))),
  34. // If JSON, return that (get), else return just the body string (else)
  35. O.getOrElse<any | string>(() => rawBody)
  36. )
  37. return x
  38. }
  39. export const runRESTRequest$ = (): TaskEither<
  40. string | Error,
  41. Observable<HoppRESTResponse>
  42. > =>
  43. pipe(
  44. getFinalEnvsFromPreRequest(
  45. getRESTRequest().preRequestScript,
  46. getCombinedEnvVariables()
  47. ),
  48. chain((envs) => {
  49. const effectiveRequest = getEffectiveRESTRequest(getRESTRequest(), {
  50. name: "Env",
  51. variables: envs,
  52. })
  53. const stream = createRESTNetworkRequestStream(effectiveRequest)
  54. // Run Test Script when request ran successfully
  55. const subscription = stream
  56. .pipe(filter((res) => res.type === "success" || res.type === "fail"))
  57. .subscribe(async (res) => {
  58. if (res.type === "success" || res.type === "fail") {
  59. const runResult = await runTestScript(res.req.testScript, {
  60. status: res.statusCode,
  61. body: getTestableBody(res),
  62. headers: res.headers,
  63. })()
  64. // TODO: Handle script executation fails (isLeft)
  65. if (isRight(runResult)) {
  66. setRESTTestResults(translateToSandboxTestResults(runResult.right))
  67. }
  68. subscription.unsubscribe()
  69. }
  70. })
  71. return right(stream)
  72. })
  73. )
  74. function translateToSandboxTestResults(
  75. testDesc: TestDescriptor
  76. ): HoppTestResult {
  77. const translateChildTests = (child: TestDescriptor): HoppTestData => {
  78. return {
  79. description: child.descriptor,
  80. expectResults: child.expectResults,
  81. tests: child.children.map(translateChildTests),
  82. }
  83. }
  84. return {
  85. expectResults: testDesc.expectResults,
  86. tests: testDesc.children.map(translateChildTests),
  87. }
  88. }