diff --git a/LICENSE b/LICENSE index a48530c..f99e9ee 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Contentstack +Copyright (c) 2024 Contentstack Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 725e13d..a8e486d 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ $ npm install -g @contentstack/apps-cli $ csdx COMMAND running command... $ csdx (--version|-v) -@contentstack/apps-cli/1.0.3 darwin-arm64 node-v20.3.1 +@contentstack/apps-cli/1.0.4 darwin-arm64 node-v20.10.0 $ csdx --help [COMMAND] USAGE $ csdx COMMAND @@ -30,6 +30,7 @@ USAGE # Commands +* [`csdx app`](#csdx-app) * [`csdx app:create`](#csdx-appcreate) * [`csdx app:delete`](#csdx-appdelete) * [`csdx app:get`](#csdx-appget) @@ -37,6 +38,33 @@ USAGE * [`csdx app:uninstall`](#csdx-appuninstall) * [`csdx app:update`](#csdx-appupdate) +## `csdx app` + +Apps CLI plugin + +``` +USAGE + $ csdx app + +DESCRIPTION + Apps CLI plugin + +EXAMPLES + $ csdx app:create + + $ csdx app:get + + $ csdx app:update + + $ csdx app:delete + + $ csdx app:install + + $ csdx app:uninstall +``` + +_See code: [src/commands/app/index.ts](https://github.com/contentstack/apps-cli/blob/v1.0.4/src/commands/app/index.ts)_ + ## `csdx app:create` Create a new app in Developer Hub and optionally clone a boilerplate locally. @@ -66,7 +94,7 @@ EXAMPLES $ csdx app:create --name App-3 --app-type organization --org -d ./boilerplate -c ./external-config.json ``` -_See code: [src/commands/app/create.ts](https://github.com/contentstack/apps-cli/blob/v1.0.3/src/commands/app/create.ts)_ +_See code: [src/commands/app/create.ts](https://github.com/contentstack/apps-cli/blob/v1.0.4/src/commands/app/create.ts)_ ## `csdx app:delete` @@ -91,7 +119,7 @@ EXAMPLES $ csdx app:delete --app-uid --org -d ./boilerplate ``` -_See code: [src/commands/app/delete.ts](https://github.com/contentstack/apps-cli/blob/v1.0.3/src/commands/app/delete.ts)_ +_See code: [src/commands/app/delete.ts](https://github.com/contentstack/apps-cli/blob/v1.0.4/src/commands/app/delete.ts)_ ## `csdx app:get` @@ -121,7 +149,7 @@ EXAMPLES $ csdx app:get --org --app-uid --app-type organization ``` -_See code: [src/commands/app/get.ts](https://github.com/contentstack/apps-cli/blob/v1.0.3/src/commands/app/get.ts)_ +_See code: [src/commands/app/get.ts](https://github.com/contentstack/apps-cli/blob/v1.0.4/src/commands/app/get.ts)_ ## `csdx app:install` @@ -147,7 +175,7 @@ EXAMPLES $ csdx app:install --org --app-uid --stack-api-key ``` -_See code: [src/commands/app/install.ts](https://github.com/contentstack/apps-cli/blob/v1.0.3/src/commands/app/install.ts)_ +_See code: [src/commands/app/install.ts](https://github.com/contentstack/apps-cli/blob/v1.0.4/src/commands/app/install.ts)_ ## `csdx app:uninstall` @@ -174,7 +202,7 @@ EXAMPLES $ csdx app:uninstall --org --app-uid --installation-uid ``` -_See code: [src/commands/app/uninstall.ts](https://github.com/contentstack/apps-cli/blob/v1.0.3/src/commands/app/uninstall.ts)_ +_See code: [src/commands/app/uninstall.ts](https://github.com/contentstack/apps-cli/blob/v1.0.4/src/commands/app/uninstall.ts)_ ## `csdx app:update` @@ -197,5 +225,5 @@ EXAMPLES $ csdx app:update --app-manifest ./boilerplate/manifest.json ``` -_See code: [src/commands/app/update.ts](https://github.com/contentstack/apps-cli/blob/v1.0.3/src/commands/app/update.ts)_ +_See code: [src/commands/app/update.ts](https://github.com/contentstack/apps-cli/blob/v1.0.4/src/commands/app/update.ts)_ diff --git a/package-lock.json b/package-lock.json index 5bd8eeb..bd687c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "@contentstack/apps-cli", - "version": "1.0.3", + "version": "1.0.4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@contentstack/apps-cli", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "dependencies": { - "@contentstack/cli-command": "^1.2.11", - "@contentstack/cli-utilities": "^1.5.1", + "@contentstack/cli-command": "^1.2.16", + "@contentstack/cli-utilities": "^1.5.8", "adm-zip": "^0.5.10", "chalk": "^4.1.2", "lodash": "^4.17.21", @@ -587,11 +587,11 @@ } }, "node_modules/@contentstack/cli-command": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@contentstack/cli-command/-/cli-command-1.2.11.tgz", - "integrity": "sha512-cd6b0ZZggUToLiBfOMeczANXhuPdbyYoyZi1JRxxvICWDble7hAV1PlYWr5eT7E14f0V0J2bsKVI96BxENWiyg==", + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@contentstack/cli-command/-/cli-command-1.2.16.tgz", + "integrity": "sha512-u8GecSKkRgMPAmsib1xtfcUF8T/+ZbHe4JKAibRLkVc7jIjwuToq36hPgGFMfUAK4HX/UZ09stS+f3dUN1maOA==", "dependencies": { - "@contentstack/cli-utilities": "^1.5.1", + "@contentstack/cli-utilities": "~1.5.7", "contentstack": "^3.10.1" }, "engines": { @@ -599,13 +599,13 @@ } }, "node_modules/@contentstack/cli-utilities": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.5.1.tgz", - "integrity": "sha512-GQK/yduIrJ/ERA534aUf9gSjNO1BYv0jjRnpHRLPmzMJPOY9gkY9crp/84SlEkaghLuFwvao9sSX+NN/27HCeg==", + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.5.8.tgz", + "integrity": "sha512-YrIXi8Nr0qlITODRUVpbW9sfdmKNHS2tY7O/y2AxvkzTFxBnRafT1HZTUm/RaLdxmnFudFD86rVzjxmojop/lA==", "dependencies": { - "@contentstack/management": "~1.10.0", + "@contentstack/management": "~1.12.0", "@oclif/core": "^2.9.3", - "axios": "1.3.4", + "axios": "^1.6.0", "chalk": "^4.0.0", "cli-cursor": "^3.1.0", "cli-table": "^0.3.11", @@ -615,27 +615,19 @@ "inquirer": "8.2.4", "inquirer-search-checkbox": "^1.0.0", "inquirer-search-list": "^1.2.6", + "klona": "^2.0.6", "lodash": "^4.17.15", "mkdirp": "^1.0.4", "open": "^8.4.2", "ora": "^5.4.0", "rxjs": "^6.6.7", + "traverse": "^0.6.7", "unique-string": "^2.0.0", "uuid": "^9.0.0", "winston": "^3.7.2", "xdg-basedir": "^4.0.0" } }, - "node_modules/@contentstack/cli-utilities/node_modules/axios": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", - "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/@contentstack/cli-utilities/node_modules/define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", @@ -644,19 +636,6 @@ "node": ">=8" } }, - "node_modules/@contentstack/cli-utilities/node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/@contentstack/cli-utilities/node_modules/open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -674,11 +653,11 @@ } }, "node_modules/@contentstack/management": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@contentstack/management/-/management-1.10.2.tgz", - "integrity": "sha512-jO24EqcCJhOjqdsqw8y3T0SPPAd0DG4BByjUcV0S28W2yoa8aBbcjcbZioRPzRLYKTmZWsAZissl18cIJm5djQ==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@contentstack/management/-/management-1.12.0.tgz", + "integrity": "sha512-+L+WVhSYEtfdG9v794TjLT8Fd6fCB8meqoho666mg1kNufzXcsqr7hjubX5cSL7GcZFdKntkDpZ2RaOnTHReJg==", "dependencies": { - "axios": "^1.4.0", + "axios": "^1.5.1", "form-data": "^3.0.1", "lodash": "^4.17.21", "qs": "^6.11.2" @@ -2426,9 +2405,9 @@ } }, "node_modules/axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "dependencies": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -5135,9 +5114,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", "funding": [ { "type": "individual", @@ -7036,6 +7015,14 @@ "json-buffer": "3.0.1" } }, + "node_modules/klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==", + "engines": { + "node": ">= 8" + } + }, "node_modules/kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -11024,6 +11011,14 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/traverse": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", + "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -13453,22 +13448,22 @@ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" }, "@contentstack/cli-command": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@contentstack/cli-command/-/cli-command-1.2.11.tgz", - "integrity": "sha512-cd6b0ZZggUToLiBfOMeczANXhuPdbyYoyZi1JRxxvICWDble7hAV1PlYWr5eT7E14f0V0J2bsKVI96BxENWiyg==", + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@contentstack/cli-command/-/cli-command-1.2.16.tgz", + "integrity": "sha512-u8GecSKkRgMPAmsib1xtfcUF8T/+ZbHe4JKAibRLkVc7jIjwuToq36hPgGFMfUAK4HX/UZ09stS+f3dUN1maOA==", "requires": { - "@contentstack/cli-utilities": "^1.5.1", + "@contentstack/cli-utilities": "~1.5.7", "contentstack": "^3.10.1" } }, "@contentstack/cli-utilities": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.5.1.tgz", - "integrity": "sha512-GQK/yduIrJ/ERA534aUf9gSjNO1BYv0jjRnpHRLPmzMJPOY9gkY9crp/84SlEkaghLuFwvao9sSX+NN/27HCeg==", + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@contentstack/cli-utilities/-/cli-utilities-1.5.8.tgz", + "integrity": "sha512-YrIXi8Nr0qlITODRUVpbW9sfdmKNHS2tY7O/y2AxvkzTFxBnRafT1HZTUm/RaLdxmnFudFD86rVzjxmojop/lA==", "requires": { - "@contentstack/management": "~1.10.0", + "@contentstack/management": "~1.12.0", "@oclif/core": "^2.9.3", - "axios": "1.3.4", + "axios": "^1.6.0", "chalk": "^4.0.0", "cli-cursor": "^3.1.0", "cli-table": "^0.3.11", @@ -13478,42 +13473,24 @@ "inquirer": "8.2.4", "inquirer-search-checkbox": "^1.0.0", "inquirer-search-list": "^1.2.6", + "klona": "^2.0.6", "lodash": "^4.17.15", "mkdirp": "^1.0.4", "open": "^8.4.2", "ora": "^5.4.0", "rxjs": "^6.6.7", + "traverse": "^0.6.7", "unique-string": "^2.0.0", "uuid": "^9.0.0", "winston": "^3.7.2", "xdg-basedir": "^4.0.0" }, "dependencies": { - "axios": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz", - "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==", - "requires": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" }, - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - }, "open": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", @@ -13527,11 +13504,11 @@ } }, "@contentstack/management": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@contentstack/management/-/management-1.10.2.tgz", - "integrity": "sha512-jO24EqcCJhOjqdsqw8y3T0SPPAd0DG4BByjUcV0S28W2yoa8aBbcjcbZioRPzRLYKTmZWsAZissl18cIJm5djQ==", + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@contentstack/management/-/management-1.12.0.tgz", + "integrity": "sha512-+L+WVhSYEtfdG9v794TjLT8Fd6fCB8meqoho666mg1kNufzXcsqr7hjubX5cSL7GcZFdKntkDpZ2RaOnTHReJg==", "requires": { - "axios": "^1.4.0", + "axios": "^1.5.1", "form-data": "^3.0.1", "lodash": "^4.17.21", "qs": "^6.11.2" @@ -14934,9 +14911,9 @@ } }, "axios": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", - "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.2.tgz", + "integrity": "sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==", "requires": { "follow-redirects": "^1.15.0", "form-data": "^4.0.0", @@ -16946,9 +16923,9 @@ "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==" }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==" }, "for-each": { "version": "0.3.3", @@ -18352,6 +18329,11 @@ "json-buffer": "3.0.1" } }, + "klona": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", + "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==" + }, "kuler": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", @@ -21366,6 +21348,11 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "traverse": { + "version": "0.6.7", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.7.tgz", + "integrity": "sha512-/y956gpUo9ZNCb99YjxG7OaslxZWHfCHAUUfshwqOXmxUIvqLjVO581BT+gM59+QV9tFe6/CGG53tsA1Y7RSdg==" + }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", diff --git a/package.json b/package.json index c3340d5..bc3fff7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@contentstack/apps-cli", - "version": "1.0.3", + "version": "1.0.4", "description": "App ClI", "author": "Contentstack CLI", "homepage": "https://github.com/contentstack/contentstack-apps-cli", @@ -17,8 +17,8 @@ "/oclif.manifest.json" ], "dependencies": { - "@contentstack/cli-command": "^1.2.11", - "@contentstack/cli-utilities": "^1.5.1", + "@contentstack/cli-command": "^1.2.16", + "@contentstack/cli-utilities": "^1.5.8", "adm-zip": "^0.5.10", "chalk": "^4.1.2", "lodash": "^4.17.21", diff --git a/src/app-cli-base-coomand.ts b/src/app-cli-base-coomand.ts new file mode 100644 index 0000000..586e94a --- /dev/null +++ b/src/app-cli-base-coomand.ts @@ -0,0 +1,33 @@ +import { resolve } from "path"; +import { existsSync, readFileSync } from "fs"; + +import config from "./config"; +import { AppManifest } from "./types"; +import { BaseCommand } from "./base-command"; + +export abstract class AppCLIBaseCommand extends BaseCommand< + typeof AppCLIBaseCommand +> { + protected manifestPath!: string; + protected manifestData!: AppManifest & Record; + + public async init(): Promise { + await super.init(); + this.getManifestData(); + } + + getManifestData() { + this.manifestPath = resolve(process.cwd(), `${config.defaultAppFileName}.json`); + if (existsSync(this.manifestPath)) { + try { + this.manifestData = JSON.parse( + readFileSync(this.manifestPath, { + encoding: "utf-8", + }) + ); + } catch (error) { + throw error; + } + } + } +} diff --git a/src/commands/app/base-command.ts b/src/base-command.ts similarity index 86% rename from src/commands/app/base-command.ts rename to src/base-command.ts index c104d50..570b3b0 100644 --- a/src/commands/app/base-command.ts +++ b/src/base-command.ts @@ -12,12 +12,13 @@ import { managementSDKInitiator, InquirePayload, cliux, + isAuthenticated, } from "@contentstack/cli-utilities"; -import config from "../../config"; -import { ConfigType, LogFn } from "../../types"; -import { Logger, getDeveloperHubUrl } from "../../util"; -import messages, { $t, commonMsg } from "../../messages"; +import config from "./config"; +import { ConfigType, LogFn } from "./types"; +import { Logger, getDeveloperHubUrl } from "./util"; +import messages, { $t, commonMsg } from "./messages"; export type Flags = Interfaces.InferredFlags< (typeof BaseCommand)["baseFlags"] & T["flags"] @@ -42,8 +43,6 @@ export abstract class BaseCommand extends Command { protected flags!: Flags; protected args!: Args; - static hidden = true; - // NOTE define flags that can be inherited by any command that extends BaseCommand static baseFlags: FlagInput = { org: Flags.string({ @@ -69,14 +68,16 @@ export abstract class BaseCommand extends Command { ux.registerSearchPlugin(); this.registerConfig(); - + this.developerHubBaseUrl = - this.sharedConfig.developerHubBaseUrl || (await getDeveloperHubUrl()); + this.sharedConfig.developerHubBaseUrl || (await getDeveloperHubUrl()); await this.initCmaSDK(); - + // Init logger const logger = new Logger(this.sharedConfig); this.log = logger.log.bind(logger); + + this.validateRegionAndAuth(); } protected async catch(err: Error & { exitCode?: number }): Promise { @@ -156,4 +157,16 @@ export abstract class BaseCommand extends Command { message: message as string, }); } + + /** + * The `validateRegionAndAuth` function verify whether region is set and user is logged in or not + */ + validateRegionAndAuth() { + if (this.region) { + if (!isAuthenticated()) { + this.log(this.messages.CLI_APP_CLI_LOGIN_FAILED, "error"); + this.exit(1); + } + } + } } diff --git a/src/commands/app/create.ts b/src/commands/app/create.ts index faabcbd..f824efd 100644 --- a/src/commands/app/create.ts +++ b/src/commands/app/create.ts @@ -14,9 +14,9 @@ import { writeFileSync, createWriteStream, } from "fs"; -import { ux, cliux, flags, HttpClient } from "@contentstack/cli-utilities"; +import { ux, cliux, flags, HttpClient, configHandler } from "@contentstack/cli-utilities"; -import { BaseCommand } from "./base-command"; +import { BaseCommand } from "../../base-command"; import { AppManifest, AppType } from "../../types"; import { appCreate, commonMsg } from "../../messages"; import { @@ -28,7 +28,6 @@ import { export default class Create extends BaseCommand { private appData!: AppManifest; - static hidden: boolean = false; static description = "Create a new app in Developer Hub and optionally clone a boilerplate locally."; @@ -133,10 +132,11 @@ export default class Create extends BaseCommand { ); } - this.sharedConfig.org = await getOrg(this.flags, { + //Auto select org in case of oauth + this.sharedConfig.org = configHandler.get('oauthOrgUid') ?? (await getOrg(this.flags, { log: this.log, managementSdk: this.managementSdk, - }); + })); } /** diff --git a/src/commands/app/delete.ts b/src/commands/app/delete.ts index e69d853..e81c9ef 100644 --- a/src/commands/app/delete.ts +++ b/src/commands/app/delete.ts @@ -1,11 +1,10 @@ -import { BaseCommand } from "./base-command"; import { cliux, flags } from "@contentstack/cli-utilities"; + +import {AppCLIBaseCommand} from "../../app-cli-base-coomand"; import { $t, commonMsg, deleteAppMsg } from "../../messages"; import { getOrg, fetchAppInstallations, deleteApp, getApp } from "../../util"; -export default class Delete extends BaseCommand { - static hidden: boolean = false; - +export default class Delete extends AppCLIBaseCommand { static description = "Delete app from marketplace"; static examples = [ @@ -23,10 +22,12 @@ export default class Delete extends BaseCommand { async run(): Promise { try { let app; - this.sharedConfig.org = await getOrg(this.flags, { + this.sharedConfig.org = this.manifestData?.organization_uid ?? (await getOrg(this.flags, { managementSdk: this.managementSdk, log: this.log, - }); + })); + this.flags["app-uid"] = this.manifestData?.uid ?? this.flags["app-uid"]; + if (!this.flags["app-uid"]) { app = await getApp(this.flags, this.sharedConfig.org, { managementSdk: this.managementAppSdk, diff --git a/src/commands/app/get.ts b/src/commands/app/get.ts index d657eba..f33a2c7 100644 --- a/src/commands/app/get.ts +++ b/src/commands/app/get.ts @@ -1,11 +1,10 @@ -import { BaseCommand } from "./base-command"; -import { getOrg, getApp, writeFile, fetchApp } from "../../util"; import { flags } from "@contentstack/cli-utilities"; -import { commonMsg } from "../../messages"; -export default class Get extends BaseCommand { - static hidden: boolean = false; +import { commonMsg } from "../../messages"; +import {AppCLIBaseCommand} from "../../app-cli-base-coomand"; +import { getOrg, getApp, writeFile, fetchApp } from "../../util"; +export default class Get extends AppCLIBaseCommand { static description = "Get details of an app in developer hub"; static examples = [ @@ -33,10 +32,13 @@ export default class Get extends BaseCommand { async run(): Promise { try { let appData; - this.sharedConfig.org = await getOrg(this.flags, { + this.flags["app-uid"] = this.manifestData?.uid ?? this.flags["app-uid"]; + + this.sharedConfig.org = this.manifestData?.organization_uid ?? (await getOrg(this.flags, { managementSdk: this.managementSdk, log: this.log, - }); + })); + if (!this.flags["app-uid"]) { appData = await getApp(this.flags, this.sharedConfig.org, { managementSdk: this.managementAppSdk, diff --git a/src/commands/app/index.ts b/src/commands/app/index.ts index 7d5c73c..a70bf4c 100644 --- a/src/commands/app/index.ts +++ b/src/commands/app/index.ts @@ -7,8 +7,6 @@ import { print } from "../../util/log"; export default class App extends Command { static description = "Apps CLI plugin"; - static hidden: boolean = true; - static examples = [ "$ <%= config.bin %> <%= command.id %>:create", "$ <%= config.bin %> <%= command.id %>:get", diff --git a/src/commands/app/install.ts b/src/commands/app/install.ts index 9201b3f..d70965c 100644 --- a/src/commands/app/install.ts +++ b/src/commands/app/install.ts @@ -1,82 +1,139 @@ -import { BaseCommand } from "./base-command"; import { cliux, flags } from "@contentstack/cli-utilities"; + +import { AppCLIBaseCommand } from "../../app-cli-base-coomand"; import { $t, commonMsg, installAppMsg } from "../../messages"; -import { getOrg, getApp, getStack, installApp, fetchApp, fetchStack} from "../../util"; +import { + getOrg, + getApp, + getStack, + installApp, + fetchApp, + fetchStack, +} from "../../util"; -export default class Install extends BaseCommand { - static description: string | undefined = "Install an app from the marketplace"; - static hidden: boolean = false; +export default class Install extends AppCLIBaseCommand { + static description: string | undefined = + "Install an app from the marketplace"; - static examples = [ - "$ <%= config.bin %> <%= command.id %>", - "$ <%= config.bin %> <%= command.id %> --org --app-uid ", - "$ <%= config.bin %> <%= command.id %> --org --app-uid --stack-api-key ", - ]; + static examples = [ + "$ <%= config.bin %> <%= command.id %>", + "$ <%= config.bin %> <%= command.id %> --org --app-uid ", + "$ <%= config.bin %> <%= command.id %> --org --app-uid --stack-api-key ", + ]; - static flags = { - 'app-uid': flags.string({ - description: commonMsg.APP_UID, - }), - 'stack-api-key': flags.string({ - description: commonMsg.STACK_API_KEY - }) - } + static flags = { + "app-uid": flags.string({ + description: commonMsg.APP_UID, + }), + "stack-api-key": flags.string({ + description: commonMsg.STACK_API_KEY, + }), + }; - async run(): Promise { - try { - let app, stack, appType; - - // validating user given stack, as installation API doesn't return appropriate errors if stack-api-key is invalid - // validating this first, as orgUid is not required for fetching stack - if (this.flags['stack-api-key']) { - stack = await fetchStack(this.flags, {managementSdk: this.managementSdk, log: this.log}) - } + async run(): Promise { + try { + let app, stack, appType; + this.flags["app-uid"] = this.manifestData?.uid ?? this.flags["app-uid"]; //manifest file first preference - // get organization to be used - this.sharedConfig.org = await getOrg(this.flags, {managementSdk: this.managementSdk, log: this.log}); - - // fetch app details - if (!this.flags['app-uid']) { - app = await getApp(this.flags, this.sharedConfig.org, {managementSdk: this.managementAppSdk, log: this.log}) - } else { - app = await fetchApp(this.flags, this.sharedConfig.org, {managementSdk: this.managementAppSdk, log: this.log}) - } - appType = app?.['target_type'] // get app-type from the fetched app - this.flags['app-uid'] = app?.uid; + // validating user given stack, as installation API doesn't return appropriate errors if stack-api-key is invalid + // validating this first, as orgUid is not required for fetching stack + if (this.flags["stack-api-key"]) { + stack = await fetchStack(this.flags, { + managementSdk: this.managementSdk, + log: this.log, + }); + } + + // get organization to be used + this.sharedConfig.org = + this.manifestData?.organization_uid ?? + (await getOrg(this.flags, { + managementSdk: this.managementSdk, + log: this.log, + })); + + // fetch app details + if (!this.flags["app-uid"]) { + app = await getApp(this.flags, this.sharedConfig.org, { + managementSdk: this.managementAppSdk, + log: this.log, + }); + } else { + app = await fetchApp(this.flags, this.sharedConfig.org, { + managementSdk: this.managementAppSdk, + log: this.log, + }); + } + appType = app?.["target_type"]; // get app-type from the fetched app + this.flags["app-uid"] = app?.uid; - // in case stack-api-key is provided and the selected app is an organization app - if (appType === 'organization' && this.flags['stack-api-key']) { - const confirmation = this.flags['yes'] || await cliux.inquire({ + // in case stack-api-key is provided and the selected app is an organization app + if (appType === "organization" && this.flags["stack-api-key"]) { + const confirmation = + this.flags["yes"] || + (await cliux.inquire({ type: "confirm", - message: $t(installAppMsg.INSTALL_ORG_APP_TO_STACK, { app: app?.name || app?.uid }), - name: "confirmation" - }) - if (!confirmation) { - throw new Error(commonMsg.USER_TERMINATION) - } + message: $t(installAppMsg.INSTALL_ORG_APP_TO_STACK, { + app: app?.name || app?.uid, + }), + name: "confirmation", + })); + if (!confirmation) { + throw new Error(commonMsg.USER_TERMINATION); } + } - // in case a stack app is selected and no stack-api-key is provided - if (appType === 'stack' && !this.flags['stack-api-key']) { - this.log($t(installAppMsg.MISSING_STACK_API_KEY, { app: app?.name || app?.uid }), "warn") - stack = await getStack(this.sharedConfig.org, {managementSdk: this.managementSdk, log: this.log}) - this.flags['stack-api-key'] = stack?.['api_key'] - } + // in case a stack app is selected and no stack-api-key is provided + if (appType === "stack" && !this.flags["stack-api-key"]) { + this.log( + $t(installAppMsg.MISSING_STACK_API_KEY, { + app: app?.name || app?.uid, + }), + "warn" + ); + stack = await getStack(this.sharedConfig.org, { + managementSdk: this.managementSdk, + log: this.log, + }); + this.flags["stack-api-key"] = stack?.["api_key"]; + } - // install app - this.log($t(installAppMsg.INSTALLING_APP_NOTICE, { + // install app + this.log( + $t(installAppMsg.INSTALLING_APP_NOTICE, { app: app?.name || app?.uid, type: appType, - target: this.flags['stack-api-key'] || this.sharedConfig.org - }), "info") - await installApp(this.flags, this.sharedConfig.org, appType, {managementSdk: this.managementAppSdk, log: this.log}) - this.log($t(installAppMsg.APP_INSTALLED_SUCCESSFULLY, { - app: app?.name || this.flags['app-uid'] as string, - target: stack?.name || this.sharedConfig.org - }), "info"); - } catch(error: any) { - this.log(error?.errorMessage || error?.message || error, "error") - this.exit(1) - } + target: this.flags["stack-api-key"] || this.sharedConfig.org, + }), + "info" + ); + await installApp(this.flags, this.sharedConfig.org, appType, { + managementSdk: this.managementAppSdk, + log: this.log, + }); + this.log( + $t(installAppMsg.APP_INSTALLED_SUCCESSFULLY, { + app: app?.name || (this.flags["app-uid"] as string), + target: stack?.name || this.sharedConfig.org, + }), + "info" + ); + + this.displayStackUrl(); + } catch (error: any) { + this.log(error?.errorMessage || error?.message || error, "error"); + this.exit(1); } + } + + /** + * @method displayStackUrl - show guid to stack after installing app successfully in the stack + */ + displayStackUrl(): void { + const stackPath = `${this.uiHost}/#!/stack/${this.flags["stack-api-key"]}/dashboard`; + this.log( + `Please use the following URL to start using the stack: ${stackPath}`, + "info" + ); + } } \ No newline at end of file diff --git a/src/commands/app/uninstall.ts b/src/commands/app/uninstall.ts index 679c33b..42d8645 100644 --- a/src/commands/app/uninstall.ts +++ b/src/commands/app/uninstall.ts @@ -1,12 +1,12 @@ -import { BaseCommand } from "./base-command"; import { flags } from "@contentstack/cli-utilities"; -import { getOrg, fetchApp, getInstalledApps } from "../../util"; + import { commonMsg, uninstallAppMsg } from "../../messages"; +import {AppCLIBaseCommand} from "../../app-cli-base-coomand"; +import { getOrg, fetchApp, getInstalledApps } from "../../util"; import { UninstallAppFactory } from "../../factories/uninstall-app-factory"; -export default class Uninstall extends BaseCommand { +export default class Uninstall extends AppCLIBaseCommand { static description = "Uninstall an app"; - static hidden: boolean = false; static examples = [ "$ <%= config.bin %> <%= command.id %>", @@ -29,8 +29,10 @@ export default class Uninstall extends BaseCommand { async run(): Promise { try { let app, appType + this.flags["app-uid"] = this.manifestData?.uid ?? this.flags["app-uid"]; + // get organization to be used - this.sharedConfig.org = await getOrg(this.flags, {managementSdk: this.managementSdk, log: this.log}); + this.sharedConfig.org = this.manifestData?.organization_uid ?? (await getOrg(this.flags, {managementSdk: this.managementSdk, log: this.log})); // fetch app details if (!this.flags['app-uid']) { diff --git a/src/commands/app/update.ts b/src/commands/app/update.ts index 033fb23..05d3a25 100644 --- a/src/commands/app/update.ts +++ b/src/commands/app/update.ts @@ -5,17 +5,13 @@ import { flags } from "@contentstack/cli-utilities"; import { App } from "@contentstack/management/types/app"; import { existsSync, readFileSync, writeFileSync } from "fs"; -import { AppManifest } from "../../types"; -import { BaseCommand } from "./base-command"; import { $t, appUpdate } from "../../messages"; import { fetchApp, getApp, getOrg } from "../../util"; +import {AppCLIBaseCommand} from "../../app-cli-base-coomand"; -export default class Update extends BaseCommand { +export default class Update extends AppCLIBaseCommand { private orgUid!: string; private manifestPathRetry: number = 0; - private manifestData!: AppManifest & Record; - - static hidden: boolean = false; static description = "Update the existing app in developer hub"; @@ -32,8 +28,12 @@ export default class Update extends BaseCommand { async run(): Promise { try { - await this.validateManifest(); - this.orgUid = this.flags.org || this.manifestData.organization_uid; + //if working directory isn't app directory + if(!this.manifestData){ + await this.validateManifest(); + } + this.flags["app-manifest"] = this.manifestPath ?? this.flags["app-manifest"]; + this.orgUid = this.flags.org ?? this.manifestData?.organization_uid; this.sharedConfig.org = await getOrg( { org: this.orgUid as any }, { diff --git a/src/messages/index.ts b/src/messages/index.ts index eb06c1b..98d47d9 100644 --- a/src/messages/index.ts +++ b/src/messages/index.ts @@ -34,7 +34,8 @@ const commonMsg = { APP_TYPE_DESCRIPTION: "Type of App", CONTACT_SUPPORT: "Please contact the support team.", STACK_API_KEY: "API key of the stack where the app is to be installed.", - USER_TERMINATION: "Process terminated by the user." + USER_TERMINATION: "Process terminated by the user.", + CLI_APP_CLI_LOGIN_FAILED: 'You are not logged in. Please login with command $ csdx auth:login' }; const appCreate = { @@ -121,7 +122,8 @@ const $t = (msg: string, args: Record): string => { if (!msg) return ""; for (const key of Object.keys(args)) { - msg = msg.replace(new RegExp(`{${key}}`, 'g'), args[key]); + const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + msg = msg.replace(new RegExp(`{${escapedKey}}`, "g"), args[key]); } return msg; diff --git a/src/util/common-utils.ts b/src/util/common-utils.ts index 39aef40..c4a661f 100644 --- a/src/util/common-utils.ts +++ b/src/util/common-utils.ts @@ -67,7 +67,6 @@ async function fetchApps( .catch((error) => { cliux.loader("failed"); log("Some error occurred while fetching apps.", "warn"); - log(error.errorMessage, "error"); throw error; }); diff --git a/src/util/inquirer.ts b/src/util/inquirer.ts index df3eeab..14667e7 100644 --- a/src/util/inquirer.ts +++ b/src/util/inquirer.ts @@ -1,4 +1,5 @@ import find from "lodash/find"; +import isEmpty from "lodash/isEmpty"; import { existsSync } from "fs"; import { basename, dirname, join } from "path"; import { @@ -228,13 +229,19 @@ async function getInstallation( if (uninstallAll) { return installations.map(installation => installation.uid).join(',') } - let _selectedInstallation = await cliux + let _selectedInstallation: string[] = await cliux .inquire({ type: 'checkbox', name: 'appInstallation', choices: installations, - message: messages.CHOOSE_AN_INSTALLATION - }) as string[] + message: messages.CHOOSE_AN_INSTALLATION, + validate: (input) => { + if (isEmpty(input)) { + return $t(errors.NOT_EMPTY, { value: "stack value"}); + } + return true; + } + }) selectedInstallation = _selectedInstallation.join(',') } else { // as this is an organization app, and it is supposed to only be installed on the source organization