curlparser.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import parser from "yargs-parser"
  2. import * as O from "fp-ts/Option"
  3. import * as A from "fp-ts/Array"
  4. import { pipe, flow } from "fp-ts/function"
  5. import {
  6. FormDataKeyValue,
  7. HoppRESTReqBody,
  8. makeRESTRequest,
  9. } from "@hoppscotch/data"
  10. import { getAuthObject } from "./sub_helpers/auth"
  11. import { getHeaders, recordToHoppHeaders } from "./sub_helpers/headers"
  12. // import { getCookies } from "./sub_helpers/cookies"
  13. import { getQueries } from "./sub_helpers/queries"
  14. import { getMethod } from "./sub_helpers/method"
  15. import { concatParams, getURLObject } from "./sub_helpers/url"
  16. import { preProcessCurlCommand } from "./sub_helpers/preproc"
  17. import { getBody, getFArgumentMultipartData } from "./sub_helpers/body"
  18. import { getDefaultRESTRequest } from "~/newstore/RESTSession"
  19. import {
  20. objHasProperty,
  21. objHasArrayProperty,
  22. } from "~/helpers/functional/object"
  23. const defaultRESTReq = getDefaultRESTRequest()
  24. export const parseCurlCommand = (curlCommand: string) => {
  25. // const isDataBinary = curlCommand.includes(" --data-binary")
  26. // const compressed = !!parsedArguments.compressed
  27. curlCommand = preProcessCurlCommand(curlCommand)
  28. const parsedArguments = parser(curlCommand)
  29. const headerObject = getHeaders(parsedArguments)
  30. const { headers } = headerObject
  31. let { rawContentType } = headerObject
  32. const hoppHeaders = pipe(
  33. headers,
  34. O.fromPredicate(() => Object.keys(headers).length > 0),
  35. O.map(recordToHoppHeaders),
  36. O.getOrElse(() => defaultRESTReq.headers)
  37. )
  38. const method = getMethod(parsedArguments)
  39. // const cookies = getCookies(parsedArguments)
  40. const urlObject = getURLObject(parsedArguments)
  41. const auth = getAuthObject(parsedArguments, headers, urlObject)
  42. let rawData: string | string[] = pipe(
  43. parsedArguments,
  44. O.fromPredicate(objHasArrayProperty("d", "string")),
  45. O.map((args) => args.d),
  46. O.altW(() =>
  47. pipe(
  48. parsedArguments,
  49. O.fromPredicate(objHasProperty("d", "string")),
  50. O.map((args) => args.d)
  51. )
  52. ),
  53. O.getOrElseW(() => "")
  54. )
  55. let body: HoppRESTReqBody["body"] = ""
  56. let contentType: HoppRESTReqBody["contentType"] =
  57. defaultRESTReq.body.contentType
  58. let hasBodyBeenParsed = false
  59. let { queries, danglingParams } = getQueries(
  60. Array.from(urlObject.searchParams.entries())
  61. )
  62. const stringToPair = flow(
  63. decodeURIComponent,
  64. (pair) => <[string, string]>pair.split("=", 2)
  65. )
  66. const pairs = pipe(
  67. rawData,
  68. O.fromPredicate(Array.isArray),
  69. O.map(A.map(stringToPair)),
  70. O.alt(() =>
  71. pipe(
  72. rawData,
  73. O.fromPredicate((s) => s.length > 0),
  74. O.map(() => [stringToPair(rawData as string)])
  75. )
  76. ),
  77. O.getOrElseW(() => undefined)
  78. )
  79. if (objHasProperty("G", "boolean")(parsedArguments) && !!pairs) {
  80. const newQueries = getQueries(pairs)
  81. queries = [...queries, ...newQueries.queries]
  82. danglingParams = [...danglingParams, ...newQueries.danglingParams]
  83. hasBodyBeenParsed = true
  84. } else if (
  85. rawContentType.includes("application/x-www-form-urlencoded") &&
  86. !!pairs
  87. ) {
  88. body = pairs.map((p) => p.join(": ")).join("\n") || null
  89. contentType = "application/x-www-form-urlencoded"
  90. hasBodyBeenParsed = true
  91. }
  92. const urlString = concatParams(urlObject, danglingParams)
  93. let multipartUploads: Record<string, string> = pipe(
  94. O.of(parsedArguments),
  95. O.chain(getFArgumentMultipartData),
  96. O.match(
  97. () => ({}),
  98. (args) => {
  99. hasBodyBeenParsed = true
  100. rawContentType = "multipart/form-data"
  101. return args
  102. }
  103. )
  104. )
  105. if (!hasBodyBeenParsed) {
  106. if (typeof rawData !== "string") {
  107. rawData = rawData.join("")
  108. }
  109. const bodyObject = getBody(rawData, rawContentType, contentType)
  110. if (O.isSome(bodyObject)) {
  111. const bodyObjectValue = bodyObject.value
  112. if (bodyObjectValue.type === "FORMDATA") {
  113. multipartUploads = bodyObjectValue.body
  114. } else {
  115. body = bodyObjectValue.body.body
  116. contentType = bodyObjectValue.body
  117. .contentType as HoppRESTReqBody["contentType"]
  118. }
  119. }
  120. }
  121. const finalBody: HoppRESTReqBody = pipe(
  122. body,
  123. O.fromNullable,
  124. O.filter((b) => b.length > 0),
  125. O.map((b) => <HoppRESTReqBody>{ body: b, contentType }),
  126. O.alt(() =>
  127. pipe(
  128. multipartUploads,
  129. O.of,
  130. O.map((m) => Object.entries(m)),
  131. O.filter((m) => m.length > 0),
  132. O.map(
  133. flow(
  134. A.map(
  135. ([key, value]) =>
  136. <FormDataKeyValue>{
  137. active: true,
  138. isFile: false,
  139. key,
  140. value,
  141. }
  142. ),
  143. (b) =>
  144. <HoppRESTReqBody>{ body: b, contentType: "multipart/form-data" }
  145. )
  146. )
  147. )
  148. ),
  149. O.getOrElse(() => defaultRESTReq.body)
  150. )
  151. return makeRESTRequest({
  152. name: defaultRESTReq.name,
  153. endpoint: urlString,
  154. method: (method || defaultRESTReq.method).toUpperCase(),
  155. params: queries ?? defaultRESTReq.params,
  156. headers: hoppHeaders,
  157. preRequestScript: defaultRESTReq.preRequestScript,
  158. testScript: defaultRESTReq.testScript,
  159. auth,
  160. body: finalBody,
  161. })
  162. }