convert to typescript (#368)

* compile with typescript

* add types

* rename tree-sitter import file

* proofread README.md
This commit is contained in:
Boris Verkhovskiy
2022-03-30 22:56:25 -07:00
committed by GitHub
parent 9c01335485
commit f29c397882
568 changed files with 3342 additions and 2352 deletions

2
.eslintignore Normal file
View File

@@ -0,0 +1,2 @@
dist
test/fixtures/

24
.eslintrc.json Normal file
View File

@@ -0,0 +1,24 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"prettier"
],
"rules": {
"no-empty": ["error", { "allowEmptyCatch": true }]
},
"parserOptions": {
"ecmaVersion": 2018,
"sourceType": "module"
},
"ignorePatterns": [
"out",
"dist",
"**/*.d.ts"
]
}

2
.gitignore vendored
View File

@@ -7,3 +7,5 @@ npm-debug.log
# Generated by `npm run prepare`
tree-sitter-bash.wasm
dist/

View File

@@ -5,12 +5,11 @@ Standard.xml
.github/*
.gitattributes
docs/*
.gitmodules
extract_curl_args.py
fixtures/*
tools/*
test.js
test-utils.js
src/tools
test
dist/test
dist/tools

5
.prettierrc Normal file
View File

@@ -0,0 +1,5 @@
{
"semi": false,
"singleQuote": true,
"trailingComma": "none"
}

View File

@@ -84,14 +84,14 @@ Note: you have to add `"type": "module"` to your package.json for the above exam
Make sure you're running **Node 12** or greater. The test suite will fail on older versions of Node.js.
If you add a new generator, make sure to update the list of supported languages in [bin/cli.js](bin/cli.js) or else it won't be accessible from the command line. Further, you'll want to update test.js and index.js for your new generator to make it part of the testing.
If you add a new generator, make sure to update the list of supported languages in [src/cli.ts](src/cli.ts) or else it won't be accessible from the command line. Further, you'll want to update test.js and index.js for your new generator to make it part of the testing.
If you want to add new functionality, start with a test.
- Create a file containing the curl command in `fixtures/curl_commands` with a descriptive filename like `post_with_headers.sh`
- Create a file containing the output in `fixtures/python/` with a matching filename (but different extension) like `post_with_headers.py`
- Create a file containing the curl command in `test/fixtures/curl_commands` with a descriptive filename like `post_with_headers.sh`
- Create a file containing the output in `test/fixtures/python/` with a matching filename (but different extension) like `post_with_headers.py`
- Run tests with `npm test`.
- If your filenames match correctly, you should see one failing test. Fix it by modifying the parser in `util.js` or the generators in `generators/`
- If your filenames match correctly, you should see one failing test. Fix it by modifying the parser in `util.ts` or the generators in `src/generators/`
The parser generates a generic data structure consumed by code generator functions.
@@ -99,25 +99,21 @@ You can run a specific test with:
``` sh
npm test -- test_name
# or
node test.js test_name
```
where `test_name` is a file (without the `.sh` extension) in `fixtures/curl_commands/`
where `test_name` is a file (without the `.sh` extension) in `test/fixtures/curl_commands/`
You can run only the tests for a specific language generator with:
``` sh
npm test -- --language=python
# or
node test.js --language=python
```
I recommend setting this up with a debugger so you can see exactly what the parser is passing to the generator.
Here's my Intellij run configuration for a single test:
![Screenshot of intellij debug configuration](/docs/intellijconfig.png)
Before submitting a PR, please check that your JS code conforms to the code style enforced by [StandardJS](https://standardjs.com) with
Before submitting a PR, please check that your JS code conforms to our code style with
```sh
npm run lint
@@ -126,7 +122,7 @@ npm run lint
Use the following to fix your code if it doesn't:
```sh
npm run lint:fix
npm run fix
```
If you get stuck, please reach out via email. I am always willing to hop on a Google Hangout and pair program.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

4263
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,14 +1,17 @@
{
"name": "curlconverter",
"version": "4.0.0-alpha.10",
"description": "convert curl commands to Python, JavaScript, Go, PHP and other languages",
"homepage": "https://github.com/curlconverter/curlconverter",
"description": "convert Curl commands to Python, JavaScript, Go, PHP and other languages",
"homepage": "https://curlconverter.com",
"author": {
"name": "Nick Carneiro",
"email": "nickc@trillworks.com",
"url": "http://trillworks.com"
"url": "https://trillworks.com"
},
"repository": {
"type": "git",
"url": "https://github.com/curlconverter/curlconverter.git"
},
"repository": "curlconverter/curlconverter",
"license": "MIT",
"keywords": [
"curl",
@@ -17,11 +20,10 @@
"python",
"converter"
],
"main": "dist/src/index.js",
"type": "module",
"main": "index.js",
"dependencies": {
"@curlconverter/tree-sitter-bash": "^0.0.2",
"cookie": "^0.4.2",
"jsesc": "^3.0.2",
"nunjucks": "^3.2.3",
"query-string": "^7.1.1",
@@ -30,30 +32,44 @@
"yamljs": "^0.3.0"
},
"devDependencies": {
"@types/diff": "^5.0.2",
"@types/glob": "^7.2.0",
"@types/jsesc": "^3.0.1",
"@types/nunjucks": "^3.2.1",
"@types/tape": "^4.13.2",
"@types/yamljs": "^0.2.31",
"@types/yargs": "^17.0.10",
"@typescript-eslint/eslint-plugin": "^5.17.0",
"@typescript-eslint/parser": "^5.17.0",
"colors": "^1.4.0",
"diff": "^5.0.0",
"standard": "^17.0.0-2",
"eslint": "^8.12.0",
"eslint-config-prettier": "^8.5.0",
"glob": "^7.2.0",
"mocha": "^9.2.2",
"prettier": "^2.6.1",
"tape": "^5.5.2",
"tree-sitter-cli": "^0.20.6",
"yargs": "^17.3.1"
"typescript": "^4.6.3",
"yargs": "^17.4.0"
},
"scripts": {
"test": "tape test.js",
"lint": "standard",
"lint:fix": "standard --fix",
"prepare": "tree-sitter build-wasm --docker node_modules/@curlconverter/tree-sitter-bash",
"gen-test": "./tools/gen-test.js",
"compare-requests": "./tools/compare-requests.js",
"compare-request": "./tools/compare-requests.js"
"compile": "tsc",
"watch": "tsc -watch -p ./",
"pretest": "npm run compile",
"test": "NODE_OPTIONS=--enable-source-maps npx tape dist/test/test.js",
"prelint": "npm run compile",
"lint": "eslint src --ext ts",
"prefix": "npm run compile",
"fix": "eslint src --ext ts --fix",
"prepare": "npm run compile && tree-sitter build-wasm --docker node_modules/@curlconverter/tree-sitter-bash && mv tree-sitter-bash.wasm dist/",
"pregen-test": "npm run compile",
"gen-test": "node --enable-source-maps ./dist/tools/gen-test.js",
"compare-requests": "npm run compile && node --enable-source-maps ./dist/tools/compare-requests.js",
"compare-request": "npm run compile && node --enable-source-maps ./dist/tools/compare-requests.js"
},
"standard": {
"ignore": [
"/fixtures/",
"/dist/"
]
},
"bin": "bin/cli.js",
"bin": "dist/src/cli.js",
"browser": {
"./parser.js": "./browser-parser.js"
"./dist/src/bash-parser.js": "./dist/src/bash-parser-web.js"
}
}

View File

@@ -1,23 +1,24 @@
#!/usr/bin/env node
import { curlLongOpts, curlShortOpts, parseArgs, buildRequest, parseCurlCommand, CCError } from '../util.js'
import { curlLongOpts, curlShortOpts, parseArgs, buildRequest, parseCurlCommand, CCError, has } from './util.js'
import type { LongOpts, ShortOpts, Request } from './util.js'
import { _toAnsible } from '../generators/ansible.js'
import { _toDart } from '../generators/dart.js'
import { _toElixir } from '../generators/elixir.js'
import { _toGo } from '../generators/go.js'
import { _toJava } from '../generators/java.js'
import { _toJavaScript } from '../generators/javascript/javascript.js'
import { _toJsonString } from '../generators/json.js'
import { _toMATLAB } from '../generators/matlab/matlab.js'
import { _toNode } from '../generators/javascript/node-fetch.js'
import { _toNodeRequest } from '../generators/javascript/node-request.js'
import { _toPhp } from '../generators/php/php.js'
import { _toPhpRequests } from '../generators/php/php-requests.js'
import { _toPython } from '../generators/python.js'
import { _toR } from '../generators/r.js'
import { _toRust } from '../generators/rust.js'
import { _toStrest } from '../generators/strest.js'
import { _toAnsible } from './generators/ansible.js'
import { _toDart } from './generators/dart.js'
import { _toElixir } from './generators/elixir.js'
import { _toGo } from './generators/go.js'
import { _toJava } from './generators/java.js'
import { _toJavaScript } from './generators/javascript/javascript.js'
import { _toJsonString } from './generators/json.js'
import { _toMATLAB } from './generators/matlab/matlab.js'
import { _toNode } from './generators/javascript/node-fetch.js'
import { _toNodeRequest } from './generators/javascript/node-request.js'
import { _toPhp } from './generators/php/php.js'
import { _toPhpRequests } from './generators/php/php-requests.js'
import { _toPython } from './generators/python.js'
import { _toR } from './generators/r.js'
import { _toRust } from './generators/rust.js'
import { _toStrest } from './generators/strest.js'
import fs from 'fs'
@@ -29,9 +30,9 @@ const defaultLanguage = 'python'
// Maps options for --language to functions
// NOTE: make sure to update this when adding language support
const translate = {
const translate: {[key: string]: (request: Request) => string} = {
ansible: _toAnsible,
browser: _toJavaScript, // backwards compatibility, undocumented
browser: _toJavaScript, // for backwards compatibility, undocumented
dart: _toDart,
elixir: _toElixir,
go: _toGo,
@@ -74,29 +75,29 @@ language: the language to convert the curl command to. The choices are
curl_options: these should be passed exactly as they would be passed to curl.
see 'curl --help' or 'curl --manual' for which options are allowed here`
const curlConverterLongOpts = {
const curlConverterLongOpts: LongOpts = {
language: { type: 'string', name: 'language' },
stdin: { type: 'boolean', name: 'stdin' }
stdin: { type: 'bool', name: 'stdin' }
}
const curlConverterShortOpts = {
const curlConverterShortOpts: ShortOpts = {
// a single - (dash) tells curlconverter to read input from stdin
'': 'stdin'
}
const opts = [
const opts: [LongOpts, ShortOpts] = [
{ ...curlLongOpts, ...curlConverterLongOpts },
{ ...curlShortOpts, ...curlConverterShortOpts }
]
const exitWithError = (error, verbose = false) => {
let errMsg = error
function exitWithError (error: unknown, verbose = false): never {
let errMsg: Error | string | unknown = error
if (!verbose) {
if (error instanceof CCError) {
errMsg = ''
for (const line of error.message.toString().split('\n')) {
errMsg += 'error: ' + line + '\n'
}
errMsg = errMsg.trimEnd()
} else {
errMsg = (errMsg as string).trimEnd()
} else if (error instanceof Error) {
// .toString() removes the traceback
errMsg = error.toString()
}
@@ -124,7 +125,7 @@ if (parsedArguments.version) {
const argc = Object.keys(parsedArguments).length
const language = parsedArguments.language || defaultLanguage
const stdin = parsedArguments.stdin
if (!Object.prototype.hasOwnProperty.call(translate, language)) {
if (!has(translate, language)) {
exitWithError(
new CCError('unexpected --language: ' + JSON.stringify(language) + '\n' +
'must be one of: ' + Object.keys(translate).join(', ')),
@@ -168,7 +169,7 @@ if (argc === 0) {
}
// Warning for users using the pre-4.0 CLI
if (request.url && request.url.startsWith('curl ')) {
if (request.url?.startsWith('curl ')) {
console.error('warning: Passing a whole curl command as a single argument?')
console.error('warning: Pass options to curlconverter as if it was curl instead:')
console.error("warning: curlconverter 'curl example.com' -> curlconverter example.com")

View File

@@ -1,8 +1,11 @@
import * as util from '../util.js'
import type { Request} from '../util.js'
import { ansibleTemplate } from '../templates/ansible.js'
import nunjucks from 'nunjucks'
import querystring from 'query-string'
import { ansibleTemplate } from '../templates/ansible.js'
function getDataString (request) {
function getDataString (request: Request): any {
const parsedQueryString = querystring.parse(request.data, { sort: false })
const keyCount = Object.keys(parsedQueryString).length
const singleKeyOnly = keyCount === 1 && !parsedQueryString[Object.keys(parsedQueryString)[0]]
@@ -16,7 +19,7 @@ function getDataString (request) {
return request.data
}
export const _toAnsible = request => {
export const _toAnsible = (request: Request): string => {
let convertedData
if (request.data && typeof request.data === 'string') {
convertedData = getDataString(request)
@@ -24,7 +27,7 @@ export const _toAnsible = request => {
const result = nunjucks.renderString(ansibleTemplate, { request, data: convertedData })
return result
}
export const toAnsible = curlCommand => {
export const toAnsible = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toAnsible(request)
}

View File

@@ -1,7 +1,9 @@
import * as util from '../util.js'
import type { Request} from '../util.js'
import jsesc from 'jsesc'
function repr (value) {
function repr (value: string): string {
// In context of url parameters, don't accept nulls and such.
if (!value) {
return "''"
@@ -10,7 +12,7 @@ function repr (value) {
}
}
export const _toDart = r => {
export const _toDart = (r: Request): string => {
let s = ''
if (r.auth || r.isDataBinary) s += "import 'dart:convert';\n"
@@ -47,8 +49,7 @@ export const _toDart = r => {
s += '\n'
}
const hasQuery = r.query
if (hasQuery) {
if (r.query) {
// TODO: dict won't work with repeated keys
s += ' var params = {\n'
for (const [paramName, rawValue] of r.query) {
@@ -84,7 +85,7 @@ export const _toDart = r => {
}
}
if (hasQuery) {
if (r.query) {
s += " var url = Uri.parse('" + r.urlWithoutQuery + "?$query');\n"
} else {
s += " var url = Uri.parse('" + r.url + "');\n"
@@ -104,7 +105,7 @@ export const _toDart = r => {
return s + '\n'
}
export const toDart = curlCommand => {
export const toDart = (curlCommand: string | string[]): string => {
const r = util.parseCurlCommand(curlCommand)
return _toDart(r)
}

View File

@@ -1,18 +1,21 @@
import * as util from '../util.js'
import type { Request} from '../util.js'
import jsesc from 'jsesc'
import querystring from 'query-string'
function repr (value) {
// TODO: I bet elixir's array syntax is different and if the query string
// values are arrays that actually generates broken code.
function repr (value: string | null | (string | null)[]): string {
// In context of url parameters, don't accept nulls and such.
if (!value) {
return '""'
} else {
return `~s|${jsesc(value, { quotes: 'backticks' })}|`
return `~s|${jsesc(value, { quotes: 'backtick' })}|`
}
}
function getCookies (request) {
function getCookies (request: Request): string {
if (!request.cookies) {
return ''
}
@@ -24,7 +27,7 @@ function getCookies (request) {
return `cookies: [~s|${cookies.join('; ')}|]`
}
function getOptions (request) {
function getOptions (request: Request): string {
const hackneyOptions = []
const auth = getBasicAuth(request)
@@ -49,7 +52,7 @@ function getOptions (request) {
return `[${hackneyOptionsString}]`
}
function getBasicAuth (request) {
function getBasicAuth (request: Request): string {
if (!request.auth) {
return ''
}
@@ -58,7 +61,7 @@ function getBasicAuth (request) {
return `basic_auth: {${repr(user)}, ${repr(password)}}`
}
function getQueryDict (request) {
function getQueryDict (request: Request): string {
if (!request.query) {
return '[]'
}
@@ -70,7 +73,7 @@ function getQueryDict (request) {
return queryDict
}
function getHeadersDict (request) {
function getHeadersDict (request: Request): string {
if (!request.headers) {
return '[]'
}
@@ -82,7 +85,7 @@ function getHeadersDict (request) {
return dict
}
function getBody (request) {
function getBody (request: Request): string {
const formData = getFormDataString(request)
if (formData) {
@@ -92,7 +95,7 @@ function getBody (request) {
return '""'
}
function getFormDataString (request) {
function getFormDataString (request: Request): string {
if (request.data && typeof request.data === 'string') {
return getDataString(request)
}
@@ -101,8 +104,8 @@ function getFormDataString (request) {
return ''
}
let fileArgs = []
let dataArgs = []
let fileArgs: string[] | string = []
let dataArgs: string[] | string = []
for (const [multipartKey, multipartValue] of request.multipartUploads) {
if (multipartValue.startsWith('@')) {
const fileName = multipartValue.slice(1)
@@ -112,7 +115,7 @@ function getFormDataString (request) {
}
}
let content = []
let content: string[] | string = []
fileArgs = fileArgs.join(',\n')
if (fileArgs) {
content.push(fileArgs)
@@ -133,7 +136,7 @@ ${content}
return ''
}
function getDataString (request) {
function getDataString (request: Request): string {
if (!request.isDataRaw && request.data.startsWith('@')) {
const filePath = request.data.slice(1)
if (request.isDataBinary) {
@@ -155,7 +158,7 @@ function getDataString (request) {
}
}
function getMultipleDataString (request, parsedQueryString) {
function getMultipleDataString (request: Request, parsedQueryString: querystring.ParsedQuery<string>): string {
let repeatedKey = false
for (const key in parsedQueryString) {
const value = parsedQueryString[key]
@@ -194,7 +197,7 @@ ${data.join(',\n')}
return dataString
}
export const _toElixir = request => {
export const _toElixir = (request: Request): string => {
// curl automatically prepends 'http' if the scheme is missing, but python fails and returns an error
// we tack it on here to mimic curl
if (!request.url.match(/https?:/)) {
@@ -221,7 +224,7 @@ response = HTTPoison.request(request)
return template
}
export const toElixir = curlCommand => {
export const toElixir = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toElixir(request)
}

View File

@@ -1,7 +1,9 @@
import * as util from '../util.js'
import type { Request} from '../util.js'
import jsesc from 'jsesc'
export const _toGo = request => {
export const _toGo = (request: Request): string => {
let goCode = 'package main\n\n'
goCode += 'import (\n\t"fmt"\n\t"io/ioutil"\n\t"log"\n\t"net/http"\n)\n\n'
goCode += 'func main() {\n'
@@ -42,7 +44,7 @@ export const _toGo = request => {
return goCode + '\n'
}
export const toGo = curlCommand => {
export const toGo = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toGo(request)
}

View File

@@ -1,10 +1,11 @@
import * as util from '../util.js'
import type { Request} from '../util.js'
import jsesc from 'jsesc'
const doubleQuotes = str => jsesc(str, { quotes: 'double' })
const doubleQuotes = (str: string): string => jsesc(str, { quotes: 'double' })
export const _toJava = request => {
export const _toJava = (request: Request): string => {
let javaCode = ''
if (request.auth) {
@@ -31,8 +32,11 @@ export const _toJava = request => {
let gzip = false
if (request.headers) {
for (const [headerName, headerValue] of request.headers) {
if (headerValue === null) {
continue
}
javaCode += '\t\thttpConn.setRequestProperty("' + headerName + '", "' + doubleQuotes(headerValue) + '");\n'
if (headerName.toLowerCase() === 'accept-encoding') {
if (headerName.toLowerCase() === 'accept-encoding' && headerValue) {
gzip = headerValue.indexOf('gzip') !== -1
}
}
@@ -74,7 +78,7 @@ export const _toJava = request => {
return javaCode + '\n'
}
export const toJava = curlCommand => {
export const toJava = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toJava(request)
}

View File

@@ -1,7 +1,9 @@
import * as util from '../../util.js'
import type { Request} from '../../util.js'
import jsesc from 'jsesc'
export const _toJavaScript = request => {
export const _toJavaScript = (request: Request): string => {
let jsFetchCode = ''
if (request.data) {
@@ -73,7 +75,7 @@ export const _toJavaScript = request => {
return jsFetchCode + '\n'
}
export const toJavaScript = curlCommand => {
export const toJavaScript = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toJavaScript(request)
}

View File

@@ -1,12 +1,13 @@
import * as util from '../../util.js'
import type { Request } from '../../util.js'
import { _toJavaScript } from './javascript.js'
const importStatement = 'var fetch = require(\'node-fetch\');\n\n'
export const _toNode = request => {
export const _toNode = (request: Request): string => {
return importStatement + _toJavaScript(request)
}
export const toNode = curlCommand => {
export const toNode = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toNode(request)
}

View File

@@ -1,7 +1,9 @@
import * as util from '../../util.js'
import type { Request} from '../../util.js'
import jsesc from 'jsesc'
export const _toNodeRequest = request => {
export const _toNodeRequest = (request: Request): string => {
let nodeRequestCode = 'var request = require(\'request\');\n\n'
if (request.headers) {
nodeRequestCode += 'var headers = {\n'
@@ -62,7 +64,7 @@ export const _toNodeRequest = request => {
return nodeRequestCode + '\n'
}
export const toNodeRequest = curlCommand => {
export const toNodeRequest = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toNodeRequest(request)
}

View File

@@ -1,22 +1,26 @@
// Author: ssi-anik (sirajul.islam.anik@gmail.com)
import * as util from '../util.js'
import type { Request, QueryDict } from '../util.js'
import querystring from 'query-string'
import jsesc from 'jsesc'
function repr (value, isKey) {
// In context of url parameters, don't accept nulls and such.
/*
if ( !value ) {
return ""
} else {
return "'" + jsesc(value, { quotes: 'single' }) + "'"
} */
return isKey ? "'" + jsesc(value, { quotes: 'single' }) + "'" : value
type JSONOutput = {
url: string,
raw_url: string,
method: string,
cookies?: { [key: string]: string },
headers?: { [key: string]: string | null },
queries?: QueryDict,
data?: { [key: string]: string },
// raw_data?: string[],
files?: { [key: string]: string },
// raw_files: string[],
insecure?: boolean,
auth?: {user: string, password: string},
}
function getDataString (request) {
function getDataString (request: Request) {
/*
if ( !request.isDataRaw && request.data.startsWith('@') ) {
var filePath = request.data.slice(1);
@@ -29,57 +33,55 @@ function getDataString (request) {
const singleKeyOnly = keyCount === 1 && !parsedQueryString[Object.keys(parsedQueryString)[0]]
const singularData = request.isDataBinary || singleKeyOnly
if (singularData) {
const data = {}
const data: {[key: string]: string} = {}
// TODO: dataRaw = request.data ?
data[repr(request.data)] = ''
data[request.data] = ''
return { data }
} else {
return getMultipleDataString(request, parsedQueryString)
}
}
function getMultipleDataString (request, parsedQueryString) {
const data = {}
function getMultipleDataString (request: Request, parsedQueryString: querystring.ParsedQuery<string> ) {
const data: {[key: string]: string | string[]} = {}
for (const key in parsedQueryString) {
const value = parsedQueryString[key]
if (Array.isArray(value)) {
data[repr(key)] = value
data[key] = value.map((v: string | null) => v ? v : '')
} else {
data[repr(key)] = repr(value)
data[key] = value ? value : ''
}
}
return { data }
}
function getFilesString (request) {
const data = {}
data.files = {}
data.data = {}
function getFilesString (request: Request): {'files'?: {[key: string]: string}, 'data'?: {[key: string]: string}} | undefined {
if (!request.multipartUploads) {
return undefined
}
const data: {files: { [key: string]: string }, data: { [key: string]: string }} = {
files : {},
data : {}
}
for (const [multipartKey, multipartValue] of request.multipartUploads) {
if (multipartValue.startsWith('@')) {
const fileName = multipartValue.slice(1)
data.files[repr(multipartKey)] = repr(fileName)
data.files[multipartKey] = fileName
} else {
data.data[repr(multipartKey)] = repr(multipartValue)
data.data[multipartKey] = multipartValue
}
}
if (Object.keys(data.files).length === 0) {
delete data.files
return {
files: Object.keys(data.files).length ? data.files : undefined,
data: Object.keys(data.data).length ? data.data : undefined
}
if (Object.keys(data.data).length === 0) {
delete data.data
}
return data
}
export const _toJsonString = request => {
export const _toJsonString = (request: Request) => {
// curl automatically prepends 'http' if the scheme is missing, but python fails and returns an error
// we tack it on here to mimic curl
if (!request.url.match(/https?:/)) {
@@ -89,7 +91,7 @@ export const _toJsonString = request => {
request.urlWithoutQuery = 'http://' + request.urlWithoutQuery
}
const requestJson = {
const requestJson: JSONOutput = {
url: (request.queryDict ? request.urlWithoutQuery : request.url).replace(/\/$/, ''),
// url: request.queryDict ? request.urlWithoutQuery : request.url,
raw_url: request.url,
@@ -118,6 +120,7 @@ export const _toJsonString = request => {
requestJson.queries = request.queryDict
}
// TODO: not Object.assign, doesn't work with type system
if (request.data && typeof request.data === 'string') {
Object.assign(requestJson, getDataString(request))
} else if (request.multipartUploads) {
@@ -131,14 +134,14 @@ export const _toJsonString = request => {
if (request.auth) {
const [user, password] = request.auth
requestJson.auth = {
user: repr(user),
password: repr(password)
user: user,
password: password
}
}
return JSON.stringify(Object.keys(requestJson).length ? requestJson : '{}', null, 4) + '\n'
}
export const toJsonString = curlCommand => {
export const toJsonString = (curlCommand: string | string[]) => {
const request = util.parseCurlCommand(curlCommand)
return _toJsonString(request)
}

View File

@@ -1,8 +1,9 @@
import { CCError } from '../../util.js'
import type { Request} from '../../util.js'
import jsesc from 'jsesc'
const repr = (value) => {
const repr = (value?: string | null) => {
// In context of url parameters, don't accept nulls and such.
if (!value) {
return "''"
@@ -11,7 +12,7 @@ const repr = (value) => {
return "'" + jsesc(value, { quotes: 'single' }).replace(/\\'/g, "''") + "'"
}
const setVariableValue = (outputVariable, value, termination) => {
const setVariableValue = (outputVariable: string | null, value: string, termination?: string): string => {
let result = ''
if (outputVariable) {
@@ -23,7 +24,7 @@ const setVariableValue = (outputVariable, value, termination) => {
return result
}
const callFunction = (outputVariable, functionName, params, termination) => {
const callFunction = (outputVariable: string | null, functionName: string, params: string | string[] | string[][], termination?: string) => {
let functionCall = functionName + '('
if (Array.isArray(params)) {
const singleLine = params.map(x => Array.isArray(x) ? x.join(', ') : x).join(', ')
@@ -47,7 +48,7 @@ const callFunction = (outputVariable, functionName, params, termination) => {
return setVariableValue(outputVariable, functionCall, termination)
}
const addCellArray = (mapping, keysNotToQuote, keyValSeparator, indentLevel, pairs) => {
const addCellArray = (mapping: {[key: string]: string | null | (null | string)[]}, keysNotToQuote: string[], keyValSeparator: string, indentLevel: number, pairs?: boolean) => {
const indentUnit = ' '.repeat(4)
const indent = indentUnit.repeat(indentLevel)
const indentPrevLevel = indentUnit.repeat(indentLevel - 1)
@@ -57,22 +58,27 @@ const addCellArray = (mapping, keysNotToQuote, keyValSeparator, indentLevel, pai
let response = pairs ? '' : '{'
if (entries.length === 1) {
let [key, value] = entries.pop()
if (keysNotToQuote && !keysNotToQuote.includes(key)) value = `${repr(value)}`
response += `${repr(key)}${keyValSeparator} ${value}`
const [key, value] = entries.pop() as [string, string]
let val = value
if (keysNotToQuote && !keysNotToQuote.includes(key)) val = `${repr(value)}`
response += `${repr(key)}${keyValSeparator} ${val}`
} else {
if (pairs) response += '...'
let counter = entries.length
for (let [key, value] of entries) {
for (const [key, value] of entries) {
let val = value
if (val === null) {
continue
}
--counter
if (keysNotToQuote && !keysNotToQuote.includes(key)) {
if (typeof value === 'object') {
value = `[${value.map(repr).join()}]`
if (typeof val === 'object') {
val = `[${val.map(repr).join()}]`
} else {
value = `${repr(value)}`
val = `${repr(val)}`
}
}
response += `\n${indent}${repr(key)}${keyValSeparator} ${value}`
response += `\n${indent}${repr(key)}${keyValSeparator} ${val}`
if (pairs) {
if (counter !== 0) response += ','
response += '...'
@@ -84,7 +90,7 @@ const addCellArray = (mapping, keysNotToQuote, keyValSeparator, indentLevel, pai
return response
}
const structify = (obj, indentLevel) => {
const structify = (obj: any, indentLevel?: number) => {
let response = ''
indentLevel = !indentLevel ? 1 : ++indentLevel
const indent = ' '.repeat(4 * indentLevel)
@@ -137,27 +143,25 @@ const structify = (obj, indentLevel) => {
return response
}
const containsBody = (request) => {
return Object.prototype.hasOwnProperty.call(request, 'data') || request.multipartUploads
const containsBody = (request: Request): boolean => {
return Boolean(Object.prototype.hasOwnProperty.call(request, 'data') || request.multipartUploads)
}
const prepareQueryString = (request) => {
let response = null
const prepareQueryString = (request: Request): string | null => {
if (request.queryDict) {
const params = addCellArray(request.queryDict, [], '', 1)
response = setVariableValue('params', params)
return setVariableValue('params', params)
}
return response
return null
}
const prepareCookies = (request) => {
let response = null
const prepareCookies = (request: Request): string | null => {
if (request.cookies) {
// TODO: throws away repeat cookies
const cookies = addCellArray(Object.fromEntries(request.cookies), [], '', 1)
response = setVariableValue('cookies', cookies)
return setVariableValue('cookies', cookies)
}
return response
return null
}
const cookieString = 'char(join(join(cookies, \'=\'), \'; \'))'

View File

@@ -3,8 +3,9 @@ import {
structify, containsBody, prepareQueryString,
prepareCookies
} from './common.js'
import type { Request} from '../../util.js'
const prepareHeaders = (request) => {
const prepareHeaders = (request: Request): string | null => {
let response = null
if (request.headers) {
@@ -15,6 +16,9 @@ const prepareHeaders = (request) => {
let header = headerCount === 1 ? '' : '['
for (const [key, value] of request.headers) {
if (value === null) {
continue
}
switch (key) {
case 'Cookie':
break
@@ -55,7 +59,7 @@ const prepareHeaders = (request) => {
return response
}
const prepareURI = (request) => {
const prepareURI = (request: Request) => {
const uriParams = []
if (request.queryDict) {
uriParams.push(repr(request.urlWithoutQuery))
@@ -66,7 +70,7 @@ const prepareURI = (request) => {
return callFunction('uri', 'URI', uriParams)
}
const prepareAuth = (request) => {
const prepareAuth = (request: Request): string[] => {
const options = []
const optionsParams = []
if (request.auth) {
@@ -89,16 +93,13 @@ const prepareAuth = (request) => {
return options
}
const prepareMultipartUploads = (request) => {
const prepareMultipartUploads = (request: Request): string | null => {
let response = null
if (request.multipartUploads) {
const params = []
const params: [string, string][] = []
for (const [key, value] of request.multipartUploads) {
const pair = []
pair.push(repr(key))
const fileProvider = prepareDataProvider(value, null, '', 1)
pair.push(fileProvider)
params.push(pair)
params.push([repr(key), fileProvider as string]) // TODO: can this be not a string?
}
response = callFunction('body', 'MultipartFormProvider', params)
}
@@ -106,7 +107,7 @@ const prepareMultipartUploads = (request) => {
return response
}
const isJsonString = (str) => {
const isJsonString = (str: string): boolean => {
// Source: https://stackoverflow.com/a/3710226/5625738
try {
JSON.parse(str)
@@ -116,7 +117,7 @@ const isJsonString = (str) => {
return true
}
const prepareDataProvider = (value, output, termination, indentLevel, isDataBinary, isDataRaw) => {
const prepareDataProvider = (value: string, output: string | null, termination: string, indentLevel: number, isDataBinary?: boolean, isDataRaw?: boolean): string | string[] => {
if (typeof indentLevel === 'undefined' || indentLevel === null) indentLevel = 0
if (typeof isDataBinary === 'undefined') isDataBinary = true
if (!isDataRaw && value[0] === '@') {
@@ -155,10 +156,10 @@ const prepareDataProvider = (value, output, termination, indentLevel, isDataBina
return callFunction(output, 'FormProvider', formValue, termination)
}
const prepareData = (request) => {
const prepareData = (request: Request) => {
let response = null
if (request.dataArray) {
const data = request.dataArray.map(x => x.split('=').map(x => {
const data = request.dataArray.map((x: string) => x.split('=').map(x => {
let ans = repr(x)
try {
const jsonData = JSON.parse(x)
@@ -180,8 +181,8 @@ const prepareData = (request) => {
return response
}
const prepareRequestMessage = (request) => {
let reqMessage = [repr(request.method.toLowerCase())]
const prepareRequestMessage = (request: Request): string => {
let reqMessage: string[] | string = [repr(request.method.toLowerCase())]
if (request.cookie || request.headers) {
reqMessage.push('header')
} else if (request.method.toLowerCase() === 'get') {
@@ -189,9 +190,10 @@ const prepareRequestMessage = (request) => {
}
if (containsBody(request)) {
if (reqMessage.length === 1) {
reqMessage.push('[]')
// TODO: this could be a string actually
(reqMessage as string[]).push('[]')
}
reqMessage.push('body')
(reqMessage as string[]).push('body')
}
// list as many params as necessary
@@ -207,7 +209,7 @@ const prepareRequestMessage = (request) => {
return response.join('\n')
}
export const toHTTPInterface = (request) => {
export const toHTTPInterface = (request: Request): (string | string[] | null)[] => {
return [
'%% HTTP Interface',
'import matlab.net.*',

View File

@@ -1,13 +1,14 @@
import * as util from '../../util.js'
import type { Request} from '../../util.js'
import { toWebServices } from './webservices.js'
import { toHTTPInterface } from './httpinterface.js'
export const _toMATLAB = request => {
export const _toMATLAB = (request: Request): string => {
const lines = toWebServices(request).concat('', toHTTPInterface(request))
return lines.flat().filter(line => line !== null).join('\n')
}
export const toMATLAB = curlCommand => {
export const toMATLAB = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toMATLAB(request)
}

View File

@@ -4,16 +4,17 @@ import {
prepareQueryString, prepareCookies,
cookieString, paramsString
} from './common.js'
import type { Request} from '../../util.js'
const isSupportedByWebServices = (request) => {
const isSupportedByWebServices = (request: Request): boolean => {
if (!new Set(['get', 'post', 'put', 'delete', 'patch']).has(request.method.toLowerCase())) {
return false
}
return !request.multipartUploads && !request.insecure
}
const parseWebOptions = (request) => {
const options = {}
const parseWebOptions = (request: Request): {[key: string]: string} => {
const options: {[key: string]: string} = {}
// MATLAB uses GET in `webread` and POST in `webwrite` by default
// thus, it is necessary to set the method for other requests
@@ -21,7 +22,7 @@ const parseWebOptions = (request) => {
options.RequestMethod = request.method.toLowerCase()
}
const headers = {}
const headers: {[key: string]: string} = {}
if (request.auth) {
const [username, password] = request.auth
if (username !== '') {
@@ -34,6 +35,9 @@ const parseWebOptions = (request) => {
if (request.headers) {
for (const [key, value] of request.headers) {
if (value === null) {
continue
}
switch (key) {
case 'User-Agent':
case 'user-agent':
@@ -98,8 +102,8 @@ const parseWebOptions = (request) => {
return options
}
const prepareOptions = (request, options) => {
const lines = []
const prepareOptions = (request: Request, options: {[key: string]: string}): string[] => {
const lines: string[] = []
if (Object.keys(options).length === 0) {
return lines
}
@@ -109,7 +113,7 @@ const prepareOptions = (request, options) => {
return lines
}
const prepareBasicURI = (request) => {
const prepareBasicURI = (request: Request): string[] => {
const response = []
if (request.queryDict) {
response.push(setVariableValue('baseURI', repr(request.urlWithoutQuery)))
@@ -120,8 +124,8 @@ const prepareBasicURI = (request) => {
return response
}
const prepareBasicData = (request) => {
let response = []
const prepareBasicData = (request: Request): string | string[] => {
let response: string | string[] = []
if (Object.prototype.hasOwnProperty.call(request, 'data')) {
if (request.data === '') {
response = setVariableValue('body', repr())
@@ -151,7 +155,7 @@ const prepareBasicData = (request) => {
return response
}
const prepareWebCall = (request, options) => {
const prepareWebCall = (request: Request, options: {[key: string]: string}): string[] => {
const lines = []
const webFunction = containsBody(request) ? 'webwrite' : 'webread'
@@ -167,8 +171,8 @@ const prepareWebCall = (request, options) => {
return lines
}
export const toWebServices = (request) => {
let lines = [
export const toWebServices = (request: Request): (string | string[] | null)[] => {
let lines: (string | string[] | null)[] = [
'%% Web Access using Data Import and Export API'
]

View File

@@ -1,17 +1,22 @@
import * as util from '../../util.js'
import type { Request} from '../../util.js'
import querystring from 'query-string'
import jsesc from 'jsesc'
const quote = str => jsesc(str, { quotes: 'single' })
// TODO: only string
const quote = (str: string | null | (string | null)[]): string => jsesc(str, { quotes: 'single' })
export const _toPhpRequests = request => {
let headerString = false
export const _toPhpRequests = (request: Request): string => {
let headerString: string
if (request.headers) {
headerString = '$headers = array(\n'
let i = 0
const headerCount = request.headers ? request.headers.length : 0
for (const [headerName, headerValue] of request.headers) {
if (headerValue === null) {
continue // TODO: this could miss not adding a trailing comma
}
headerString += " '" + headerName + "' => '" + quote(headerValue) + "'"
if (i < headerCount - 1) {
headerString += ',\n'
@@ -23,13 +28,13 @@ export const _toPhpRequests = request => {
headerString = '$headers = array();'
}
let optionsString = false
let optionsString
if (request.auth) {
const [user, password] = request.auth
optionsString = "$options = array('auth' => array('" + user + "', '" + password + "'));"
}
let dataString = false
let dataString
if (request.data) {
const parsedQueryString = querystring.parse(request.data, { sort: false })
dataString = '$data = array(\n'
@@ -74,7 +79,7 @@ export const _toPhpRequests = request => {
return phpCode + '\n'
}
export const toPhpRequests = curlCommand => {
export const toPhpRequests = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toPhpRequests(request)
}

View File

@@ -1,9 +1,11 @@
import * as util from '../../util.js'
import type { Request} from '../../util.js'
import jsesc from 'jsesc'
const quote = str => jsesc(str, { quotes: 'single' })
const quote = (str: string): string => jsesc(str, { quotes: 'single' })
export const _toPhp = request => {
export const _toPhp = (request: Request): string => {
let cookieString
if (util.hasHeader(request, 'cookie')) {
cookieString = util.getHeader(request, 'cookie')
@@ -30,6 +32,9 @@ export const _toPhp = request => {
}
for (const [headerName, headerValue] of (request.headers || [])) {
if (headerValue === null) {
continue
}
headersArrayCode += " '" + quote(headerName) + "' => '" + quote(headerValue) + "',\n"
}
@@ -93,7 +98,7 @@ export const _toPhp = request => {
return phpCode
}
export const toPhp = curlCommand => {
export const toPhp = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toPhp(request)
}

View File

@@ -1,8 +1,9 @@
import * as util from '../util.js'
import jsesc from 'jsesc'
import type { Request, Query, QueryDict } from '../util.js'
function reprWithVariable (value, hasEnvironmentVariable) {
function reprWithVariable (value: string, hasEnvironmentVariable: boolean) {
if (!value) {
return "''"
}
@@ -14,12 +15,12 @@ function reprWithVariable (value, hasEnvironmentVariable) {
return 'f"' + jsesc(value, { quotes: 'double' }) + '"'
}
function repr (value) {
function repr (value: string): string {
// In context of url parameters, don't accept nulls and such.
return reprWithVariable(value, false)
}
function objToPython (obj, indent = 0) {
function objToPython (obj: any, indent = 0): string {
let s = ''
switch (typeof obj) {
case 'string':
@@ -64,7 +65,7 @@ function objToPython (obj, indent = 0) {
return s
}
function objToDictOrListOfTuples (obj) {
function objToDictOrListOfTuples (obj: Query | QueryDict): string {
if (!Array.isArray(obj)) {
return objToPython(obj)
}
@@ -79,7 +80,7 @@ function objToDictOrListOfTuples (obj) {
return s
}
function getDataString (request) {
function getDataString (request: Request) {
if (!request.isDataRaw && request.data.startsWith('@')) {
let filePath = request.data.slice(1)
if (filePath === '-') {
@@ -106,8 +107,8 @@ function getDataString (request) {
const dataString = 'data = ' + repr(request.data) + '\n'
const isJson = util.hasHeader(request, 'content-type') &&
util.getHeader(request, 'content-type').split(';')[0].trim() === 'application/json'
const contentTypeHeader = util.getHeader(request, 'content-type')
const isJson = contentTypeHeader && contentTypeHeader.split(';')[0].trim() === 'application/json'
if (isJson) {
try {
const dataAsJson = JSON.parse(request.data)
@@ -136,7 +137,10 @@ function getDataString (request) {
return [dataString, null, null]
}
function getFilesString (request) {
function getFilesString (request: Request): string | undefined {
if (!request.multipartUploads) {
return undefined
}
const multipartUploads = request.multipartUploads.map(m => {
let multipartValue
if (m[1].startsWith('@')) {
@@ -170,7 +174,7 @@ function getFilesString (request) {
// convertVarToStringFormat will convert if inputString to f"..." format
// if inputString has possible variable as its substring
function detectEnvVar (inputString) {
function detectEnvVar (inputString: string): [Set<string>, string] {
// Using state machine to detect environment variable
// Each character is an edge, state machine:
// IN_ENV_VAR: means that currently we are iterating inside a possible environment variable
@@ -184,15 +188,15 @@ function detectEnvVar (inputString) {
const IN_STRING = 1
// We only care for the unique element
const detectedVariables = new Set()
const detectedVariables: Set<string> = new Set()
let currState = IN_STRING
let envVarStartIndex = -1
const whiteSpaceSet = new Set(' \n\t')
const modifiedString = []
for (const idx in inputString) {
const currIdx = +idx
for (let idx = 0; idx < inputString.length; idx++) {
const currIdx = idx
const currChar = inputString[currIdx]
if (currState === IN_ENV_VAR && whiteSpaceSet.has(currChar)) {
const newVariable = inputString.substring(envVarStartIndex, currIdx)
@@ -239,11 +243,11 @@ function detectEnvVar (inputString) {
return [detectedVariables, modifiedString.join('')]
}
export const _toPython = request => {
export const _toPython = (request: Request): string => {
// Currently, only assuming that the env-var only used in
// the value part of cookies, params, or body
const osVariables = new Set()
const commentedOutHeaders = {
const commentedOutHeaders: {[key: string]: string} = {
'accept-encoding': '',
'content-length': ''
}
@@ -317,6 +321,7 @@ export const _toPython = request => {
queryStr = 'params = ' + objToDictOrListOfTuples(request.queryDict || request.query) + '\n'
}
const contentType = util.getHeader(request, 'content-type')
let dataString
let jsonDataString
let jsonDataStringRoundtrips
@@ -325,7 +330,7 @@ export const _toPython = request => {
[dataString, jsonDataString, jsonDataStringRoundtrips] = getDataString(request)
// Remove "Content-Type" from the headers dict
// because Requests adds it automatically when you use json=
if (jsonDataString && util.getHeader(request, 'content-type').trim() === 'application/json') {
if (jsonDataString && contentType && contentType.trim() === 'application/json') {
commentedOutHeaders['content-type'] = 'Already added when you pass json='
if (!jsonDataStringRoundtrips) {
commentedOutHeaders['content-type'] += ' but not when you pass data='
@@ -338,9 +343,9 @@ export const _toPython = request => {
// wheras curl does, so the request will fail.
// https://github.com/curlconverter/curlconverter/issues/248
if (filesString &&
util.hasHeader(request, 'content-type') &&
util.getHeader(request, 'content-type').trim() === 'multipart/form-data' &&
!util.getHeader(request, 'content-type').includes('boundary=')) {
contentType &&
contentType.trim() === 'multipart/form-data' &&
!contentType.includes('boundary=')) {
// TODO: better wording
commentedOutHeaders['content-type'] = "requests won't add a boundary if this header is set when you pass files="
}
@@ -351,6 +356,9 @@ export const _toPython = request => {
// TODO: what if there are repeat headers
headerDict = 'headers = {\n'
for (const [headerName, headerValue] of request.headers) {
if (headerValue === null) {
continue
}
const [detectedVars, modifiedString] = detectEnvVar(headerValue)
const hasVariable = detectedVars.size > 0
@@ -502,7 +510,7 @@ export const _toPython = request => {
return pythonCode + '\n'
}
export const toPython = curlCommand => {
export const toPython = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toPython(request)
}

View File

@@ -1,11 +1,12 @@
// Author: Bob Rudis (bob@rud.is)
import * as util from '../util.js'
import type { Request, Cookie, QueryDict } from '../util.js'
import jsesc from 'jsesc'
import querystring from 'query-string'
function reprn (value) { // back-tick quote names
function reprn (value: string | null): string { // back-tick quote names
if (!value) {
return '``'
} else {
@@ -13,7 +14,7 @@ function reprn (value) { // back-tick quote names
}
}
function repr (value) {
function repr (value: string): string {
// In context of url parameters, don't accept nulls and such.
if (!value) {
return "''"
@@ -22,15 +23,19 @@ function repr (value) {
}
}
function getQueryDict (request) {
function getQueryDict (request: Request): string | undefined {
if (request.queryDict === undefined) {
return undefined
}
let queryDict = 'params = list(\n'
queryDict += Object.keys(request.queryDict).map((paramName) => {
const rawValue = request.queryDict[paramName]
const rawValue = (request.queryDict as QueryDict)[paramName]
let paramValue
if (Array.isArray(rawValue)) {
paramValue = 'c(' + rawValue.map(repr).join(', ') + ')'
paramValue = 'c(' + (rawValue as string[]).map(repr).join(', ') + ')'
} else {
paramValue = repr(rawValue)
paramValue = repr(rawValue as string)
}
return (' ' + reprn(paramName) + ' = ' + paramValue)
}).join(',\n')
@@ -38,7 +43,7 @@ function getQueryDict (request) {
return queryDict
}
function getDataString (request) {
function getDataString (request: Request) {
if (!request.isDataRaw && request.data.startsWith('@')) {
const filePath = request.data.slice(1)
return 'data = upload_file(\'' + filePath + '\')'
@@ -55,7 +60,7 @@ function getDataString (request) {
}
}
function getMultipleDataString (request, parsedQueryString) {
function getMultipleDataString (request: Request, parsedQueryString: querystring.ParsedQuery<string> ) {
let repeatedKey = false
for (const key in parsedQueryString) {
const value = parsedQueryString[key]
@@ -72,10 +77,11 @@ function getMultipleDataString (request, parsedQueryString) {
const value = parsedQueryString[key]
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) {
els.push(' ' + reprn(key) + ' = ' + repr(value[i]))
const val = value[i]
els.push(' ' + reprn(key) + ' = ' + repr(val === null ? '' : val))
}
} else {
els.push(' ' + reprn(key) + ' = ' + repr(value))
els.push(' ' + reprn(key) + ' = ' + repr(value === null ? '' : value))
}
}
dataString += els.join(',\n')
@@ -84,7 +90,7 @@ function getMultipleDataString (request, parsedQueryString) {
dataString = 'data = list(\n'
dataString += Object.keys(parsedQueryString).map((key) => {
const value = parsedQueryString[key]
return (' ' + reprn(key) + ' = ' + repr(value))
return (' ' + reprn(key) + ' = ' + repr(value === null ? '' : value as string))
}).join(',\n')
dataString += '\n)\n'
}
@@ -92,7 +98,10 @@ function getMultipleDataString (request, parsedQueryString) {
return dataString
}
function getFilesString (request) {
function getFilesString (request: Request): string | undefined {
if (!request.multipartUploads) {
return undefined
}
// http://docs.rstats-requests.org/en/master/user/quickstart/#post-a-multipart-encoded-file
let filesString = 'files = list(\n'
filesString += request.multipartUploads.map((m) => {
@@ -112,11 +121,11 @@ function getFilesString (request) {
return filesString
}
export const _toR = request => {
export const _toR = (request: Request) => {
let cookieDict
if (request.cookies) {
cookieDict = 'cookies = c(\n'
cookieDict += request.cookies.map(c => ' ' + repr(c[0]) + ' = ' + repr(c[1])).join(',\n')
cookieDict += request.cookies.map((c: Cookie) => ' ' + repr(c[0]) + ' = ' + repr(c[1])).join(',\n')
// TODO: isn't this an extra \n?
cookieDict += '\n)\n'
util.deleteHeader(request, 'Cookie')
@@ -126,16 +135,15 @@ export const _toR = request => {
const hels = []
headerDict = 'headers = c(\n'
for (const [headerName, headerValue] of request.headers) {
hels.push(' ' + reprn(headerName) + ' = ' + repr(headerValue))
if (headerValue !== null) {
hels.push(' ' + reprn(headerName) + ' = ' + repr(headerValue))
}
}
headerDict += hels.join(',\n')
headerDict += '\n)\n'
}
let queryDict
if (request.queryDict) {
queryDict = getQueryDict(request)
}
const queryDict = getQueryDict(request)
let dataString
let filesString
@@ -189,7 +197,7 @@ export const _toR = request => {
if (headerDict) {
rstatsCode += headerDict + '\n'
}
if (queryDict) {
if (queryDict !== undefined) {
rstatsCode += queryDict + '\n'
}
if (dataString) {
@@ -201,7 +209,7 @@ export const _toR = request => {
return rstatsCode + '\n'
}
export const toR = curlCommand => {
export const toR = (curlCommand: string | string[]): string => {
const request = util.parseCurlCommand(curlCommand)
return _toR(request)
}

View File

@@ -1,12 +1,13 @@
import * as util from '../util.js'
import type { Request } from '../util.js'
import jsesc from 'jsesc'
const INDENTATION = ' '.repeat(4)
const indent = (line, level = 1) => INDENTATION.repeat(level) + line
const quote = str => jsesc(str, { quotes: 'double' })
const indent = (line:string, level = 1): string => INDENTATION.repeat(level) + line
const quote = (str: string): string => jsesc(str, { quotes: 'double' })
export const _toRust = request => {
export const _toRust = (request: Request) => {
const lines = ['extern crate reqwest;']
{
// Generate imports.
@@ -25,13 +26,15 @@ export const _toRust = request => {
if (request.headers) {
lines.push(indent('let mut headers = header::HeaderMap::new();'))
const headerEnum = {
const headerEnum: { [key: string]: string } = {
cookie: 'header::COOKIE'
}
for (const [headerName, headerValue] of (request.headers || [])) {
const enumValue = headerEnum[headerName.toLowerCase()]
const name = enumValue || `"${headerName}"`
lines.push(indent(`headers.insert(${name}, "${quote(headerValue)}".parse().unwrap());`))
if (headerValue !== null) {
lines.push(indent(`headers.insert(${name}, "${quote(headerValue)}".parse().unwrap());`))
}
}
lines.push('')
}
@@ -95,7 +98,7 @@ export const _toRust = request => {
return lines.join('\n') + '\n'
}
export const toRust = curlCommand => {
export const toRust = (curlCommand: string | string[]) => {
const request = util.parseCurlCommand(curlCommand)
return _toRust(request)
}

View File

@@ -1,10 +1,11 @@
import * as util from '../util.js'
import type { Request } from '../util.js'
import yaml from 'yamljs'
import jsesc from 'jsesc'
import querystring from 'query-string'
function getDataString (request) {
function getDataString (request: Request) {
let mimeType = 'application/json'
if (request.data.indexOf("'") > -1) {
request.data = jsesc(request.data)
@@ -24,7 +25,7 @@ function getDataString (request) {
}
for (const [paramName, paramValue] of (request.headers || [])) {
if (paramName === 'Content-Type') {
if (paramName.toLowerCase() === 'content-type' && paramValue !== null) {
mimeType = paramValue
}
}
@@ -34,13 +35,8 @@ function getDataString (request) {
}
}
function getQueryList (request) {
// Convert nulls to empty string
return request.query.map(p => ({ name: p[0], value: p[1] || '' }))
}
export const _toStrest = request => {
const response = { version: 2 }
export const _toStrest = (request: Request) => {
const response: {[key: string]: any} = { version: 2 }
if (request.insecure) {
response.allowInsecure = true
}
@@ -70,7 +66,7 @@ export const _toStrest = request => {
}
if (request.auth) {
const [username, password] = request.auth
const basic = {}
const basic: {[key: string]: string} = {}
if (username) {
basic.username = username
}
@@ -80,14 +76,15 @@ export const _toStrest = request => {
let queryList
if (request.query) {
queryList = getQueryList(request)
// Convert nulls to empty string
queryList = request.query.map((p) => ({ name: p[0], value: p[1] || '' }))
response.requests.curl_converter.request.queryString = queryList
}
const yamlString = yaml.stringify(response, 100, 2)
return yamlString
}
export const toStrest = curlCommand => {
export const toStrest = (curlCommand: string | string[]) => {
const request = util.parseCurlCommand(curlCommand)
return _toStrest(request)
}

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More