From 2e970e6a4e23df7d2ff12a012b7c8aeaa1d9b72e Mon Sep 17 00:00:00 2001 From: midnqp Date: Thu, 9 Mar 2023 19:44:03 +0600 Subject: [PATCH] feat: cmd-update #8 --- package.json | 123 +++++++++++++++++++++------------------ src/handlers/update.ts | 101 +++++++++++++++++++++++++++++--- src/services/cmdopts.ts | 5 +- src/services/common.ts | 25 ++++++-- src/services/example.ts | 22 ++++++- src/services/response.ts | 4 +- 6 files changed, 201 insertions(+), 79 deletions(-) diff --git a/package.json b/package.json index be6482f..5ef5e0d 100644 --- a/package.json +++ b/package.json @@ -1,60 +1,67 @@ { - "name": "pcli", - "type": "module", - "scripts": { - "test:backend": "npx ts-node-esm test/server.ts", - "build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", - "build:bin": "npm run build && npm run prepack:esbuild:cjs && npm run prepack:pkg", - "build:bin:all": "npm run build && npm run prepack:esbuild:cjs && npm run prepack:pkg:all", - "dev": "tsc-watch --noClear -p tsconfig.json", - "prepack:webpack": "npx webpack --config webpack.config.cjs", - "prepack:pkg:all": "npx pkg --options 'no-warnings' -t node18-linux-x64,node18-macos-x64,node18-win-x64 ./dist/bundle.cjs -o ./bin/pcli", - "prepack:pkg": "npx pkg --options 'no-warnings' -t node18 ./dist/bundle.cjs -o ./bin/pcli", - "prepack:esbuild:cjs": "npx esbuild dist/src/index.js --bundle --outfile=dist/bundle.cjs --format=cjs --platform=node", - "prepack:esbuild:esm": "npx esbuild dist/src/index.js --bundle --outfile=dist/bundle.mjs --format=esm --platform=node --banner:js=\"import {createRequire} from 'module'; const require = createRequire(import.meta.url); import { dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url));\"" - }, - "dependencies": { - "axios": "^0.27.2", - "browserify": "^17.0.0", - "chai": "^4.3.7", - "chalk": "^5.2.0", - "clean-stack": "^5.1.0", - "commander": "^9.4.0", - "content-type": "^1.0.4", - "dotenv": "^16.0.1", - "enquirer": "^2.3.6", - "fs-extra": "^11.1.0", - "lodash": "^4.17.21", - "newman": "^5.3.2", - "postman-collection": "^4.1.5", - "pretty-bytes": "^6.1.0", - "terser": "^5.16.5", - "uuid": "^9.0.0", - "winston": "^3.8.2" - }, - "devDependencies": { - "@babel/cli": "^7.18.10", - "@babel/core": "^7.18.10", - "@inquirer/editor": "^0.0.21-alpha.0", - "@types/chai": "^4.3.4", - "@types/content-type": "^1.1.5", - "@types/express": "^4.17.17", - "@types/fs-extra": "^9.0.13", - "@types/lodash": "^4.14.182", - "@types/newman": "^5.3.1", - "@types/postman-collection": "^3.5.7", - "@types/uuid": "^9.0.0", - "@typescript-eslint/eslint-plugin": "^5.51.0", - "@typescript-eslint/parser": "^5.51.0", - "eslint": "^8.33.0", - "express": "^4.18.2", - "pkg": "^5.8.0", - "ts-node": "^10.9.1", - "tsc-alias": "^1.8.2", - "tsc-watch": "^5.0.3", - "typescript": "^4.7.4" - }, - "bundleDependencies": [ - "terser" - ] + "name": "pcli", + "type": "module", + "main": "./src/index.js", + "scripts": { + "test:backend": "npx ts-node-esm test/server.ts", + "build": "tsc -p tsconfig.json && tsc-alias -p tsconfig.json", + "build:bin": "npm run build && npm run prepack:esbuild:cjs && npm run prepack:pkg", + "build:bin:all": "npm run build && npm run prepack:esbuild:cjs && npm run prepack:pkg:all", + "dev": "tsc-watch --noClear -p tsconfig.json", + "prepack:webpack": "npx webpack --config webpack.config.cjs", + "prepack:pkg:all": "npx pkg --options 'no-warnings' -t node18-linux-x64,node18-macos-x64,node18-win-x64 ./dist/bundle.cjs -o ./bin/pcli", + "prepack:pkg": "npx pkg --options 'no-warnings' -t node18 ./dist/bundle.cjs -o ./bin/pcli", + "prepack:esbuild:cjs": "npx esbuild dist/src/index.js --bundle --outfile=dist/bundle.cjs --format=cjs --platform=node", + "prepack:esbuild:esm": "npx esbuild dist/src/index.js --bundle --outfile=dist/bundle.mjs --format=esm --platform=node --banner:js=\"import {createRequire} from 'module'; const require = createRequire(import.meta.url); import { dirname } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url));\"" + }, + "dependencies": { + "axios": "^0.27.2", + "browserify": "^17.0.0", + "chai": "^4.3.7", + "chalk": "^5.2.0", + "clean-stack": "^5.1.0", + "commander": "^9.4.0", + "content-type": "^1.0.4", + "dotenv": "^16.0.1", + "enquirer": "^2.3.6", + "fs-extra": "^11.1.0", + "js-object-pretty-print": "^0.3.0", + "lodash": "^4.17.21", + "newman": "^5.3.2", + "postman-collection": "^4.1.5", + "pretty-bytes": "^6.1.0", + "terser": "^5.16.5", + "tmp": "^0.2.1", + "uuid": "^9.0.0", + "winston": "^3.8.2" + }, + "devDependencies": { + "@babel/cli": "^7.18.10", + "@babel/core": "^7.18.10", + "@inquirer/editor": "^0.0.21-alpha.0", + "@types/chai": "^4.3.4", + "@types/content-type": "^1.1.5", + "@types/express": "^4.17.17", + "@types/fs-extra": "^9.0.13", + "@types/lodash": "^4.14.182", + "@types/newman": "^5.3.1", + "@types/postman-collection": "^3.5.7", + "@types/tmp": "^0.2.3", + "@types/uuid": "^9.0.0", + "@typescript-eslint/eslint-plugin": "^5.54.1", + "@typescript-eslint/parser": "^5.54.1", + "esbuild": "^0.17.11", + "eslint": "^8.33.0", + "express": "^4.18.2", + "open": "^8.4.2", + "open-editor": "^4.0.0", + "pkg": "^5.8.0", + "ts-node": "^10.9.1", + "tsc-alias": "^1.8.2", + "tsc-watch": "^5.0.3", + "typescript": "^4.7.4" + }, + "bundleDependencies": [ + "terser" + ] } diff --git a/src/handlers/update.ts b/src/handlers/update.ts index 7cbe2ab..77aaf44 100644 --- a/src/handlers/update.ts +++ b/src/handlers/update.ts @@ -2,8 +2,23 @@ import services from '@src/services/index.js' import psdk from 'postman-collection' import { PostmanCli } from '@src/types' import { Command } from 'commander' -import editor from '@inquirer/editor' import util from 'node:util' +import _ from 'lodash' +import Enquirer from 'enquirer' +import openeditor from 'open-editor' +import editor from '@inquirer/editor' +import open from 'open' +import tmp from 'tmp' +import fs from 'node:fs' + +function openJson(input: string) { + const { name } = tmp.fileSync({ postfix: '.js' }) + fs.writeFileSync(name, input) + + //openeditor([{file: name}]) + open(name) + return name +} export default async function ( args: PostmanCli.Cmd.VariadicResources, @@ -15,20 +30,90 @@ export default async function ( const resource = services.resource.getFromNested(co, args) if (services.response.isResponse(resource)) { - const p = services.example.toPrintable(resource) + const p = services.example.toPrintable(resource, { + addParsedBody: true, + }) + const str = util.inspect(p, { colors: false, maxArrayLength: null, maxStringLength: null, depth: 50, }) - const prompt: any = await editor({ default: str, message: '' }) + //const prompt = await editor({default: str, message: ''}) + const filename = openJson(str) + let done: boolean + try { + const enq = (await Enquirer.prompt({ + name: 'done', + required: true, + initial: true, + message: 'press any key to continue', + type: 'confirm', + })) as any + done = enq.done + } catch (err) { + done = false + } - new psdk.Response({ - code: prompt.response.code, - responseTime: prompt.response.time, - body: prompt.response.body, - header: [{}], + if (!done) return + + type ParsedPrompt = { + request: PostmanCli.RequestPrintable + response: PostmanCli.ResponsePrintable + } + let prompt: ParsedPrompt + try { + prompt = eval( + '(' + (await fs.promises.readFile(filename, 'utf8')) + ')' + ) + } catch (e) { + throw Error('could not parse') + } + const parsed = prompt + + let request: psdk.RequestDefinition | undefined + if (parsed.request && !_.isEmpty(parsed.request)) { + const rawBody = JSON.stringify(parsed.request.body) + request = { + url: { + path: parsed.request.url.path, + query: services.common.jsonToHeaders(parsed.request.query), + }, + body: { mode: 'raw', raw: rawBody }, + header: services.common.jsonToHeaders(parsed.request.headers), + method: parsed.request.url.method, + } + } + const response = new psdk.Response({ + code: parsed.response.code, + responseTime: parsed.response.time, + body: JSON.stringify(parsed.response.body), + header: services.common.jsonToHeaders(parsed.response.headers), + + originalRequest: request, }) + + services.example.print(response) + const fn = async () => { + try { + const { saveOrNot } = (await Enquirer.prompt({ + name: 'saveOrNot', + type: 'select', + message: 'save changes?', + choices: [{ name: 'yes' }, { name: 'no' }], + })) as any + + return saveOrNot + } catch (e) { + return 'no' + } + } + const saveOrNot = await fn() + + if (saveOrNot == 'yes') { + resource.update(response) + await services.collection.save(cmd, co) + } } } diff --git a/src/services/cmdopts.ts b/src/services/cmdopts.ts index 489d9d5..5bab853 100644 --- a/src/services/cmdopts.ts +++ b/src/services/cmdopts.ts @@ -182,10 +182,7 @@ export class CmdOptsService { const headersString = cmd.parent.opts().headers || services.env.globalHeaders || '{}' const headers = JSON.parse(headersString) - const result = Object.entries(headers).map(([k, v]) => { - const value: string | any = v - return new psdk.Header({ key: k, value, system: true }) - }) + const result = services.common.jsonToHeaders(headers) return result } } diff --git a/src/services/common.ts b/src/services/common.ts index 89459f5..e08f5f4 100644 --- a/src/services/common.ts +++ b/src/services/common.ts @@ -1,8 +1,10 @@ import fs from 'fs-extra' +import psdk from 'postman-collection' import util, { inspect } from 'node:util' import lodash from 'lodash' import newman, { NewmanRunOptions, NewmanRunSummary } from 'newman' import services from '@src/services/index.js' +import pretty from 'js-object-pretty-print' export class CommonService { _ = lodash @@ -39,11 +41,16 @@ export class CommonService { * into JSON-parsable string. */ toJsonString(input: string) { - const keyMatcher = '([^",{}\\s]+?)' - const valMatcher = '(.,*)' - const matcher = new RegExp(`${keyMatcher}\\s*:\\s*${valMatcher}`, 'g') - const parser = (_, key, value) => `"${key}":${value}` - return input.replace(matcher, parser) + //const keyMatcher = '([^",{}\\s]+?)' + //const valMatcher = '(.,*)' + //const matcher = new RegExp(`${keyMatcher}\\s*:\\s*${valMatcher}`, 'g') + //const parser = (_, key, value) => `"${key}":${value}` + //return input.replace(matcher, parser) + + //return input.replace(/([\$\w]+)\s*:/g, function (_, $1) {return '"' + $1 + '":'}) + //.replace(/'([^']+)'/g, function (_, $1) {return '"' + $1 + '"'}) + + return pretty.pretty(input, 4, 'JSON') } isJson(input: string) { @@ -55,6 +62,14 @@ export class CommonService { } } + jsonToHeaders(jsonHeaders: Record) { + const result = Object.entries(jsonHeaders).map(([k, v]) => { + const value: string | any = v + return new psdk.Header({ key: k, value }) + }) + return result + } + /** * Pretty-prints an object recursively. */ diff --git a/src/services/example.ts b/src/services/example.ts index 85a8627..04b382f 100644 --- a/src/services/example.ts +++ b/src/services/example.ts @@ -2,12 +2,21 @@ import { PostmanCli } from '@src/types.js' import psdk from 'postman-collection' import services from '@src/services/index.js' +type ToPrintableOpts = { + addParsedBody?: true +} + /** * Mostly an abstraction over psdk.Response * for cmd-show. */ export class ExampleService { - toPrintable(r: psdk.Response): PostmanCli.ExamplePrintable { + declare ToPrintableOpts: ToPrintableOpts + + toPrintable( + r: psdk.Response, + opts: ToPrintableOpts = {} + ): PostmanCli.ExamplePrintable { let urlMethod = '' let urlPath = '' const resultResponse = services.response.toPrintable(r) @@ -28,6 +37,15 @@ export class ExampleService { urlPath = item.request.url.getPath() } + if (opts.addParsedBody && resultRequest?.$parsedBody) { + resultRequest.body = resultRequest.$parsedBody + delete resultRequest.$parsedBody + } + if (opts.addParsedBody && resultResponse?.$parsedBody) { + resultResponse.body = resultResponse.$parsedBody + delete resultResponse.$parsedBody + } + return { response: resultResponse, request: resultRequest } } @@ -35,7 +53,7 @@ export class ExampleService { let req = '' if (r.request) req = services.request.getPrintString(r.request) const res = services.response.getPrintString(r.response) - return req + '\n\n\n\n' + res + return req + '\n\n' + res } print(r: psdk.Response) { diff --git a/src/services/response.ts b/src/services/response.ts index de26b22..236014d 100644 --- a/src/services/response.ts +++ b/src/services/response.ts @@ -121,12 +121,12 @@ export class ResponseService { }, headers, body: rawBody, - $parsedBody, - $parseHint, size: r.size() as any, time: r.responseTime, code: r.code, status: r.status, + $parsedBody, + $parseHint, } }