diff --git a/.github/workflows/node.js.yml b/.github/workflows/ci.yml similarity index 100% rename from .github/workflows/node.js.yml rename to .github/workflows/ci.yml diff --git a/package.json b/package.json index 3a1e658..80d12ab 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "scripts": { "compile": "tsc", "watch": "tsc -watch -p ./", - "pretest": "npm run compile", + "pretest": "TSC_COMPILE_ON_ERROR=true npm run compile || exit 0", "test": "NODE_OPTIONS=--enable-source-maps npx tape dist/test/test.js", "prelint": "npm run compile", "lint": "eslint src --ext ts", diff --git a/src/generators/ansible.ts b/src/generators/ansible.ts index 243fd43..05e4778 100644 --- a/src/generators/ansible.ts +++ b/src/generators/ansible.ts @@ -5,7 +5,10 @@ import { ansibleTemplate } from "../templates/ansible.js"; import nunjucks from "nunjucks"; import querystring from "query-string"; -function getDataString(request: Request): any { +function getDataString(request: Request): string | object { + if (!request.data) { + return ""; + } const parsedQueryString = querystring.parse(request.data, { sort: false }); const keyCount = Object.keys(parsedQueryString).length; const singleKeyOnly = diff --git a/src/generators/dart.ts b/src/generators/dart.ts index 34b95b6..401b3cb 100644 --- a/src/generators/dart.ts +++ b/src/generators/dart.ts @@ -75,7 +75,7 @@ export const _toDart = (r: Request): string => { } const hasData = r.data; - if (hasData) { + if (r.data) { // escape single quotes if there're not already escaped if (r.data.indexOf("'") !== -1 && r.data.indexOf("\\'") === -1) r.data = jsesc(r.data); diff --git a/src/generators/elixir.ts b/src/generators/elixir.ts index 400b482..21354c8 100644 --- a/src/generators/elixir.ts +++ b/src/generators/elixir.ts @@ -96,7 +96,7 @@ function getBody(request: Request): string { } function getFormDataString(request: Request): string { - if (request.data && typeof request.data === "string") { + if (request.data) { return getDataString(request); } @@ -137,6 +137,10 @@ ${content} } function getDataString(request: Request): string { + if (!request.data) { + return ""; + } + if (!request.isDataRaw && request.data.startsWith("@")) { const filePath = request.data.slice(1); if (request.isDataBinary) { diff --git a/src/generators/javascript/javascript.ts b/src/generators/javascript/javascript.ts index b233fdb..d982de9 100644 --- a/src/generators/javascript/javascript.ts +++ b/src/generators/javascript/javascript.ts @@ -38,7 +38,7 @@ export const _toJavaScript = (request: Request): string => { request.method.toUpperCase() !== "GET" || request.headers || request.auth || - request.body + request.data ) { jsFetchCode += ", {\n"; diff --git a/src/generators/json.ts b/src/generators/json.ts index 71c708a..ee33c91 100644 --- a/src/generators/json.ts +++ b/src/generators/json.ts @@ -20,7 +20,12 @@ type JSONOutput = { auth?: { user: string; password: string }; }; -function getDataString(request: Request) { +function getDataString(request: Request): { + data?: { [key: string]: string | string[] }; +} { + if (!request.data) { + return {}; + } /* if ( !request.isDataRaw && request.data.startsWith('@') ) { var filePath = request.data.slice(1); diff --git a/src/generators/matlab/common.ts b/src/generators/matlab/common.ts index 55fa69b..777819d 100644 --- a/src/generators/matlab/common.ts +++ b/src/generators/matlab/common.ts @@ -115,7 +115,10 @@ const addCellArray = ( return response; }; -const structify = (obj: any, indentLevel?: number) => { +const structify = ( + obj: number[] | string[] | { [key: string]: string } | string | number | null, + indentLevel?: number +) => { let response = ""; indentLevel = !indentLevel ? 1 : ++indentLevel; const indent = " ".repeat(4 * indentLevel); diff --git a/src/generators/matlab/httpinterface.ts b/src/generators/matlab/httpinterface.ts index 0a17e10..5f5ddb4 100644 --- a/src/generators/matlab/httpinterface.ts +++ b/src/generators/matlab/httpinterface.ts @@ -205,7 +205,7 @@ const prepareData = (request: Request) => { response = callFunction("body", "FormProvider", data); } else if (Object.prototype.hasOwnProperty.call(request, "data")) { response = prepareDataProvider( - request.data, + request.data as string, "body", ";", 0, @@ -221,7 +221,7 @@ const prepareData = (request: Request) => { const prepareRequestMessage = (request: Request): string => { let reqMessage: string[] | string = [repr(request.method.toLowerCase())]; - if (request.cookie || request.headers) { + if (request.cookies || request.headers) { reqMessage.push("header"); } else if (request.method.toLowerCase() === "get") { reqMessage = ""; diff --git a/src/generators/matlab/webservices.ts b/src/generators/matlab/webservices.ts index a8e1fb8..e62c3e7 100644 --- a/src/generators/matlab/webservices.ts +++ b/src/generators/matlab/webservices.ts @@ -152,9 +152,13 @@ const prepareBasicData = (request: Request): string | string[] => { if (Object.prototype.hasOwnProperty.call(request, "data")) { if (request.data === "") { response = setVariableValue("body", repr()); - } else if (request.data[0] === "@") { + } else if ((request.data as string)[0] === "@") { response.push( - callFunction("body", "fileread", repr(request.data.slice(1))) + callFunction( + "body", + "fileread", + repr((request.data as string).slice(1)) + ) ); if (!request.isDataBinary) { @@ -164,7 +168,7 @@ const prepareBasicData = (request: Request): string | string[] => { // if the data is in JSON, store it as struct in MATLAB // otherwise just keep it as a char vector try { - const jsonData = JSON.parse(request.data); + const jsonData = JSON.parse(request.data as string); if (typeof jsonData === "object") { let jsonText = structify(jsonData); if (!jsonText.startsWith("struct")) jsonText = repr(jsonText); diff --git a/src/generators/php/php.ts b/src/generators/php/php.ts index b5b037c..17860f5 100644 --- a/src/generators/php/php.ts +++ b/src/generators/php/php.ts @@ -81,11 +81,16 @@ export const _toPhp = (request: Request): string => { } } requestDataCode += "]"; - } else if (request.isDataBinary && request.data.charAt(0) === "@") { + } else if ( + request.isDataBinary && + (request.data as string).charAt(0) === "@" + ) { requestDataCode = - "file_get_contents('" + quote(request.data.substring(1)) + "')"; + "file_get_contents('" + + quote((request.data as string).substring(1)) + + "')"; } else { - requestDataCode = "'" + quote(request.data) + "'"; + requestDataCode = "'" + quote(request.data as string) + "'"; } phpCode += "curl_setopt($ch, CURLOPT_POSTFIELDS, " + requestDataCode + ");\n"; diff --git a/src/generators/python.ts b/src/generators/python.ts index ac1bb48..ee27d99 100644 --- a/src/generators/python.ts +++ b/src/generators/python.ts @@ -20,7 +20,10 @@ function repr(value: string): string { return reprWithVariable(value, false); } -function objToPython(obj: any, indent = 0): string { +function objToPython( + obj: string | number | boolean | object | null, + indent = 0 +): string { let s = ""; switch (typeof obj) { case "string": @@ -87,7 +90,12 @@ function objToDictOrListOfTuples(obj: Query | QueryDict): string { return s; } -function getDataString(request: Request) { +function getDataString( + request: Request +): [string | null, string | null, boolean | null] { + if (!request.data) { + return [null, null, null]; + } if (!request.isDataRaw && request.data.startsWith("@")) { let filePath = request.data.slice(1); if (filePath === "-") { @@ -505,7 +513,8 @@ export const _toPython = (request: Request): string => { if (request.insecure) { requestLineBody += ", verify=False"; } else if (request.cacert || request.capath) { - requestLineBody += ", verify=" + repr(request.cacert || request.capath); + requestLineBody += + ", verify=" + repr((request.cacert || request.capath) as string); } if (request.auth) { diff --git a/src/generators/r.ts b/src/generators/r.ts index 3e1c4aa..9949cd9 100644 --- a/src/generators/r.ts +++ b/src/generators/r.ts @@ -46,7 +46,10 @@ function getQueryDict(request: Request): string | undefined { return queryDict; } -function getDataString(request: Request) { +function getDataString(request: Request): string { + if (!request.data) { + return ""; + } if (!request.isDataRaw && request.data.startsWith("@")) { const filePath = request.data.slice(1); return "data = upload_file('" + filePath + "')"; @@ -167,7 +170,7 @@ export const _toR = (request: Request) => { let dataString; let filesString; - if (request.data && typeof request.data === "string") { + if (request.data) { dataString = getDataString(request); } else if (request.multipartUploads) { filesString = getFilesString(request); diff --git a/src/generators/strest.ts b/src/generators/strest.ts index 145609b..9764361 100644 --- a/src/generators/strest.ts +++ b/src/generators/strest.ts @@ -5,7 +5,10 @@ import yaml from "yamljs"; import jsesc from "jsesc"; import querystring from "query-string"; -function getDataString(request: Request) { +function getDataString(request: Request): PostData | null { + if (!request.data) { + return null; + } let mimeType = "application/json"; if (request.data.indexOf("'") > -1) { request.data = jsesc(request.data); @@ -36,8 +39,40 @@ function getDataString(request: Request) { }; } -export const _toStrest = (request: Request) => { - const response: { [key: string]: any } = { version: 2 }; +type PostData = { + mimeType: string; + text: object | string; +}; + +type BasicAuth = { + username?: string; + password: string; +}; + +type StrestOutput = { + version: number; + allowInsecure?: boolean; + requests?: { + curl_converter: { + request: { + url: string; + method: string; + postData?: PostData | null; + headers?: { + name: string; + value: string | null; + }[]; + queryString?: { name: string; value: string }[]; + }; + auth?: { + basic: BasicAuth; + }; + }; + }; +}; + +export const _toStrest = (request: Request): string => { + const response: StrestOutput = { version: 2 }; if (request.insecure) { response.allowInsecure = true; } @@ -52,7 +87,7 @@ export const _toStrest = (request: Request) => { }, }, }; - if (request.data && typeof request.data === "string") { + if (request.data) { response.requests.curl_converter.request.postData = getDataString(request); } @@ -67,12 +102,12 @@ export const _toStrest = (request: Request) => { } if (request.auth) { const [username, password] = request.auth; - const basic: { [key: string]: string } = {}; + const basic: { username?: string; password?: string } = {}; if (username) { basic.username = username; } basic.password = password; - response.requests.curl_converter.auth = { basic }; + response.requests.curl_converter.auth = { basic: basic as BasicAuth }; } let queryList; @@ -85,7 +120,7 @@ export const _toStrest = (request: Request) => { const yamlString = yaml.stringify(response, 100, 2); return yamlString; }; -export const toStrest = (curlCommand: string | string[]) => { +export const toStrest = (curlCommand: string | string[]): string => { const request = util.parseCurlCommand(curlCommand); return _toStrest(request); }; diff --git a/src/util.ts b/src/util.ts index 73a9d79..dd25728 100644 --- a/src/util.ts +++ b/src/util.ts @@ -86,14 +86,33 @@ interface ParsedArguments { interface Request { url: string; + urlWithoutQuery: string; query?: Query; queryDict?: QueryDict; + method: string; headers?: Headers; stdin?: string; input?: string; multipartUploads?: [string, string][]; auth?: [string, string]; - [key: string]: any; + cookies?: Cookies; + compressed?: boolean; + isDataBinary?: boolean; + isDataRaw?: boolean; + digest?: boolean; + dataArray?: string[]; + data?: string; + insecure?: boolean; + cert?: string | [string, string]; + cacert?: string; + capath?: string; + proxy?: string; + proxyAuth?: string; + timeout?: string; + followRedirects?: boolean; + output?: string; + http2?: boolean; + http3?: boolean; } // BEGIN GENERATED CURL OPTIONS @@ -1365,20 +1384,25 @@ function buildRequest(parsedArguments: ParsedArguments): Request { urlObject.query = urlObject.query.slice(0, -1); } const [queryAsList, queryAsDict] = parseQueryString(urlObject.query); + const useParsedQuery = + queryAsList && + queryAsList.length && + queryAsList.every((p) => p[1] !== null); // Most software libraries don't let you distinguish between a=&b= and a&b, // so if we get an `a&b`-type query string, don't bother. - const request: Request = { url }; - if (!queryAsList || queryAsList.some((p) => p[1] === null)) { - request.urlWithoutQuery = url; // TODO: rename? - } else { + let urlWithoutQuery; + if (useParsedQuery) { urlObject.search = null; // Clean out the search/query portion. - request.urlWithoutQuery = URL.format(urlObject); + urlWithoutQuery = URL.format(urlObject); + } else { + urlWithoutQuery = url; // TODO: rename? + } - if (queryAsList.length > 0) { - request.query = queryAsList; - if (queryAsDict) { - request.queryDict = queryAsDict; - } + const request: Request = { url, method, urlWithoutQuery }; + if (useParsedQuery) { + request.query = queryAsList; + if (queryAsDict) { + request.queryDict = queryAsDict; } } @@ -1392,8 +1416,6 @@ function buildRequest(parsedArguments: ParsedArguments): Request { request.compressed = true; } - request.method = method; - // TODO: all of these could be specified in the same command. // They also need to maintain order. // TODO: do all of these allow @file?