├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── README.md ├── package.json ├── src ├── command-invoker.ts ├── commands │ ├── command.ts │ ├── config-command.ts │ ├── create-command.ts │ ├── delete-command.ts │ ├── destroy-command.ts │ ├── get-command.ts │ ├── help-command.ts │ ├── info-command.ts │ ├── init-command.ts │ ├── kill-command.ts │ ├── login-command.ts │ ├── logout-command.ts │ ├── logs-command.ts │ ├── ls-command.ts │ ├── orders-command.ts │ ├── publish-command.ts │ ├── register-command.ts │ ├── rename-command.ts │ ├── restart-command.ts │ ├── run-command.ts │ ├── starters-command.ts │ ├── status-command.ts │ ├── update-command.ts │ ├── version-command.ts │ └── whoami-command.ts ├── config │ └── index.ts ├── context.ts ├── gulpfile.js ├── helpers │ ├── archiver-wrapper.ts │ ├── create-checkbox-prompt.ts │ ├── create-confirmation-prompt.ts │ ├── create-jenkinsfile.ts │ ├── create-list-prompt.ts │ ├── create-pass-prompt.ts │ ├── create-text-prompt.ts │ ├── deserialize-json-file.ts │ ├── download-from-ftp.ts │ ├── erase-directories.ts │ ├── generate-random-string.ts │ ├── get-theme-name.ts │ ├── index.ts │ ├── remove-folder.ts │ └── serialize-json-file.ts ├── index.ts ├── models │ ├── auth-method.ts │ ├── cli-status.ts │ ├── database.ts │ ├── dot-mdb.ts │ ├── entity.ts │ ├── index.ts │ ├── order.ts │ ├── output-color.ts │ ├── output-message.ts │ ├── package-json.ts │ ├── package-managers.ts │ ├── parsed-flags.ts │ ├── project-entry.ts │ ├── project-meta.ts │ ├── project-status.ts │ ├── project.ts │ ├── starter-option.ts │ ├── subscription-plan.ts │ ├── user-plan-data.ts │ └── user-project.ts ├── postbuild.js ├── receivers │ ├── app-receiver.ts │ ├── backend-receiver.ts │ ├── blank-receiver.ts │ ├── compose-receiver.ts │ ├── config-receiver.ts │ ├── database-receiver.ts │ ├── frontend-receiver.ts │ ├── order-receiver.ts │ ├── receiver.ts │ ├── repo-receiver.ts │ ├── starter-receiver.ts │ ├── strategies │ │ ├── auth │ │ │ ├── auth-strategy.ts │ │ │ ├── facebook-auth-strategy.ts │ │ │ ├── google-auth-strategy.ts │ │ │ ├── index.ts │ │ │ ├── normal-auth-strategy.ts │ │ │ ├── social-auth-strategy.ts │ │ │ └── twitter-auth-strategy.ts │ │ ├── config │ │ │ ├── config-strategy.ts │ │ │ ├── domain-config-strategy.ts │ │ │ ├── index.ts │ │ │ ├── init-config-strategy.ts │ │ │ ├── member-config-strategy.ts │ │ │ ├── normal-config-strategy.ts │ │ │ └── project-name-config-strategy.ts │ │ └── publish │ │ │ ├── ftp-publish-strategy.ts │ │ │ ├── index.ts │ │ │ └── pipeline-publish-strategy.ts │ ├── user-receiver.ts │ └── wordpress-receiver.ts ├── test │ ├── command-invoker.test.js │ ├── commands │ │ ├── command.test.ts │ │ ├── config-command.test.ts │ │ ├── delete-command.test.ts │ │ ├── get-command.test.ts │ │ ├── help-command.test.ts │ │ ├── info-command.test.ts │ │ ├── init-command.test.ts │ │ ├── kill-command.test.ts │ │ ├── login-command.test.ts │ │ ├── logout-command.test.ts │ │ ├── logs-command.test.ts │ │ ├── ls-command.test.ts │ │ ├── orders-command.test.ts │ │ ├── publish-command.test.ts │ │ ├── register-command.test.ts │ │ ├── rename-command.test.ts │ │ ├── restart-command.test.ts │ │ ├── run-command.test.ts │ │ ├── update-command.test.ts │ │ └── version-command.test.ts │ ├── context.test.ts │ ├── helpers │ │ ├── archive-wrapper.test.ts │ │ ├── create-checkbox-prompt.test.ts │ │ ├── create-confirmation-prompt.test.ts │ │ ├── create-jenkinsfile.test.ts │ │ ├── create-list-prompt.test.ts │ │ ├── create-pass-prompt.test.ts │ │ ├── create-text-prompt.test.ts │ │ ├── deserialize-json-file.test.ts │ │ ├── download-from-ftp.test.ts │ │ └── serialize-json-file.test.ts │ ├── index.ts │ ├── receivers │ │ ├── app-receiver.test.ts │ │ ├── backend-receiver.test.ts │ │ ├── blank-receiver.test.ts │ │ ├── compose-receiver.ts │ │ ├── config-receiver.test.ts │ │ ├── database-receiver.test.ts │ │ ├── frontend-receiver.test.ts │ │ ├── order-receiver.test.ts │ │ ├── repo-receiver.test.ts │ │ ├── starter-receiver.test.ts │ │ ├── strategies │ │ │ ├── auth │ │ │ │ ├── auth-strategy.test.ts │ │ │ │ ├── facebook-auth-strategy.test.ts │ │ │ │ ├── google-auth-strategy.test.ts │ │ │ │ ├── normal-auth-strategy.test.ts │ │ │ │ └── twitter-auth-strategy.test.ts │ │ │ ├── config │ │ │ │ ├── domain-config-strategy.test.ts │ │ │ │ ├── init-config-strategy.test.ts │ │ │ │ ├── member-config-strategy.test.ts │ │ │ │ ├── normal-config-strategy.test.ts │ │ │ │ └── project-name-config-strategy.test.ts │ │ │ └── publish │ │ │ │ ├── ftp-publish-strategy.test.ts │ │ │ │ └── pipeline-publish-strategy.test.ts │ │ ├── user-receiver.test.ts │ │ └── wordpress-receiver.test.ts │ └── utils │ │ ├── command-result.test.ts │ │ ├── http-wrapper.test.ts │ │ ├── managers │ │ ├── dot-mdb-config-manager.test.ts │ │ ├── git-manager.test.ts │ │ ├── npm-package-manager.test.ts │ │ ├── package-manager-loader.test.ts │ │ └── yarn-package-manager.test.ts │ │ └── output-printer.test.ts └── utils │ ├── command-result.ts │ ├── http-wrapper.ts │ ├── managers │ ├── dot-mdb-config-manager.ts │ ├── git-manager.ts │ ├── npm-package-manager.ts │ ├── package-manager-loader.ts │ ├── package-manager.ts │ └── yarn-package-manager.ts │ └── output-printer.ts └── tsconfig.json /.eslintignore: -------------------------------------------------------------------------------- 1 | mocks 2 | static 3 | dist 4 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": 2018, 10 | "sourceType": "module" 11 | }, 12 | "rules": { 13 | "indent": [ 14 | "error", 15 | 4 16 | ], 17 | "linebreak-style": [ 18 | "error", 19 | "unix" 20 | ], 21 | "quotes": [ 22 | "error", 23 | "single" 24 | ], 25 | "semi": [ 26 | "error", 27 | "always" 28 | ], 29 | "no-console": "off" 30 | }, 31 | "globals": { 32 | "expect": false, 33 | "chai": false, 34 | "request": false, 35 | "app": false, 36 | "httpMocks": false 37 | } 38 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | node_modules/ 3 | package-lock.json 4 | dist/ 5 | .nyc_output/ 6 | yarn.lock 7 | .env 8 | out/ 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 | 5 |

6 | 7 |

MDB CLI β

8 | 9 |

10 | The fastest way to create & host MDB projects. 11 |

12 | 13 |

14 | 15 | logo 16 | 17 |

18 | 19 | | | Features | | 20 | |-----------------------------------------|------------------|------------------------------| 21 | |💡 |Initiate your projects with a single command⠀- cut your working time!⠀⠀ | ✔️ | 22 | |📘 |Publish your projects with a single command - just publish and grab a link to share | ✔️ | 23 | |📑 |Connect your own domain _(Coming soon...)_| ✔️ | 24 | |📱 |Ready to use, pre-set environment for every initiated project| ✔️ | 25 | |🗺️ |Easy, 1 minute installation ⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀ | ✔️| 26 | |💽 |Multiple terminal support⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀ ⠀⠀⠀| ✔️ | 27 | |👥 |Active community and quick updates| ✔️ | 28 | |🆓 |Free for personal and commercial use| ✔️ | 29 | ________ 30 | ## Table of Contents 31 | 32 | * [Quick Start](#quick-start) 33 | * [Integrations](#integrated-with) 34 | * [Supported terminals](#supported-terminals) 35 | * [Beta testing](#beta-testing) 36 | * [Usage](#usage) 37 | ________ 38 | 39 | # Quick Start 40 | 41 | - [Written Tutorial](https://mdbootstrap.com/cli/quick-start/) 42 | 43 | - [Video Tutorial](https://www.youtube.com/watch?v=-pIJnQQMexg) 44 | 45 | # Integrated with 46 | 47 | [](https://mdbootstrap.com/docs/jquery/)[](https://mdbootstrap.com/docs/angular/)[](https://mdbootstrap.com/docs/react/)[](https://mdbootstrap.com/docs/vue/) 48 | 49 | # Supported terminals 50 | 51 | ### Mac OS: 52 | - Terminal.app 53 | - iTerm 54 | ### Windows: 55 | - ConEmu 56 | - cmd.exe 57 | - Powershell 58 | - Cygwin 59 | ### Linux (Ubuntu, openSUSE, Arch Linux, etc): 60 | - gnome-terminal (Terminal GNOME) 61 | - konsole 62 | 63 | # Beta testing 64 | 65 | Please use GitHub Issues if you encounter any bugs or if you'd like to see a new feature in MDB CLI. 66 | If you'd like to join our Slack channel for beta testers send your request [here](http://mattonit.mdbootstrap.com/slack/). 67 | 68 | # Usage 69 | 70 | Please read the full [installation guide](https://mdbootstrap.com/cli/quick-start/) in order to install and use MDB CLI. 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mdb-cli", 3 | "version": "4.3.3", 4 | "description": "Command Line Interface for MDB", 5 | "main": "index.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/mdbootstrap/mdb-cli.git" 9 | }, 10 | "scripts": { 11 | "test": "NODE_ENV=\"test\" mocha -r ts-node/register", 12 | "test:coverage": "nyc --reporter=text -e .ts -x \"**/*.test.ts\" npm run test", 13 | "compile": "./node_modules/pkg/lib-es5/bin.js -c package.json src/dist/index.js --targets node6", 14 | "lint": "./node_modules/.bin/eslint ./src", 15 | "lintfix": "./node_modules/.bin/eslint ./src --fix", 16 | "jsdoc": "./node_modules/.bin/jsdoc ./src/", 17 | "build": "tsc", 18 | "postbuild": "node src/postbuild.js" 19 | }, 20 | "bin": { 21 | "mdb": "src/dist/index.js" 22 | }, 23 | "author": "MDBootstrap.com", 24 | "license": "ISC", 25 | "devDependencies": { 26 | "@babel/core": "^7.2.2", 27 | "@babel/preset-env": "^7.3.1", 28 | "@types/archiver": "^5.1.0", 29 | "@types/atob": "^2.1.2", 30 | "@types/btoa": "^1.2.3", 31 | "@types/chai": "^4.2.17", 32 | "@types/fs-extra": "^9.0.11", 33 | "@types/inquirer": "^7.3.1", 34 | "@types/mocha": "^8.2.2", 35 | "@types/node": "^15.0.1", 36 | "@types/progress": "^2.0.3", 37 | "@types/sinon": "^10.0.0", 38 | "@types/sinon-chai": "^3.2.5", 39 | "@types/unzip-stream": "^0.3.4", 40 | "chai": "^4.2.0", 41 | "eslint": "^6.1.0", 42 | "gulp": "^4.0.0", 43 | "gulp-babel": "^8.0.0", 44 | "jsdoc": "^3.6.6", 45 | "mocha": "^6.1.1", 46 | "nyc": "^13.3.0", 47 | "pkg": "^4.3.5", 48 | "sinon": "^7.3.1", 49 | "sinon-chai": "^3.5.0", 50 | "ts-node": "^9.1.1", 51 | "type-fest": "^1.0.2", 52 | "typescript": "^4.2.4" 53 | }, 54 | "dependencies": { 55 | "archiver": "^3.0.0", 56 | "atob": "^2.1.2", 57 | "babel-polyfill": "^6.26.0", 58 | "btoa": "^1.2.1", 59 | "clipboardy": "^2.1.0", 60 | "console.table": "^0.10.0", 61 | "dotenv": "^8.0.0", 62 | "fs-extra": "^8.0.1", 63 | "inquirer": "^6.2.1", 64 | "open": "^7.3.0", 65 | "ora": "^3.4.0", 66 | "progress": "^2.0.3", 67 | "socket.io-client": "^4.1.3", 68 | "unzip-stream": "^0.3.1" 69 | }, 70 | "pkg": { 71 | "assets": [ 72 | "dist/commands/*.js", 73 | "dist/config/*.js", 74 | "dist/helpers/*.js", 75 | "dist/models/*.js", 76 | "dist/utils/*.js" 77 | ] 78 | }, 79 | "nyc": { 80 | "exclude": [ 81 | "test/", 82 | "config/" 83 | ] 84 | }, 85 | "mocha": { 86 | "spec": "./src/test/**/*.ts", 87 | "recursive": true, 88 | "exit": true 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/commands/command.ts: -------------------------------------------------------------------------------- 1 | import { join, parse } from 'path'; 2 | import { existsSync, readFileSync, writeFileSync } from 'fs'; 3 | import OutputPrinter from '../utils/output-printer'; 4 | import CommandResult from '../utils/command-result'; 5 | import HttpWrapper from '../utils/http-wrapper'; 6 | import { OutputColor } from '../models'; 7 | import Context from '../context'; 8 | import helpers from '../helpers'; 9 | import config from '../config'; 10 | 11 | abstract class Command { 12 | 13 | public args: string[]; 14 | public entity: string; 15 | public flags: string[]; 16 | public results: CommandResult = new CommandResult(); 17 | 18 | private _output: OutputPrinter = new OutputPrinter(); 19 | 20 | protected constructor(protected context: Context) { 21 | this.args = context.args; 22 | this.entity = context.entity; 23 | this.flags = context.rawFlags; 24 | } 25 | 26 | abstract execute(): void | Promise; 27 | 28 | /** 29 | * @param results: CommandResult[] 30 | */ 31 | async printResult(results: CommandResult[]) { 32 | this._output.print(results); 33 | if (this.context.serverMessageLast) await this._printAdditionalMessage(); 34 | } 35 | 36 | private async _printAdditionalMessage() { 37 | const lastMsg = existsSync(config.msgPath) ? readFileSync(config.msgPath, 'utf8') : ''; 38 | if (lastMsg !== this.context.serverMessageLast) { 39 | writeFileSync(config.msgPath, this.context.serverMessageLast, 'utf8'); 40 | try { 41 | const res = await new HttpWrapper().get({ hostname: config.host, path: '/app/message' }); 42 | const msg = JSON.parse(res.body); 43 | const result = new CommandResult(); 44 | result.addAlert(OutputColor.Yellow, msg.title, msg.body); 45 | this._output.print([result]); 46 | } catch {} 47 | } 48 | } 49 | 50 | async requireDotMdb() { 51 | let cwd = process.cwd(); 52 | const requiredPath = join(cwd, '.mdb'); 53 | const { root } = parse(requiredPath); 54 | 55 | while (true) { 56 | 57 | let dotMdbPath = join(cwd, '.mdb'); 58 | if (existsSync(dotMdbPath)) { 59 | if (dotMdbPath !== requiredPath) 60 | throw new Error(`Required .mdb file found at ${dotMdbPath} - please change your current working directory and run the command again.`); 61 | return; 62 | } 63 | cwd = join(cwd, '..') 64 | if (dotMdbPath === join(root, '.mdb')) break; 65 | } 66 | 67 | const confirmed = await helpers.createConfirmationPrompt('Required .mdb file not found. Create?', true); 68 | 69 | if (!confirmed) { 70 | throw new Error('Required .mdb file not found. Probably not an mdb project - please change directory or initialize new project with `mdb init` command.'); 71 | } 72 | 73 | const flags = []; 74 | const isProjectTypeDeterminable = this.entity && config.projectTypes.includes(this.entity); 75 | if (isProjectTypeDeterminable) flags.push('-t', this.entity); 76 | flags.push(...this.flags); 77 | 78 | const context = new Context('config', 'config', ['init'], flags); 79 | 80 | const ConfigCommand = require('./config-command'); 81 | await new ConfigCommand(context).execute(); 82 | this.context.mdbConfig.load(); 83 | } 84 | } 85 | 86 | export default Command; 87 | -------------------------------------------------------------------------------- /src/commands/config-command.ts: -------------------------------------------------------------------------------- 1 | import Command from './command'; 2 | import ConfigReceiver from '../receivers/config-receiver'; 3 | import DatabaseReceiver from '../receivers/database-receiver'; 4 | import Entity from '../models/entity'; 5 | import Context from '../context'; 6 | 7 | class ConfigCommand extends Command { 8 | 9 | private receiver: ConfigReceiver | DatabaseReceiver | null = null; 10 | 11 | constructor(protected readonly context: Context) { 12 | super(context); 13 | 14 | this.args = context.args; 15 | this.receiver = null; 16 | } 17 | 18 | async execute(): Promise { 19 | 20 | await this.setReceiver(); 21 | 22 | if (this.args.length === 0) { 23 | return this.help(); 24 | } 25 | 26 | if (this.receiver) { 27 | await this.receiver.changeConfig(); 28 | this.printResult([this.receiver.result]); 29 | } else { 30 | this.help(); 31 | } 32 | } 33 | 34 | async setReceiver() { 35 | 36 | switch (this.entity) { 37 | case Entity.Config: 38 | if (this.args[0] !== 'init') await this.requireDotMdb(); 39 | this.receiver = new ConfigReceiver(this.context); 40 | break; 41 | 42 | case Entity.Database: 43 | this.receiver = new DatabaseReceiver(this.context); 44 | break; 45 | } 46 | } 47 | 48 | help(): void { 49 | 50 | this.results.addTextLine('Configuration'); 51 | this.results.addTextLine('\nUsage: mdb [entity] config [options]'); 52 | this.results.addTextLine('\nAvailable entities: config (default), database'); 53 | this.results.addTextLine('\nFlags:'); 54 | this.results.addTextLine(' --unset \tUnset option'); 55 | this.results.addTextLine('\nAvailable options to configure for projects: projectName, domain, publishMethod (ftp, pipeline), packageManager (npm, yarn)'); 56 | this.results.addTextLine('\nAvailable options to configure for databases: password'); 57 | this.printResult([this.results]); 58 | } 59 | } 60 | 61 | module.exports = ConfigCommand; 62 | export default ConfigCommand; 63 | -------------------------------------------------------------------------------- /src/commands/create-command.ts: -------------------------------------------------------------------------------- 1 | import InitCommand from './init-command'; 2 | 3 | class CreateCommand extends InitCommand { } 4 | 5 | module.exports = CreateCommand; 6 | export default CreateCommand; 7 | -------------------------------------------------------------------------------- /src/commands/delete-command.ts: -------------------------------------------------------------------------------- 1 | import Context from '../context'; 2 | import Command from './command'; 3 | import Entity from '../models/entity'; 4 | import CommandResult from '../utils/command-result'; 5 | import BackendReceiver from '../receivers/backend-receiver'; 6 | import DatabaseReceiver from '../receivers/database-receiver'; 7 | import WordpressReceiver from '../receivers/wordpress-receiver'; 8 | import FrontendReceiver from '../receivers/frontend-receiver'; 9 | 10 | class DeleteCommand extends Command { 11 | 12 | private receiver: BackendReceiver | FrontendReceiver | DatabaseReceiver | WordpressReceiver; 13 | 14 | constructor(context: Context) { 15 | super(context); 16 | 17 | this.receiver = this.getReceiver(); 18 | } 19 | 20 | async execute(): Promise { 21 | 22 | if (this.receiver.flags.help) return this.help(); 23 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 24 | if (this.receiver.flags.all || this.receiver.flags.many || this.context.args.length > 0) await this.receiver.deleteMany(); 25 | else await this.receiver.delete(); 26 | this.printResult([this.receiver.result]); 27 | } 28 | 29 | getReceiver(): BackendReceiver | FrontendReceiver | DatabaseReceiver | WordpressReceiver { 30 | switch (this.entity) { 31 | case Entity.Backend: return new BackendReceiver(this.context); 32 | case Entity.Frontend: return new FrontendReceiver(this.context); 33 | case Entity.Database: return new DatabaseReceiver(this.context); 34 | case Entity.Wordpress: return new WordpressReceiver(this.context); 35 | default: return new FrontendReceiver(this.context); 36 | } 37 | } 38 | 39 | help(): void { 40 | 41 | this.results.addTextLine('Remove your project from the remote server.'); 42 | this.results.addTextLine('Note: If you are using our MDB Go pipeline, your project will still exist as the GitLab repository. The Jenkins job will also remain untouched. However, if you are not using our CI/CD setup, you will have only your local copy of the project available after running this command.'); 43 | this.results.addTextLine('\nUsage: mdb [entity] delete'); 44 | this.results.addTextLine('\nAvailable entities: frontend, backend, wordpress, database'); 45 | this.results.addTextLine('\nFlags:'); 46 | this.results.addTextLine(' -n, --name \tProject name'); 47 | this.results.addTextLine(' --ftp-only \tRemove project files only from ftp server'); 48 | this.results.addTextLine(' --force \tDo not require confirmation of deletion by entering the name again'); 49 | this.printResult([this.results]); 50 | } 51 | } 52 | 53 | module.exports = DeleteCommand; 54 | export default DeleteCommand; 55 | -------------------------------------------------------------------------------- /src/commands/destroy-command.ts: -------------------------------------------------------------------------------- 1 | import KillCommand from './kill-command'; 2 | 3 | class DestroyCommand extends KillCommand { } 4 | 5 | module.exports = DestroyCommand; 6 | export default DestroyCommand; 7 | -------------------------------------------------------------------------------- /src/commands/get-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import Receiver from "../receivers/receiver"; 3 | import FrontendReceiver from "../receivers/frontend-receiver"; 4 | import BackendReceiver from "../receivers/backend-receiver"; 5 | import WordpressReceiver from "../receivers/wordpress-receiver"; 6 | import CommandResult from "../utils/command-result"; 7 | import Entity from "../models/entity"; 8 | import Context from "../context"; 9 | 10 | class GetCommand extends Command { 11 | 12 | private receiver: FrontendReceiver | BackendReceiver | WordpressReceiver | null = null; 13 | 14 | constructor(protected readonly context: Context) { 15 | super(context); 16 | 17 | this.setReceiver(); 18 | } 19 | 20 | async execute(): Promise { 21 | 22 | if (this.receiver) { 23 | 24 | if (this.receiver.flags.help) return this.help(); 25 | 26 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 27 | await this.receiver.get(); 28 | 29 | this.printResult([this.receiver.result]); 30 | } else { 31 | await this.detectReceiver(); 32 | if (!this.receiver) return this.help(); 33 | 34 | this.receiver!.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 35 | await this.receiver!.get(); 36 | 37 | this.printResult([this.receiver!.result]); 38 | } 39 | } 40 | 41 | setReceiver(): void { 42 | 43 | switch (this.entity) { 44 | 45 | case Entity.Backend: 46 | this.receiver = new BackendReceiver(this.context); 47 | break; 48 | 49 | case Entity.Frontend: 50 | this.receiver = new FrontendReceiver(this.context); 51 | break; 52 | 53 | case Entity.Wordpress: 54 | this.receiver = new WordpressReceiver(this.context); 55 | break; 56 | 57 | default: 58 | break; 59 | } 60 | } 61 | 62 | async detectReceiver(): Promise { 63 | this.entity = await Receiver.detectEntity(this.context); 64 | this.setReceiver(); 65 | } 66 | 67 | help(): void { 68 | this.results.addTextLine('Clone your project into the local machine.'); 69 | this.results.addTextLine('If your project has repo connected it will download project from git server. Otherwise it will download latest version from FTP.'); 70 | this.results.addTextLine('\nUsage: mdb [entity] get [args]'); 71 | this.results.addTextLine('\nAvailable entities: frontend, backend, wordpress'); 72 | this.results.addTextLine('\nFlags:'); 73 | this.results.addTextLine(' -n, --name \tProject name'); 74 | this.results.addTextLine(' --ftp \tDownload from FTP server'); 75 | this.printResult([this.results]); 76 | } 77 | } 78 | 79 | module.exports = GetCommand; 80 | export default GetCommand; 81 | -------------------------------------------------------------------------------- /src/commands/help-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import AppReceiver from "../receivers/app-receiver"; 3 | import Context from "../context"; 4 | 5 | class HelpCommand extends Command { 6 | 7 | private receiver: AppReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new AppReceiver(context); 13 | } 14 | 15 | execute(): void { 16 | this.receiver.getHelp(); 17 | this.printResult([this.receiver.result]); 18 | } 19 | } 20 | 21 | module.exports = HelpCommand; 22 | export default HelpCommand; 23 | -------------------------------------------------------------------------------- /src/commands/info-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import Receiver from "../receivers/receiver"; 3 | import BackendReceiver from "../receivers/backend-receiver"; 4 | import DatabaseReceiver from "../receivers/database-receiver"; 5 | import WordpressReceiver from "../receivers/wordpress-receiver"; 6 | import CommandResult from "../utils/command-result"; 7 | import Entity from "../models/entity"; 8 | import Context from "../context"; 9 | 10 | class InfoCommand extends Command { 11 | 12 | private receiver: BackendReceiver | DatabaseReceiver | WordpressReceiver | null = null; 13 | 14 | constructor(protected readonly context: Context) { 15 | super(context); 16 | } 17 | 18 | async execute(): Promise { 19 | 20 | await this.setReceiver(); 21 | 22 | if (this.receiver) { 23 | 24 | if (this.receiver.flags.help) return this.help(); 25 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 26 | await this.receiver.info(); 27 | this.printResult([this.receiver.result]); 28 | } else { 29 | await this.detectReceiver(); 30 | if (!this.receiver) return this.help(); 31 | 32 | this.receiver!.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 33 | await this.receiver!.info(); 34 | 35 | this.printResult([this.receiver!.result]); 36 | } 37 | } 38 | 39 | setReceiver(): void { 40 | 41 | switch (this.entity) { 42 | 43 | case Entity.Backend: 44 | this.receiver = new BackendReceiver(this.context); 45 | break; 46 | 47 | case Entity.Database: 48 | this.receiver = new DatabaseReceiver(this.context); 49 | break; 50 | 51 | case Entity.Wordpress: 52 | this.receiver = new WordpressReceiver(this.context); 53 | break; 54 | 55 | default: 56 | break; 57 | } 58 | } 59 | 60 | async detectReceiver(): Promise { 61 | this.entity = await Receiver.detectEntity(this.context); 62 | await this.setReceiver(); 63 | } 64 | 65 | help(): void { 66 | this.results.addTextLine('Displays info about entity (current status).'); 67 | this.results.addTextLine('\nUsage: mdb [entity] info [args]'); 68 | this.results.addTextLine('\nAvailable entities: database, backend, wordpress'); 69 | this.results.addTextLine('\nFlags:'); 70 | this.results.addTextLine(' -n, --name \tProject name'); 71 | this.printResult([this.results]); 72 | } 73 | } 74 | 75 | module.exports = InfoCommand; 76 | export default InfoCommand; 77 | -------------------------------------------------------------------------------- /src/commands/kill-command.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Context from "../context"; 4 | import Command from "./command"; 5 | import BackendReceiver from "../receivers/backend-receiver"; 6 | import WordpressReceiver from "../receivers/wordpress-receiver"; 7 | import Entity from "../models/entity"; 8 | import CommandResult from "../utils/command-result"; 9 | 10 | class KillCommand extends Command { 11 | 12 | private receiver!: BackendReceiver | WordpressReceiver; 13 | constructor(context: Context) { 14 | super(context); 15 | 16 | this.setReceiver(context); 17 | } 18 | 19 | async execute() { 20 | 21 | if (this.receiver) { 22 | 23 | if (this.receiver.flags.help) return this.help(); 24 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 25 | await this.receiver.kill(); 26 | this.printResult([this.receiver.result]); 27 | 28 | } else { 29 | 30 | this.help(); 31 | } 32 | } 33 | 34 | setReceiver(ctx: Context) { 35 | 36 | switch (this.entity) { 37 | 38 | case Entity.Backend: 39 | this.receiver = new BackendReceiver(ctx); 40 | break; 41 | 42 | case Entity.Wordpress: 43 | this.receiver = new WordpressReceiver(ctx); 44 | break; 45 | 46 | default: 47 | break; 48 | } 49 | } 50 | 51 | help(): void { 52 | this.results.addTextLine('Stop a project.'); 53 | this.results.addTextLine('\nUsage: mdb [entity] kill'); 54 | this.results.addTextLine('\nAvailable entities: backend (default), wordpress'); 55 | this.results.addTextLine('\nFlags:'); 56 | this.results.addTextLine(' -n, --name \tProject name'); 57 | this.results.addTextLine(' -rm, --remove \tRemove the container'); 58 | this.printResult([this.results]); 59 | } 60 | } 61 | 62 | module.exports = KillCommand; 63 | export default KillCommand; 64 | -------------------------------------------------------------------------------- /src/commands/login-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import UserReceiver from "../receivers/user-receiver"; 3 | import Context from "../context"; 4 | 5 | class LoginCommand extends Command { 6 | 7 | private receiver: UserReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new UserReceiver(context); 13 | } 14 | 15 | async execute(): Promise { 16 | if (this.receiver.flags.help) return this.help(); 17 | await this.receiver.login(); 18 | this.printResult([this.receiver.result]); 19 | } 20 | 21 | help(): void { 22 | 23 | this.results.addTextLine('Log in to your MDB account'); 24 | this.results.addTextLine('\nUsage: mdb [entity] login [options]'); 25 | this.results.addTextLine('\nAvailable entities: user (default)'); 26 | this.results.addTextLine('\nFlags:'); 27 | this.results.addTextLine(' -u, --username \tUsername'); 28 | this.results.addTextLine(' -p, --password \tPassword'); 29 | this.results.addTextLine(' -m, --method \tSign in using social media. Possible values: google, facebook, twitter'); 30 | this.printResult([this.results]); 31 | } 32 | } 33 | 34 | module.exports = LoginCommand; 35 | export default LoginCommand; 36 | -------------------------------------------------------------------------------- /src/commands/logout-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import UserReceiver from "../receivers/user-receiver"; 3 | import Context from "../context"; 4 | 5 | class LogoutCommand extends Command { 6 | 7 | private receiver: UserReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new UserReceiver(context); 13 | } 14 | 15 | async execute(): Promise { 16 | if (this.receiver.flags.help) return this.help(); 17 | await this.receiver.logout(); 18 | this.printResult([this.receiver.result]); 19 | } 20 | 21 | help(): void { 22 | this.results.addTextLine('Log out from MDB CLI'); 23 | this.results.addTextLine('\nUsage: mdb [entity] logout'); 24 | this.results.addTextLine('\nAvailable entities: user (default)'); 25 | this.printResult([this.results]); 26 | } 27 | } 28 | 29 | module.exports = LogoutCommand; 30 | export default LogoutCommand; 31 | -------------------------------------------------------------------------------- /src/commands/logs-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import BackendReceiver from "../receivers/backend-receiver"; 3 | import WordpressReceiver from "../receivers/wordpress-receiver"; 4 | import CommandResult from "../utils/command-result"; 5 | import Entity from "../models/entity"; 6 | import Context from "../context"; 7 | 8 | class LogsCommand extends Command { 9 | 10 | private receiver: BackendReceiver | WordpressReceiver | null = null; 11 | 12 | constructor(context: Context) { 13 | super(context); 14 | 15 | this.receiver = null; 16 | 17 | this.setReceiver(context); 18 | } 19 | 20 | async execute(): Promise { 21 | 22 | if (this.receiver) { 23 | 24 | if (this.receiver.flags.help) return this.help(); 25 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 26 | await this.receiver.logs(); 27 | this.printResult([this.receiver.result]); 28 | 29 | } else { 30 | 31 | this.help(); 32 | } 33 | } 34 | 35 | setReceiver(ctx: Context): void { 36 | 37 | switch (this.entity) { 38 | 39 | case Entity.Backend: 40 | this.receiver = new BackendReceiver(ctx); 41 | break; 42 | 43 | case Entity.Wordpress: 44 | this.receiver = new WordpressReceiver(ctx); 45 | break; 46 | 47 | default: 48 | break; 49 | } 50 | } 51 | 52 | help(): void { 53 | 54 | this.results.addTextLine('Display logs of a given project.'); 55 | this.results.addTextLine('\nUsage: mdb [entity] logs'); 56 | this.results.addTextLine('\nAvailable entities: backend, wordpress'); 57 | this.results.addTextLine('\nFlags:'); 58 | this.results.addTextLine(' -n, --name \tProject name'); 59 | this.results.addTextLine(' -f, --follow \tOutput new log lines as they appear (live)'); 60 | this.results.addTextLine(' --lines X, --tail X \tShow X last log lines'); 61 | this.printResult([this.results]); 62 | } 63 | } 64 | 65 | module.exports = LogsCommand; 66 | export default LogsCommand; 67 | -------------------------------------------------------------------------------- /src/commands/orders-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import OrderReceiver from "../receivers/order-receiver"; 3 | import Context from "../context"; 4 | 5 | class OrdersCommand extends Command { 6 | 7 | private receiver: OrderReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new OrderReceiver(context); 13 | } 14 | 15 | async execute(): Promise { 16 | await this.receiver.list(); 17 | this.printResult([this.receiver.result]); 18 | } 19 | } 20 | 21 | module.exports = OrdersCommand; 22 | export default OrdersCommand; 23 | -------------------------------------------------------------------------------- /src/commands/publish-command.ts: -------------------------------------------------------------------------------- 1 | import Context from "../context"; 2 | import Command from "./command"; 3 | import BackendReceiver from "../receivers/backend-receiver"; 4 | import ComposeReceiver from "../receivers/compose-receiver"; 5 | import FrontendReceiver from "../receivers/frontend-receiver"; 6 | import WordpressReceiver from "../receivers/wordpress-receiver"; 7 | import CommandResult from "../utils/command-result"; 8 | import Entity from "../models/entity"; 9 | import config from "../config"; 10 | 11 | class PublishCommand extends Command { 12 | 13 | private receiver!: FrontendReceiver | BackendReceiver | ComposeReceiver | WordpressReceiver; 14 | 15 | constructor(protected context: Context) { 16 | super(context); 17 | } 18 | 19 | async execute(): Promise { 20 | 21 | await this.setReceiver(); 22 | 23 | const flags = this.context.getParsedFlags(); 24 | if (flags.help) return this.help(); 25 | 26 | await this.receiver.publish(); 27 | this.printResult([this.receiver.result]); 28 | } 29 | 30 | async setReceiver() { 31 | 32 | await this.requireDotMdb(); 33 | 34 | if (!this.entity) { 35 | const type = this.context.mdbConfig.getValue('meta.type'); 36 | if (type) { 37 | this.entity = type; 38 | this.context.entity = type; 39 | } 40 | } 41 | 42 | switch (this.entity) { 43 | 44 | case Entity.Backend: 45 | this.receiver = new BackendReceiver(this.context); 46 | break; 47 | 48 | case Entity.Compose: 49 | this.receiver = new ComposeReceiver(this.context); 50 | break; 51 | 52 | case Entity.Wordpress: 53 | this.receiver = new WordpressReceiver(this.context); 54 | break; 55 | 56 | case Entity.Frontend: 57 | this.receiver = new FrontendReceiver(this.context); 58 | break; 59 | 60 | default: 61 | this.context.entity = Entity.Frontend; 62 | this.receiver = new FrontendReceiver(this.context); 63 | break; 64 | } 65 | 66 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 67 | } 68 | 69 | help(): void { 70 | this.results.addTextLine('Upload your current project to our remote server'); 71 | this.results.addTextLine('\nUsage: mdb [entity] publish'); 72 | this.results.addTextLine('\nAvailable entities: frontend (default), backend, wordpress'); 73 | this.results.addTextLine('\nFlags:'); 74 | this.results.addTextLine(` -p, --platform \tSpecify the backend platform. Allowed options: ${config.backend.technologies.join(', ')}`); 75 | this.results.addTextLine(' -t, --test \tRun the "test" script defined in the "package.json" file before publishing'); 76 | this.results.addTextLine(' -o, --open \tOpen in default browser after publication'); 77 | this.results.addTextLine(' -c, --advanced \tPerform an advanced WordPress publication'); 78 | this.results.addTextLine(' --ftp, \tDo not use MDB Go pipeline'); 79 | this.printResult([this.results]); 80 | } 81 | } 82 | 83 | module.exports = PublishCommand; 84 | export default PublishCommand; 85 | -------------------------------------------------------------------------------- /src/commands/register-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import UserReceiver from "../receivers/user-receiver"; 3 | import CommandResult from "../utils/command-result"; 4 | import Context from "../context"; 5 | 6 | class RegisterCommand extends Command { 7 | 8 | private receiver: UserReceiver; 9 | 10 | constructor(context: Context) { 11 | super(context); 12 | 13 | this.receiver = new UserReceiver(context); 14 | } 15 | 16 | async execute(): Promise { 17 | if (this.receiver.flags.help) return this.help(); 18 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 19 | 20 | await this.receiver.register(); 21 | this.printResult([this.receiver.result]); 22 | } 23 | 24 | help(): void { 25 | this.results.addTextLine('Create a new MDB account'); 26 | this.results.addTextLine('\nUsage: mdb [entity] register'); 27 | this.results.addTextLine('\nAvailable entities: user (default)'); 28 | this.printResult([this.results]); 29 | } 30 | } 31 | 32 | module.exports = RegisterCommand; 33 | export default RegisterCommand; 34 | -------------------------------------------------------------------------------- /src/commands/rename-command.ts: -------------------------------------------------------------------------------- 1 | import Command from './command'; 2 | import BackendReceiver from '../receivers/backend-receiver'; 3 | import FrontendReceiver from '../receivers/frontend-receiver'; 4 | import WordpressReceiver from '../receivers/wordpress-receiver'; 5 | import CommandResult from "../utils/command-result"; 6 | import Entity from '../models/entity'; 7 | import Context from "../context"; 8 | 9 | class RenameCommand extends Command { 10 | 11 | private receiver!: BackendReceiver | FrontendReceiver | WordpressReceiver; 12 | 13 | constructor(protected readonly context: Context) { 14 | super(context); 15 | } 16 | 17 | async execute(): Promise { 18 | await this.setReceiver(); 19 | 20 | if (this.receiver.flags.help) return this.help(); 21 | await this.receiver.rename(); 22 | this.printResult([this.receiver.result]); 23 | } 24 | 25 | async setReceiver() { 26 | 27 | await this.requireDotMdb(); 28 | 29 | if (!this.entity) { 30 | const type = this.context.mdbConfig.getValue('meta.type'); 31 | if (type) { 32 | this.entity = type; 33 | this.context.entity = type; 34 | } 35 | } 36 | 37 | switch (this.entity) { 38 | 39 | case Entity.Backend: 40 | this.receiver = new BackendReceiver(this.context); 41 | break; 42 | 43 | case Entity.Frontend: 44 | this.receiver = new FrontendReceiver(this.context); 45 | break; 46 | 47 | case Entity.Wordpress: 48 | this.receiver = new WordpressReceiver(this.context); 49 | break; 50 | 51 | default: 52 | this.context.entity = Entity.Frontend; 53 | this.receiver = new FrontendReceiver(this.context); 54 | break; 55 | } 56 | 57 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 58 | } 59 | 60 | help(): void { 61 | this.results.addTextLine('Change the project name locally and on public server.'); 62 | this.results.addTextLine('\nUsage: mdb [entity] rename'); 63 | this.results.addTextLine('\nAvailable entities: frontend (default), backend, wordpress'); 64 | this.results.addTextLine('\nFlags:'); 65 | this.results.addTextLine(' -n, --name \tProject name'); 66 | this.results.addTextLine(' --new-name \tNew project name'); 67 | this.printResult([this.results]); 68 | } 69 | } 70 | 71 | module.exports = RenameCommand; 72 | export default RenameCommand; 73 | -------------------------------------------------------------------------------- /src/commands/restart-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import BackendReceiver from "../receivers/backend-receiver"; 3 | import WordpressReceiver from "../receivers/wordpress-receiver"; 4 | import CommandResult from "../utils/command-result"; 5 | import Entity from "../models/entity"; 6 | import Context from "../context"; 7 | 8 | class RestartCommand extends Command { 9 | 10 | private receiver: BackendReceiver | WordpressReceiver | null = null; 11 | 12 | constructor(context: Context) { 13 | super(context); 14 | 15 | this.receiver = null; 16 | 17 | this.setReceiver(context); 18 | } 19 | 20 | async execute(): Promise { 21 | 22 | if (this.receiver) { 23 | 24 | if (this.receiver.flags.help) return this.help(); 25 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 26 | await this.receiver.restart(); 27 | this.printResult([this.receiver.result]); 28 | 29 | } else { 30 | 31 | this.help(); 32 | } 33 | } 34 | 35 | setReceiver(ctx: Context): void { 36 | 37 | switch (this.entity) { 38 | 39 | case Entity.Backend: 40 | this.receiver = new BackendReceiver(ctx); 41 | break; 42 | 43 | case Entity.Wordpress: 44 | this.receiver = new WordpressReceiver(ctx); 45 | break; 46 | 47 | default: 48 | break; 49 | } 50 | } 51 | 52 | help(): void { 53 | 54 | this.results.addTextLine('Restart a backend project.'); 55 | this.results.addTextLine('\nUsage: mdb [entity] restart'); 56 | this.results.addTextLine('\nAvailable entities: backend, wordpress'); 57 | this.results.addTextLine('\nFlags:'); 58 | this.results.addTextLine(' -n, --name \tProject name'); 59 | this.printResult([this.results]); 60 | } 61 | } 62 | 63 | module.exports = RestartCommand; 64 | export default RestartCommand; 65 | -------------------------------------------------------------------------------- /src/commands/run-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import BackendReceiver from "../receivers/backend-receiver"; 3 | import WordpressReceiver from "../receivers/wordpress-receiver"; 4 | import Entity from "../models/entity"; 5 | import Context from "../context"; 6 | import CommandResult from "../utils/command-result"; 7 | 8 | class RunCommand extends Command { 9 | 10 | private receiver: BackendReceiver | WordpressReceiver | null = null; 11 | 12 | constructor(context: Context) { 13 | super(context); 14 | 15 | this.setReceiver(context); 16 | } 17 | 18 | async execute(): Promise { 19 | 20 | if (this.receiver) { 21 | 22 | if (this.receiver.flags.help) return this.help(); 23 | this.receiver.result.on('mdb.cli.live.output', (msg: CommandResult) => this.printResult([msg])); 24 | await this.receiver.run(); 25 | this.printResult([this.receiver.result]); 26 | 27 | } else { 28 | 29 | this.help(); 30 | } 31 | } 32 | 33 | setReceiver(ctx: Context): void { 34 | 35 | switch (this.entity) { 36 | 37 | case Entity.Backend: 38 | this.receiver = new BackendReceiver(ctx); 39 | break; 40 | 41 | case Entity.Wordpress: 42 | this.receiver = new WordpressReceiver(ctx); 43 | break; 44 | 45 | default: 46 | break; 47 | } 48 | } 49 | 50 | help(): void { 51 | 52 | this.results.addTextLine('Run a backend project.'); 53 | this.results.addTextLine('\nUsage: mdb [entity] run'); 54 | this.results.addTextLine('\nAvailable entities: backend, wordpress'); 55 | this.results.addTextLine('\nFlags:'); 56 | this.results.addTextLine(' -n, --name \tProject name'); 57 | this.printResult([this.results]); 58 | } 59 | } 60 | 61 | module.exports = RunCommand; 62 | export default RunCommand; 63 | -------------------------------------------------------------------------------- /src/commands/starters-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import StarterReceiver from "../receivers/starter-receiver"; 3 | import Context from "../context"; 4 | 5 | class StartersCommand extends Command { 6 | 7 | private receiver: StarterReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new StarterReceiver(context); 13 | } 14 | 15 | execute(): void { 16 | // TODO: implement 17 | } 18 | } 19 | 20 | module.exports = StartersCommand; 21 | export default StartersCommand; 22 | -------------------------------------------------------------------------------- /src/commands/status-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import AppReceiver from "../receivers/app-receiver"; 3 | import Context from "../context"; 4 | 5 | class StatusCommand extends Command { 6 | 7 | private receiver: AppReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new AppReceiver(context); 13 | } 14 | 15 | async execute(): Promise { 16 | if (this.receiver.flags.help) return this.help(); 17 | await this.receiver.status(); 18 | this.printResult([this.receiver.result]); 19 | } 20 | 21 | help(): void { 22 | this.results.addTextLine('Get MDB GO services status'); 23 | this.results.addTextLine('\nUsage: mdb [entity] status'); 24 | this.results.addTextLine('\nAvailable entities: app (default)'); 25 | this.printResult([this.results]); 26 | } 27 | } 28 | 29 | module.exports = StatusCommand; 30 | export default StatusCommand; -------------------------------------------------------------------------------- /src/commands/update-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import AppReceiver from "../receivers/app-receiver"; 3 | import Context from "../context"; 4 | 5 | class UpdateCommand extends Command { 6 | 7 | private receiver: AppReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new AppReceiver(context); 13 | } 14 | 15 | async execute(): Promise { 16 | if (this.receiver.flags.help) return this.help(); 17 | await this.receiver.updateApp(); 18 | this.printResult([this.receiver.result]); 19 | } 20 | 21 | help(): void { 22 | this.results.addTextLine('Update MDB CLI app to the latest version'); 23 | this.results.addTextLine('\nUsage: mdb [entity] update'); 24 | this.results.addTextLine('\nAvailable entities: user (default)'); 25 | this.printResult([this.results]); 26 | } 27 | } 28 | 29 | module.exports = UpdateCommand; 30 | export default UpdateCommand; 31 | -------------------------------------------------------------------------------- /src/commands/version-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import AppReceiver from "../receivers/app-receiver"; 3 | import Context from "../context"; 4 | 5 | class VersionCommand extends Command { 6 | 7 | private receiver: AppReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new AppReceiver(context); 13 | } 14 | 15 | execute(): void { 16 | if (this.receiver.flags.help) return this.help(); 17 | this.receiver.getVersion(); 18 | this.printResult([this.receiver.result]); 19 | } 20 | 21 | help(): void { 22 | this.results.addTextLine('Check currently installed version of MDB CLI'); 23 | this.results.addTextLine('\nUsage: mdb [entity] version'); 24 | this.results.addTextLine('\nAvailable entities: app (default)'); 25 | this.printResult([this.results]); 26 | } 27 | } 28 | 29 | module.exports = VersionCommand; 30 | export default VersionCommand; 31 | -------------------------------------------------------------------------------- /src/commands/whoami-command.ts: -------------------------------------------------------------------------------- 1 | import Command from "./command"; 2 | import UserReceiver from "../receivers/user-receiver"; 3 | import Context from "../context"; 4 | 5 | class WhoamiCommand extends Command { 6 | 7 | private receiver: UserReceiver; 8 | 9 | constructor(context: Context) { 10 | super(context); 11 | 12 | this.receiver = new UserReceiver(context); 13 | } 14 | 15 | async execute(): Promise { 16 | await this.receiver.whoami(); 17 | this.printResult([this.receiver.result]); 18 | } 19 | } 20 | 21 | module.exports = WhoamiCommand; 22 | export default WhoamiCommand; 23 | -------------------------------------------------------------------------------- /src/config/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { config } from 'dotenv'; 4 | import { homedir } from 'os'; 5 | import { join } from 'path'; 6 | 7 | config({ path: join(__dirname, '..', '.env') }); 8 | 9 | const env = process.env.NODE_ENV || 'prd'; 10 | const gitlabUrl = process.env.GITLAB_URL || 'https://git.mdbgo.com'; 11 | const projectsDomain = process.env.PROJECTS_DOMAIN || 'mdbgo.io'; 12 | const technologies = process.env.BACKEND_TECHNOLOGIES || 'node8,node10,node12,node14,node16,node17,php7.2,php7.3,php7.4,php-laravel,python3.10'; 13 | const databases = process.env.DATABASES || 'mysql8,mongodb'; 14 | const mdbgoPipelinePublicBranch = process.env.MDBGO_PIPELINE_PUBLIC_BRANCH || 'mdbgo/public'; 15 | const memberRoles = process.env.MEMBER_ROLES || 'owner,developer,reporter'; 16 | const host = process.env.HOST || 'apps.mdbootstrap.com'; 17 | const port = process.env.PORT; 18 | const apiPath = typeof process.env.API_PATH === "undefined" ? '/api' : process.env.API_PATH; 19 | 20 | process.env['NODE_NO_WARNINGS'] = '1'; 21 | 22 | export default { 23 | env, 24 | gitlabUrl, 25 | mdbgoPipelinePublicBranch, 26 | projectsDomain, 27 | databases: databases.split(','), 28 | memberRoles: memberRoles.split(','), 29 | tokenFile: '.auth', 30 | tokenDir: join(homedir(), '.mdbcli'), 31 | msgPath: join(homedir(), '.mdbcli', '.msg'), 32 | port, 33 | host, 34 | apiPath, 35 | apiUrl: env === 'dev' ? process.env.API_URL : `https://${host}${apiPath}`, 36 | auth: { 37 | social: { 38 | google: { 39 | url: process.env.AUTH_SOCIAL_GOOGLE_URL 40 | }, 41 | facebook: { 42 | url: process.env.AUTH_SOCIAL_FACEBOOK_URL 43 | }, 44 | twitter: { 45 | url: process.env.AUTH_SOCIAL_TWITTER_URL 46 | } 47 | } 48 | }, 49 | backend: { 50 | technologies: technologies.split(','), 51 | requiredFiles: [ 52 | { tech: 'node', name: 'package.json' }, 53 | { tech: 'laravel', name: 'composer.json' }, 54 | { tech: 'python', name: 'requirements.txt' } 55 | ] 56 | }, 57 | projectTypes: ['frontend', 'backend', 'wordpress'] 58 | }; 59 | -------------------------------------------------------------------------------- /src/gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const babel = require('gulp-babel'); 3 | const fs = require('fs'); 4 | 5 | gulp.task('transpile', () => 6 | gulp.src(['./index.js', './context.js', './command-invoker.js', './commands/*', './config/*', './helpers/*', './models/*', './receivers/**/*', './utils/**/*'], { base: '.' }) 7 | .pipe(babel({ 8 | presets: ['@babel/env'] 9 | })) 10 | .pipe(gulp.dest('dist')) 11 | ); 12 | 13 | gulp.task('copy-package-json', async () => { 14 | 15 | const { deserializeJsonFile } = require('./helpers/deserialize-json-file'); 16 | const fileContent = await deserializeJsonFile('../package.json'); 17 | delete fileContent.devDependencies; 18 | delete fileContent.pkg; 19 | delete fileContent.nyc; 20 | delete fileContent.scripts['test:coverage']; 21 | delete fileContent.scripts.compile; 22 | fileContent.scripts.test = 'echo "Error: no test specified" && exit 1'; 23 | fileContent.bin.mdb = './index.js'; 24 | fs.writeFileSync('dist/package.json', JSON.stringify(fileContent, null, 2)); 25 | }); 26 | 27 | gulp.task('copy-readme', async () => { 28 | 29 | try { 30 | 31 | fs.copyFileSync('../README.md', './dist/README.md'); 32 | } catch (e) { 33 | 34 | if (e.code === 'ENOENT') { 35 | 36 | console.log('Either source or destination file does not exist, skipping...'); 37 | } else { 38 | 39 | throw e; 40 | } 41 | } 42 | }); 43 | 44 | gulp.task('copy-env', async () => { 45 | 46 | fs.copyFileSync('../.env', './dist/.env'); 47 | }); 48 | 49 | gulp.task('build', gulp.series('transpile', 'copy-package-json', 'copy-readme', 'copy-env')); 50 | -------------------------------------------------------------------------------- /src/helpers/archiver-wrapper.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import archiver, { Archiver, ArchiverOptions, Format } from 'archiver'; 4 | 5 | export function archiveProject(format: Format, options: ArchiverOptions): Archiver { 6 | 7 | return archiver(format, options); 8 | } 9 | -------------------------------------------------------------------------------- /src/helpers/create-checkbox-prompt.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import inquirer from 'inquirer'; 4 | 5 | export async function createCheckboxPrompt(message: string, choices: Object[]): Promise { 6 | 7 | const result = await inquirer.createPromptModule()([{ 8 | type: 'checkbox', 9 | name: 'name', 10 | message, 11 | choices 12 | }]); 13 | 14 | return result.name; 15 | } 16 | -------------------------------------------------------------------------------- /src/helpers/create-confirmation-prompt.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import inquirer from 'inquirer'; 4 | 5 | export async function createConfirmationPrompt(message: string, defaultValue = true): Promise { 6 | 7 | const result = await inquirer.createPromptModule()([{ 8 | type: 'confirm', 9 | name: 'answer', 10 | message, 11 | default: defaultValue 12 | }]); 13 | 14 | return result.answer; 15 | } 16 | -------------------------------------------------------------------------------- /src/helpers/create-jenkinsfile.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | 6 | export async function createJenkinsfile(cwd: string, simple: boolean): Promise { 7 | 8 | const simpleJenkinsfile = 9 | `pipeline { 10 | agent { 11 | docker { 12 | image 'node:10' 13 | args '-u root:root' 14 | reuseNode true 15 | } 16 | } 17 | stages { 18 | stage('Tests') { 19 | steps { 20 | echo 'Doing nothing...' 21 | } 22 | } 23 | } 24 | }`; 25 | 26 | const noopJenkinsfile = 27 | `pipeline { 28 | agent any 29 | stages { 30 | stage('No-op') { 31 | steps { 32 | echo 'Doing nothing..' 33 | } 34 | } 35 | } 36 | }`; 37 | 38 | const jenkinsfilePath = path.join(cwd, 'Jenkinsfile'); 39 | 40 | if (fs.existsSync(jenkinsfilePath)) { 41 | return false; 42 | } 43 | 44 | fs.writeFileSync(jenkinsfilePath, simple ? simpleJenkinsfile : noopJenkinsfile, 'utf8'); 45 | 46 | return true; 47 | } 48 | -------------------------------------------------------------------------------- /src/helpers/create-list-prompt.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import inquirer from 'inquirer'; 4 | 5 | export async function createListPrompt(message: string, choices: Object[]): Promise { 6 | 7 | const result = await inquirer.createPromptModule()([{ 8 | type: 'list', 9 | name: 'name', 10 | message, 11 | choices 12 | }]); 13 | 14 | return result.name; 15 | } 16 | -------------------------------------------------------------------------------- /src/helpers/create-pass-prompt.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import inquirer from 'inquirer'; 4 | 5 | export async function createPassPrompt(message: string, invalidMessage: string, validate: ((v: string) => boolean) | null = null): Promise { 6 | 7 | const result = await inquirer.createPromptModule()([{ 8 | type: 'password', 9 | message, 10 | name: 'password', 11 | mask: '*', 12 | validate: (value: string) => { 13 | /* istanbul ignore next */ 14 | const valid = validate ? validate(value) : Boolean(value); 15 | /* istanbul ignore next */ 16 | return valid || invalidMessage; 17 | } 18 | }]); 19 | 20 | return result.password; 21 | } 22 | -------------------------------------------------------------------------------- /src/helpers/create-text-prompt.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import inquirer from 'inquirer'; 4 | 5 | export async function createTextPrompt(message: string, invalidMessage: string, validate: ((v: string) => boolean) | null = null): Promise { 6 | 7 | const result = await inquirer.createPromptModule()([{ 8 | type: 'text', 9 | message, 10 | name: 'answer', 11 | validate: (value: string) => { 12 | /* istanbul ignore next */ 13 | const valid = validate ? validate(value) : Boolean(value); 14 | /* istanbul ignore next */ 15 | return valid || invalidMessage; 16 | } 17 | }]); 18 | 19 | return result.answer; 20 | } 21 | -------------------------------------------------------------------------------- /src/helpers/deserialize-json-file.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'fs'; 4 | 5 | export function deserializeJsonFile(filePath: string): Promise { 6 | 7 | return new Promise((resolve, reject) => { 8 | 9 | fs.readFile(filePath, 'utf8', (error: any, content: string) => { 10 | 11 | if (error) { 12 | 13 | return reject(error); 14 | } 15 | 16 | try { 17 | 18 | const result = JSON.parse(content); 19 | 20 | resolve(result); 21 | 22 | } catch (err) { 23 | 24 | reject(err); 25 | } 26 | }); 27 | }); 28 | } 29 | -------------------------------------------------------------------------------- /src/helpers/download-from-ftp.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import HttpWrapper from '../utils/http-wrapper'; 4 | import ProgressBar from 'progress'; 5 | import { Readable } from 'stream'; 6 | import unzip from 'unzip-stream'; 7 | 8 | export function downloadFromFTP(http: HttpWrapper, options: any, destination: string): Promise { 9 | 10 | return new Promise((resolve, reject) => { 11 | 12 | const request = http.createRawRequest(options, (response) => { 13 | 14 | let result = '', message = ''; 15 | 16 | const readStream = new Readable(); 17 | 18 | readStream._read = () => { }; 19 | 20 | const len = Number(response.headers['content-length']); 21 | 22 | const bar = new ProgressBar('[:bar] :eta s', { 23 | complete: '=', 24 | incomplete: ' ', 25 | width: 100, 26 | total: len 27 | }); 28 | 29 | response.on('data', (chunk: any) => { 30 | 31 | if (response.statusCode === 200) { 32 | 33 | readStream.push(chunk); 34 | bar.tick(chunk.length); 35 | 36 | } else { 37 | 38 | message += Buffer.from(chunk).toString('utf8'); 39 | } 40 | }); 41 | 42 | response.on('end', () => { 43 | 44 | const { statusCode, statusMessage } = response; 45 | 46 | if (statusCode === 200) { 47 | 48 | result = 'Download completed.'; 49 | 50 | readStream.push(null); 51 | 52 | } else { 53 | 54 | reject(message || statusMessage); 55 | } 56 | }); 57 | 58 | try { 59 | 60 | readStream.pipe(unzip.Extract({ path: destination })).on('close', () => resolve(result)); 61 | 62 | } catch (e) { 63 | 64 | reject('Download error.'); 65 | } 66 | }); 67 | 68 | request.on('error', reject); 69 | 70 | request.end(); 71 | }); 72 | } 73 | -------------------------------------------------------------------------------- /src/helpers/erase-directories.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'fs'; 4 | import { createConfirmationPrompt } from './create-confirmation-prompt'; 5 | import { removeFolder } from './remove-folder'; 6 | 7 | export async function eraseDirectories(path: string): Promise { 8 | 9 | if (fs.existsSync(path)) { 10 | 11 | const answer = await createConfirmationPrompt(`It will erase data in ${path}. Continue?`); 12 | 13 | if (answer) { 14 | 15 | await removeFolder(path); 16 | 17 | return; 18 | } 19 | 20 | return Promise.reject('OK, will not delete existing folder.'); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/helpers/generate-random-string.ts: -------------------------------------------------------------------------------- 1 | import crypto from "crypto"; 2 | 3 | export function generateRandomString(length = 8) { 4 | 5 | return crypto.randomBytes(Math.ceil(length / 2)) 6 | .toString('hex') 7 | .slice(0, length); 8 | } 9 | -------------------------------------------------------------------------------- /src/helpers/get-theme-name.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export function getThemeName(pageType: string): string { 4 | 5 | switch (pageType) { 6 | case 'wp-essential-blog': 7 | return 'mdb-blog-wordpress-theme'; 8 | case 'wp-essential-ecommerce': 9 | return 'mdb-ecommerce-wordpress-theme'; 10 | case 'wp-essential-blog-ecomm': 11 | return 'mdb-blog-with-ecommerce-wordpress-theme'; 12 | case 'wp-essential-starter': 13 | return 'mdb-sample-wp-theme'; 14 | case 'wp-free-starter': 15 | return 'mdb-sample-free-wp-theme'; 16 | case 'wp-free-empty-starter': 17 | return 'mdb-empty-wp-theme'; 18 | default: 19 | return ''; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/helpers/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { archiveProject } from './archiver-wrapper'; 4 | import { createCheckboxPrompt } from './create-checkbox-prompt'; 5 | import { createConfirmationPrompt } from './create-confirmation-prompt'; 6 | import { createJenkinsfile } from './create-jenkinsfile'; 7 | import { createListPrompt } from './create-list-prompt'; 8 | import { createPassPrompt } from './create-pass-prompt'; 9 | import { createTextPrompt } from './create-text-prompt'; 10 | import { deserializeJsonFile } from './deserialize-json-file'; 11 | import { downloadFromFTP } from './download-from-ftp'; 12 | import { eraseDirectories } from './erase-directories'; 13 | import { getThemeName } from './get-theme-name'; 14 | import { generateRandomString } from './generate-random-string'; 15 | import { removeFolder } from './remove-folder'; 16 | import { serializeJsonFile } from './serialize-json-file'; 17 | 18 | const helpers = { 19 | archiveProject, 20 | createCheckboxPrompt, 21 | createConfirmationPrompt, 22 | createJenkinsfile, 23 | createListPrompt, 24 | createPassPrompt, 25 | createTextPrompt, 26 | deserializeJsonFile, 27 | downloadFromFTP, 28 | eraseDirectories, 29 | generateRandomString, 30 | getThemeName, 31 | removeFolder, 32 | serializeJsonFile 33 | }; 34 | 35 | export default helpers; 36 | -------------------------------------------------------------------------------- /src/helpers/remove-folder.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fse from 'fs-extra'; 4 | 5 | export function removeFolder(path: string): Promise { 6 | 7 | return new Promise((resolve, reject) => { 8 | 9 | fse.remove(path, (err) => { 10 | 11 | if (err) reject(err); 12 | 13 | resolve(); 14 | }); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /src/helpers/serialize-json-file.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'fs'; 4 | 5 | export function serializeJsonFile(filePath: string, object: object): Promise { 6 | 7 | return new Promise((resolve, reject) => { 8 | 9 | const serializedObject = JSON.stringify(object, null, ' '); 10 | 11 | fs.writeFile(filePath, serializedObject, 'utf-8', (error) => { 12 | 13 | error ? reject(error) : resolve(); 14 | }); 15 | }); 16 | } 17 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 'use strict'; 3 | 4 | import CommandInvoker from './command-invoker'; 5 | import CommandResult from './utils/command-result'; 6 | import { OutputColor } from './models/output-color'; 7 | import OutputPrinter from './utils/output-printer'; 8 | 9 | require('babel-polyfill'); 10 | 11 | if (!process.version.match(/v[1-9][0-9]/)) { 12 | 13 | require('console.table'); 14 | } 15 | 16 | class Application { 17 | 18 | public invoker = new CommandInvoker(); 19 | public result = new CommandResult(); 20 | public output = new OutputPrinter(); 21 | 22 | constructor() { 23 | 24 | this.result.on('mdb.cli.live.output', (msg: CommandResult) => { 25 | this.output.print([msg]); 26 | }); 27 | } 28 | 29 | async run(): Promise { 30 | try { 31 | this.invoker.parse(process.argv); 32 | await this.invoker.executeCommand(); 33 | } catch (e) { 34 | this.result.liveAlert(OutputColor.Red, 'Error', `Could not process your request: ${e.message || e}`); 35 | if (process.argv.some((arg: string) => arg === '--debug')) { 36 | console.trace(e); 37 | } 38 | } 39 | } 40 | } 41 | 42 | const app = new Application(); 43 | app.run(); 44 | -------------------------------------------------------------------------------- /src/models/auth-method.ts: -------------------------------------------------------------------------------- 1 | export enum AuthMethod { 2 | Normal = 'normal', 3 | Google = 'google', 4 | Facebook = 'facebook', 5 | Twitter = 'twitter' 6 | } 7 | -------------------------------------------------------------------------------- /src/models/cli-status.ts: -------------------------------------------------------------------------------- 1 | export enum CliStatus { 2 | SUCCESS = 0, 3 | CLI_ERROR = 1, 4 | HTTP_SUCCESS = 200, 5 | CREATED = 201, 6 | SEE_OTHER = 303, 7 | ERROR = 400, 8 | UNAUTHORIZED = 401, 9 | FORBIDDEN = 403, 10 | NOT_FOUND = 404, 11 | CONFLICT = 409, 12 | INTERNAL_SERVER_ERROR = 500 13 | } 14 | -------------------------------------------------------------------------------- /src/models/database.ts: -------------------------------------------------------------------------------- 1 | export interface Database { 2 | databaseId: number, 3 | database: string, 4 | name: string, 5 | username: string, 6 | host: string, 7 | connectionString: string, 8 | description: string 9 | } -------------------------------------------------------------------------------- /src/models/dot-mdb.ts: -------------------------------------------------------------------------------- 1 | import { ProjectEntry } from './project-entry'; 2 | 3 | export interface DotMdb { 4 | meta?: { 5 | starter?: string, 6 | type: string 7 | }, 8 | backend?: { 9 | platform: string 10 | }, 11 | wordpress?: { 12 | email: string, 13 | username: string 14 | }, 15 | projectName?: string, 16 | hash?: string, 17 | domain?: string, 18 | publishMethod?: 'ftp' | 'pipeline', 19 | packageManager?: 'npm' | 'yarn', 20 | compose?: { 21 | projects: ProjectEntry[] 22 | } 23 | } 24 | 25 | export const DOT_MDB_SCHEME: DotMdb = { 26 | backend: { platform: '' }, 27 | compose: { projects: [] }, 28 | domain: '', 29 | meta: { starter: '', type: '' }, 30 | packageManager: 'npm', 31 | projectName: '', 32 | hash: '', 33 | publishMethod: 'ftp', 34 | wordpress: { email: '', username: '' } 35 | }; 36 | 37 | export interface DotMdbGlobal { 38 | packageManager?: 'npm' | 'yarn', 39 | } 40 | 41 | export const DOT_MDB_GLOBAL_SCHEME: DotMdbGlobal = { 42 | packageManager: 'npm' 43 | }; 44 | -------------------------------------------------------------------------------- /src/models/entity.ts: -------------------------------------------------------------------------------- 1 | enum Entity { 2 | App = 'app', 3 | Backend = 'backend', 4 | Blank = 'blank', 5 | Compose = 'compose', 6 | Config = 'config', 7 | Database = 'database', 8 | Frontend = 'frontend', 9 | Order = 'order', 10 | Repo = 'repo', 11 | Starter = 'starter', 12 | User = 'user', 13 | Wordpress = 'wordpress' 14 | } 15 | 16 | export default Entity; 17 | -------------------------------------------------------------------------------- /src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth-method'; 2 | export * from './cli-status'; 3 | export * from './database'; 4 | export * from './project-entry'; 5 | export * from './dot-mdb'; 6 | export * from './order'; 7 | export * from './output-color'; 8 | export * from './output-message'; 9 | export * from './package-json'; 10 | export * from './package-managers'; 11 | export * from './parsed-flags'; 12 | export * from './project-meta'; 13 | export * from './project-status'; 14 | export * from './project'; 15 | export * from './starter-option'; 16 | export * from './subscription-plan'; 17 | export * from './user-plan-data'; -------------------------------------------------------------------------------- /src/models/order.ts: -------------------------------------------------------------------------------- 1 | export interface Order { 2 | postId: number, 3 | postDate: string, 4 | postStatus: string 5 | } -------------------------------------------------------------------------------- /src/models/output-color.ts: -------------------------------------------------------------------------------- 1 | export enum OutputColor { 2 | Red = 'red', 3 | Green = 'green', 4 | Yellow = 'yellow', 5 | Blue = 'blue', 6 | Purple = 'purple', 7 | Turquoise = 'turquoise', 8 | Grey = 'grey', 9 | Inverted = 'inverted', 10 | InvertedRed = 'invertedRed', 11 | InvertedGreen = 'invertedGreen', 12 | InvertedYellow = 'invertedYellow', 13 | InvertedBlue = 'invertedBlue', 14 | InvertedPurple = 'invertedPurple', 15 | InvertedTurquoise = 'invertedTurquoise', 16 | InvertedGrey = 'invertedGrey', 17 | GreyBody = 'greyBody' 18 | } 19 | -------------------------------------------------------------------------------- /src/models/output-message.ts: -------------------------------------------------------------------------------- 1 | import {OutputColor} from "./output-color"; 2 | 3 | export type MessageType = 'text' | 'table' | 'alert'; 4 | export type TextLineMessage = { type: MessageType, value: string }; 5 | export type TableMessage = { type: MessageType, value: object[] }; 6 | export type AlertMessage = { type: MessageType, value: { title: string, body: string }, color: OutputColor }; 7 | -------------------------------------------------------------------------------- /src/models/package-json.ts: -------------------------------------------------------------------------------- 1 | import {PackageJson} from "type-fest"; 2 | 3 | export type MdbGoPackageJson = PackageJson & { domainName?: string }; 4 | -------------------------------------------------------------------------------- /src/models/package-managers.ts: -------------------------------------------------------------------------------- 1 | export enum PackageManagers { 2 | NPM = 'npm', 3 | YARN = 'yarn' 4 | } 5 | -------------------------------------------------------------------------------- /src/models/parsed-flags.ts: -------------------------------------------------------------------------------- 1 | export type ParsedFlags = { 2 | [key: string]: string | boolean; 3 | }; -------------------------------------------------------------------------------- /src/models/project-entry.ts: -------------------------------------------------------------------------------- 1 | export type ProjectEntry = { 2 | type?: string, 3 | path?: string, 4 | technology?: string, 5 | db?: string, 6 | name?: string, 7 | user?: string, 8 | pass?: string, 9 | desc?: string 10 | } 11 | -------------------------------------------------------------------------------- /src/models/project-meta.ts: -------------------------------------------------------------------------------- 1 | export interface ProjectMeta { 2 | metaId: number, 3 | metaKey: string, 4 | metaValue: string 5 | } -------------------------------------------------------------------------------- /src/models/project-status.ts: -------------------------------------------------------------------------------- 1 | export enum ProjectStatus { 2 | BACKEND = 'backend', 3 | CREATED = 'created', 4 | PUBLISHED = 'published', 5 | WORDPRESS = 'wordpress' 6 | } 7 | -------------------------------------------------------------------------------- /src/models/project.ts: -------------------------------------------------------------------------------- 1 | import { ProjectMeta } from './project-meta'; 2 | import { ProjectStatus } from './project-status'; 3 | 4 | export interface Project { 5 | projectId: number, 6 | projectName: string, 7 | domainName: string | null, 8 | publishDate: string, 9 | editDate: string, 10 | repoUrl: string | null, 11 | status: ProjectStatus, 12 | projectMeta: ProjectMeta[] 13 | user: { 14 | userNicename: string, 15 | userLogin: string 16 | }, 17 | collaborationRole: { 18 | name: string 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/models/starter-option.ts: -------------------------------------------------------------------------------- 1 | export type StarterOption = { license: string, category: string, type: string, code: string, available: boolean, displayName: string }; 2 | -------------------------------------------------------------------------------- /src/models/subscription-plan.ts: -------------------------------------------------------------------------------- 1 | export interface SubscriptionPlanInterface { 2 | planId?: number; 3 | planName?: string; 4 | stripePlanId?: string; 5 | price?: number; 6 | bytesContainerRamLimit?: number; 7 | bytesFtpLimit?: number; 8 | bytesDatabaseLimit?: number; 9 | countProjectsLimit?: number; 10 | countDatabasesLimit?: number; 11 | } 12 | -------------------------------------------------------------------------------- /src/models/user-plan-data.ts: -------------------------------------------------------------------------------- 1 | import { SubscriptionPlanInterface } from "./subscription-plan"; 2 | 3 | export interface UserPlanData { 4 | userPlan: SubscriptionPlanInterface, 5 | userPermissions: string[], 6 | projectsCount: number 7 | } -------------------------------------------------------------------------------- /src/models/user-project.ts: -------------------------------------------------------------------------------- 1 | export type UserProject = { 2 | project: { projectId: number }, 3 | role: { name: string }, 4 | user: { userNicename: string, userId: number } 5 | }; 6 | 7 | export type Member = { 8 | Username: string, 9 | Role: string 10 | }; -------------------------------------------------------------------------------- /src/postbuild.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | 3 | deserializeJsonFile('./package.json').then(fileContent => { 4 | delete fileContent.devDependencies; 5 | delete fileContent.pkg; 6 | delete fileContent.nyc; 7 | delete fileContent.mocha; 8 | delete fileContent.scripts['test:coverage']; 9 | delete fileContent.scripts.compile; 10 | fileContent.scripts.test = 'echo "Error: no test specified" && exit 1'; 11 | fileContent.bin.mdb = './index.js'; 12 | fs.writeFileSync('dist/package.json', JSON.stringify(fileContent, null, 2)); 13 | fs.copyFileSync('README.md', 'dist/README.md'); 14 | fs.copyFileSync('.env', 'dist/.env'); 15 | }).catch(err => { 16 | console.error(err); 17 | }); 18 | 19 | function deserializeJsonFile(filePath) { 20 | 21 | return new Promise((resolve, reject) => { 22 | 23 | fs.readFile(filePath, 'utf8', (error, content) => { 24 | 25 | if (error) { 26 | 27 | return reject(error); 28 | } 29 | 30 | try { 31 | 32 | const result = JSON.parse(content); 33 | 34 | resolve(result); 35 | 36 | } catch (err) { 37 | 38 | reject(err); 39 | } 40 | }); 41 | }); 42 | } 43 | -------------------------------------------------------------------------------- /src/receivers/blank-receiver.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import fs from 'fs'; 4 | import path from 'path'; 5 | import { OutputColor } from '../models'; 6 | import Receiver from './receiver'; 7 | import helpers from '../helpers'; 8 | import Context from '../context'; 9 | 10 | class BlankReceiver extends Receiver { 11 | 12 | private projectName: string; 13 | 14 | constructor(context: Context) { 15 | super(context); 16 | 17 | this.projectName = ''; 18 | 19 | this.context.registerFlagExpansions({ '-n': '--name' }); 20 | this.flags = this.context.getParsedFlags(); 21 | } 22 | 23 | async init(): Promise { 24 | 25 | const initInCurrentFolder = this.context.args.some(arg => arg === '.'); 26 | if (initInCurrentFolder && fs.readdirSync(process.cwd()).length !== 0) { 27 | return this.result.addAlert(OutputColor.Red, 'Error', 'Destination path `.` already exists and is not an empty directory.'); 28 | } 29 | let projectPath = process.cwd(); 30 | if (!initInCurrentFolder) { 31 | this.projectName = this.flags.name as string || await helpers.createTextPrompt('Enter project name', 'Project name must be less than 61 characters long and be composed of only small letters, digits and these special characters: - and _', (v: string) => /^[a-z0-9_-]+$/.test(v)); 32 | await this.checkProjectNameExists(); 33 | projectPath = path.join(process.cwd(), this.projectName); 34 | try { 35 | await helpers.eraseDirectories(projectPath); 36 | } catch (err) { 37 | return this.result.addAlert(OutputColor.Red, 'Error', err); 38 | } 39 | fs.mkdirSync(projectPath); 40 | } 41 | const result = await this.createPackageJson(projectPath); 42 | this.context._loadPackageJsonConfig(projectPath); 43 | try { 44 | const simple = !!(this.context.packageJsonConfig.scripts && this.context.packageJsonConfig.scripts.test); 45 | await helpers.createJenkinsfile(projectPath, simple); 46 | } catch (err) { 47 | return this.result.addAlert(OutputColor.Red, 'Error', err); 48 | } 49 | 50 | this.context.mdbConfig.setValue('projectName', this.context.packageJsonConfig.name as string); 51 | this.context.mdbConfig.setValue('hash', helpers.generateRandomString()); 52 | this.context.mdbConfig.save(projectPath); 53 | this.result.addAlert(OutputColor.Green, 'Success', `Your project was initialized in ${projectPath}`); 54 | } 55 | 56 | async checkProjectNameExists(): Promise { 57 | const projectPath = path.join(process.cwd(), this.projectName); 58 | if (fs.existsSync(projectPath)) { 59 | const confirmed = await helpers.createConfirmationPrompt(`Folder ${this.projectName} already exists, do you want to rename project you are creating now?`, true); 60 | if (confirmed) { 61 | this.projectName = await helpers.createTextPrompt('Enter new project name', 'Project name must not be empty.'); 62 | await this.checkProjectNameExists(); 63 | } 64 | } 65 | } 66 | 67 | async createPackageJson(cwd: string): Promise { 68 | await this.context.loadPackageManager(); 69 | return this.context.packageManager!.init(cwd); 70 | } 71 | } 72 | 73 | export default BlankReceiver; 74 | -------------------------------------------------------------------------------- /src/receivers/config-receiver.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import Context from '../context'; 4 | import Receiver from './receiver'; 5 | import { OutputColor } from '../models'; 6 | import { DomainConfigStrategy, InitConfigStrategy, MemberConfigStrategy, NormalConfigStrategy, ProjectNameConfigStrategy } from './strategies/config'; 7 | 8 | class ConfigReceiver extends Receiver { 9 | 10 | private strategy!: DomainConfigStrategy | InitConfigStrategy | MemberConfigStrategy | NormalConfigStrategy | ProjectNameConfigStrategy; 11 | 12 | constructor(context: Context) { 13 | super(context); 14 | 15 | this.context.registerNonArgFlags(['enable-ssl', 'global', 'leave', 'unset', 'list']); 16 | this.context.registerFlagExpansions({ '-ls': '--list', '-n': '--name', '-p': '--platform', '-t': '--type' }); 17 | } 18 | 19 | async changeConfig(): Promise { 20 | const [name, value] = this.context.args; 21 | const flags = this.context.getParsedFlags(); 22 | this.setStrategy(name); 23 | 24 | try { 25 | if (flags.unset) { 26 | const result = await this.strategy.unsetValue(name, value); 27 | this.result.addTextLine(result || `Config key '${name}' has been deleted.`); 28 | } else { 29 | const result = await this.strategy.setValue(name, value); 30 | if (Array.isArray(result)) this.result.addTable(result); 31 | else this.result.addTextLine(result || `Config value '${name}' has been set to '${this.context.mdbConfig.getValue(name)}'.`); 32 | } 33 | } catch (e) { 34 | this.result.addAlert(OutputColor.Red, 'Error', `Could not change config: ${e.message}`); 35 | } 36 | } 37 | 38 | setStrategy(name: string): void { 39 | 40 | switch (name) { 41 | case 'domain': 42 | this.strategy = new DomainConfigStrategy(this.context, this.result); 43 | break; 44 | 45 | case 'init': 46 | this.strategy = new InitConfigStrategy(this.context); 47 | break; 48 | 49 | case 'member': 50 | this.strategy = new MemberConfigStrategy(this.context); 51 | break; 52 | 53 | case 'projectName': 54 | this.strategy = new ProjectNameConfigStrategy(this.context); 55 | break; 56 | 57 | default: 58 | this.strategy = new NormalConfigStrategy(this.context); 59 | break; 60 | } 61 | } 62 | } 63 | 64 | export default ConfigReceiver; 65 | -------------------------------------------------------------------------------- /src/receivers/order-receiver.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../config'; 4 | import Context from '../context'; 5 | import Receiver from './receiver'; 6 | import { Order } from '../models'; 7 | 8 | class OrderReceiver extends Receiver { 9 | 10 | constructor(context: Context) { 11 | super(context); 12 | 13 | this.context.authenticateUser(); 14 | } 15 | 16 | async list(): Promise { 17 | 18 | this.result.liveTextLine('Fetching orders...'); 19 | 20 | const result = await this.http.get({ 21 | hostname: config.host, 22 | path: '/orders/read', 23 | headers: { Authorization: `Bearer ${this.context.userToken}` } 24 | }); 25 | 26 | let orders = JSON.parse(result.body); 27 | 28 | if (orders.length) { 29 | 30 | orders = orders.map((o: Order) => ({ 31 | 'Order ID': o.postId, 32 | 'Order Date': new Date(o.postDate).toLocaleString(), 33 | 'Order Status': o.postStatus.replace('wc-', '') 34 | })); 35 | 36 | this.result.addTable(orders); 37 | 38 | } else { 39 | 40 | this.result.addTextLine('You don\'t have any orders yet.'); 41 | } 42 | } 43 | } 44 | 45 | export default OrderReceiver; -------------------------------------------------------------------------------- /src/receivers/starter-receiver.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../config'; 4 | import Context from '../context'; 5 | import Receiver from './receiver'; 6 | import { OutputColor, StarterOption } from '../models'; 7 | 8 | 9 | class StarterReceiver extends Receiver { 10 | 11 | private loggedin = false; 12 | 13 | constructor(context: Context) { 14 | super(context); 15 | 16 | this.context.authenticateUser(false); 17 | this.loggedin = !!this.context.userToken; 18 | 19 | this.options = { 20 | hostname: config.host, 21 | ...this.loggedin && { headers: { Authorization: `Bearer ${this.context.userToken}` } } 22 | }; 23 | 24 | this.context.registerNonArgFlags(['codes', 'verbose']); 25 | this.context.registerFlagExpansions({ 26 | '-o': '--only', 27 | '-c': '--codes', 28 | '-v': '--verbose', 29 | }); 30 | 31 | this.flags = this.context.getParsedFlags(); 32 | } 33 | 34 | async list(): Promise { 35 | 36 | if (!this.loggedin) 37 | this.result.liveAlert(OutputColor.Yellow, '\nWarning!', 'You are not logged in, it will show only free starters as available.'); 38 | 39 | this.result.liveTextLine('\nFetching starters...'); 40 | 41 | try { 42 | this._validateOnlyFlag(); 43 | 44 | const queryParamType = this.flags.only ? `type=${this.flags.only}` : ''; 45 | const queryParamAvailable = !this.flags.all ? 'available=true' : ''; 46 | const freeStarters = this.loggedin ? '' : '/free'; 47 | 48 | this.options.path = `/packages/starters${freeStarters}?${queryParamType}&${queryParamAvailable}`; 49 | const { body: response } = await this.http.get(this.options); 50 | const starters = JSON.parse(response); 51 | 52 | const available = [], unavailable = []; 53 | for (let i = 0; i < starters.length; i++) { 54 | if (starters[i].available) available.push(starters[i]); 55 | else unavailable.push(starters[i]); 56 | } 57 | 58 | this._printStartersMap(OutputColor.Green, 'Available starters:', this._buildStartersMap(available)); 59 | if (this.flags.all) { 60 | this._printStartersMap(OutputColor.Red, 'Unavailable starters:', this._buildStartersMap(unavailable)); 61 | } 62 | } catch (e) { 63 | return this.result.addAlert(OutputColor.Red, 'Error', `Could not fetch starters: ${e.message}`); 64 | } 65 | } 66 | 67 | private _validateOnlyFlag(): void { 68 | if (!!this.flags.only && !config.projectTypes.includes(this.flags.only as string)) { 69 | throw new Error('Invalid value for --only flag'); 70 | } 71 | } 72 | 73 | private _buildStartersMap(options: StarterOption[]) { 74 | return options.reduce<{ [key: string]: { name: string, short: string, value: string }[] }>((res, curr) => { 75 | res[`${curr.category} ${curr.license}`] = res[`${curr.category} ${curr.license}`] || []; 76 | 77 | res[`${curr.category} ${curr.license}`].push({ 78 | name: curr.displayName, 79 | short: curr.code, 80 | value: curr.code 81 | }); 82 | 83 | return res; 84 | }, {}); 85 | } 86 | 87 | private _printStartersMap(color: OutputColor, header: string, map: any): void { 88 | 89 | this.result.addTextLine(''); 90 | this.result.addAlert(color, header, '\n'); 91 | 92 | for (let key in map) { 93 | this.result.addTextLine(`---- ${key} ----`); 94 | for (let starter of map[key]) { 95 | if (this.flags.codes) this.result.addTextLine(starter.value); 96 | else if (this.flags.verbose) this.result.addTextLine(starter.name + ', code: ' + starter.value); 97 | else this.result.addTextLine(starter.name); 98 | } 99 | 100 | this.result.addTextLine(''); 101 | } 102 | } 103 | 104 | async init(): Promise { 105 | // TODO: implement 106 | this.result.addTextLine('This command is not implemented yet. In order to initialize a project you need to provide an entity, e.x: mdb frontend init'); 107 | } 108 | } 109 | 110 | export default StarterReceiver; -------------------------------------------------------------------------------- /src/receivers/strategies/auth/auth-strategy.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import config from '../../../config'; 4 | 5 | const { tokenDir, tokenFile } = config; 6 | 7 | export abstract class AuthStrategy { 8 | 9 | abstract register(): Promise; 10 | 11 | abstract login(): Promise; 12 | 13 | logout() { 14 | return this.removeToken(); 15 | } 16 | 17 | saveToken(userToken: string): boolean { 18 | 19 | if (userToken) { 20 | 21 | const tokenPath = path.join(tokenDir, tokenFile); 22 | 23 | try { 24 | fs.mkdirSync(tokenDir, { recursive: true, mode: 0o755 }); 25 | fs.writeFileSync(tokenPath, userToken, { encoding: 'utf8', mode: 0o644 }); 26 | } catch (err) { 27 | 28 | return false; 29 | } 30 | 31 | return true; 32 | } 33 | 34 | return false; 35 | } 36 | 37 | removeToken(): null | string { 38 | 39 | const tokenPath = path.join(tokenDir, tokenFile); 40 | 41 | try { 42 | fs.unlinkSync(tokenPath); 43 | 44 | return null; 45 | } catch (e) { 46 | 47 | if (e.code === 'ENOENT') return 'You are not logged in.'; 48 | 49 | return `Logout failed: ${e.message}`; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/receivers/strategies/auth/facebook-auth-strategy.ts: -------------------------------------------------------------------------------- 1 | import { SocialAuthStrategy } from './social-auth-strategy'; 2 | import config from '../../../config'; 3 | 4 | export class FacebookAuthStrategy extends SocialAuthStrategy { 5 | 6 | get socialAuthUrl() { 7 | return config.auth.social.facebook.url as string; 8 | } 9 | 10 | register(): Promise { 11 | return this.login(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/receivers/strategies/auth/google-auth-strategy.ts: -------------------------------------------------------------------------------- 1 | import { SocialAuthStrategy } from './social-auth-strategy'; 2 | import config from '../../../config'; 3 | 4 | export class GoogleAuthStrategy extends SocialAuthStrategy { 5 | 6 | get socialAuthUrl() { 7 | return config.auth.social.google.url as string; 8 | } 9 | 10 | register(): Promise { 11 | return this.login(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/receivers/strategies/auth/index.ts: -------------------------------------------------------------------------------- 1 | export * from './auth-strategy'; 2 | export * from './facebook-auth-strategy'; 3 | export * from './google-auth-strategy'; 4 | export * from './normal-auth-strategy'; 5 | export * from './twitter-auth-strategy'; 6 | -------------------------------------------------------------------------------- /src/receivers/strategies/auth/social-auth-strategy.ts: -------------------------------------------------------------------------------- 1 | import open from 'open'; 2 | import { AuthStrategy } from './auth-strategy'; 3 | import helpers from '../../../helpers'; 4 | 5 | export abstract class SocialAuthStrategy extends AuthStrategy { 6 | 7 | abstract get socialAuthUrl(): string; 8 | 9 | async login(): Promise { 10 | this.openSocialLoginBrowser(this.socialAuthUrl); 11 | const token = await this.askCredentials(); 12 | const saved = this.saveToken(token); 13 | return saved ? '' : 'Login failed. Could not save token.'; 14 | } 15 | 16 | openSocialLoginBrowser(url: string) { 17 | open(url); 18 | } 19 | 20 | async askCredentials() { 21 | return helpers.createTextPrompt('Enter text copied from web browser', 'It cannot be empty.'); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/receivers/strategies/auth/twitter-auth-strategy.ts: -------------------------------------------------------------------------------- 1 | import { SocialAuthStrategy } from './social-auth-strategy'; 2 | import config from '../../../config'; 3 | 4 | export class TwitterAuthStrategy extends SocialAuthStrategy { 5 | 6 | get socialAuthUrl() { 7 | return config.auth.social.twitter.url as string; 8 | } 9 | 10 | register(): Promise { 11 | return this.login(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/receivers/strategies/config/config-strategy.ts: -------------------------------------------------------------------------------- 1 | import { Member } from '../../../models/user-project'; 2 | 3 | abstract class ConfigStrategy { 4 | 5 | abstract setValue(name: string, value: string): string | Promise; 6 | 7 | abstract unsetValue(name: string, value?: string): string | Promise; 8 | } 9 | 10 | export default ConfigStrategy; 11 | -------------------------------------------------------------------------------- /src/receivers/strategies/config/index.ts: -------------------------------------------------------------------------------- 1 | export * from './domain-config-strategy'; 2 | export * from './init-config-strategy'; 3 | export * from './member-config-strategy'; 4 | export * from './normal-config-strategy'; 5 | export * from './project-name-config-strategy'; 6 | -------------------------------------------------------------------------------- /src/receivers/strategies/config/init-config-strategy.ts: -------------------------------------------------------------------------------- 1 | import config from '../../../config'; 2 | import Context from '../../../context'; 3 | import helpers from '../../../helpers'; 4 | import ConfigStrategy from './config-strategy'; 5 | 6 | export class InitConfigStrategy extends ConfigStrategy { 7 | 8 | constructor(private readonly context: Context) { 9 | super(); 10 | } 11 | 12 | async setValue(name: string, value: string) { 13 | const flags = this.context.getParsedFlags(); 14 | 15 | const projectName = flags.name ? flags.name as string : await helpers.createTextPrompt('Enter project name', 'Project name must be less than 61 characters long and be composed of only small letters, digits and these special characters: - and _', (v: string) => /^[a-z0-9_-]+$/.test(v)); 16 | this.context.mdbConfig.setValue('projectName', projectName); 17 | const projectType = flags.type ? flags.type as string : await helpers.createListPrompt('Choose project type', config.projectTypes); 18 | this.context.mdbConfig.setValue('meta.type', projectType); 19 | if (projectType === 'backend') { 20 | const technology = flags.platform && config.backend.technologies.includes(flags.platform as string) ? flags.platform as string : await helpers.createListPrompt('Choose project technology', config.backend.technologies); 21 | this.context.mdbConfig.setValue('backend.platform', technology); 22 | } 23 | if (projectType === 'frontend') { 24 | const packageManager = await helpers.createListPrompt('Choose default package manager', ['npm', 'yarn']); 25 | this.context.mdbConfig.setValue('packageManager', packageManager); 26 | } 27 | this.context.mdbConfig.setValue('hash', helpers.generateRandomString()); 28 | this.context.mdbConfig.save(); 29 | 30 | return 'Configuration saved.'; 31 | } 32 | 33 | unsetValue(name: string): string { 34 | 35 | throw new Error('Invalid flag --unset for `mdb config init` command.'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/receivers/strategies/config/normal-config-strategy.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../../context'; 2 | import ConfigStrategy from "./config-strategy"; 3 | 4 | export class NormalConfigStrategy extends ConfigStrategy { 5 | 6 | constructor(private readonly context: Context) { 7 | super(); 8 | } 9 | 10 | setValue(name: string, value: string): string { 11 | this._validate(name, value); 12 | const global = this._getGlobalFlag(); 13 | this.context.mdbConfig.setValue(name, value, global); 14 | this.context.mdbConfig.save(process.cwd(), global); 15 | return ''; 16 | } 17 | 18 | unsetValue(name: string): string { 19 | const global = this._getGlobalFlag(); 20 | this.context.mdbConfig.unsetValue(name, global); 21 | this.context.mdbConfig.save(process.cwd(), global); 22 | return ''; 23 | } 24 | 25 | private _getGlobalFlag(): boolean { 26 | const flags = this.context.getParsedFlags(); 27 | return flags.global ? flags.global as boolean : false; 28 | } 29 | 30 | private _validate(name: string, value: string): void { 31 | switch (name) { 32 | case 'packageManager': 33 | if (!['npm', 'yarn'].includes(value)) throw new Error('Invalid value, allowed package managers: npm, yarn'); 34 | break; 35 | case 'publishMethod': 36 | if (!['ftp', 'pipeline'].includes(value)) throw new Error('Invalid value, allowed publish methods: ftp, pipeline'); 37 | break; 38 | default: 39 | break; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/receivers/strategies/config/project-name-config-strategy.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../../context'; 2 | import ConfigStrategy from './config-strategy'; 3 | 4 | export class ProjectNameConfigStrategy extends ConfigStrategy { 5 | 6 | constructor(private readonly context: Context) { 7 | super(); 8 | } 9 | 10 | async setValue(name: string, value: string): Promise { 11 | if (this.context.packageJsonConfig && Object.keys(this.context.packageJsonConfig).length > 0) { 12 | this.context.setPackageJsonValue('name', value); 13 | } 14 | 15 | this.context.mdbConfig.setValue(name, value); 16 | this.context.mdbConfig.save(); 17 | 18 | return ''; 19 | } 20 | 21 | unsetValue(name: string): string { 22 | this.context.mdbConfig.unsetValue(name); 23 | this.context.mdbConfig.save(); 24 | 25 | return ''; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/receivers/strategies/publish/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ftp-publish-strategy'; 2 | export * from './pipeline-publish-strategy'; 3 | -------------------------------------------------------------------------------- /src/receivers/strategies/publish/pipeline-publish-strategy.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../../context'; 2 | import CommandResult from '../../../utils/command-result'; 3 | import GitManager from '../../../utils/managers/git-manager'; 4 | import HttpWrapper, { CustomRequestOptions } from '../../../utils/http-wrapper'; 5 | import { OutputColor } from '../../../models'; 6 | import helpers from '../../../helpers'; 7 | import config from '../../../config'; 8 | 9 | export class PipelinePublishStrategy { 10 | 11 | private readonly cwd = process.cwd(); 12 | 13 | constructor( 14 | private readonly context: Context, 15 | private result: CommandResult, 16 | private git: GitManager, 17 | private http: HttpWrapper, 18 | private readonly options: CustomRequestOptions 19 | ) { } 20 | 21 | async publish() { 22 | const currentBranch = await this.git.currentBranch(); 23 | 24 | return this.createJenkinsfile(currentBranch) 25 | .then(() => this.git.status()) 26 | .then(() => this.confirmMerge(currentBranch)) 27 | .then(() => this.git.push(`${currentBranch}:${config.mdbgoPipelinePublicBranch}`)) 28 | .then(() => this.confirmSaveSettings()) 29 | .then(() => this.updateProjectStatus()); 30 | } 31 | 32 | async createJenkinsfile(currentBranch: string) { 33 | 34 | this.result.liveTextLine('Jenkinsfile is required. Creating...'); 35 | const created = await helpers.createJenkinsfile(this.cwd, false); 36 | 37 | if (!created) return; 38 | 39 | this.result.liveTextLine('Jenkinsfile created. Committing...'); 40 | await this.git.commit('Jenkinsfile', 'Add Jenkinsfile'); 41 | 42 | this.result.liveTextLine('Jenkinsfile commited. Pushing...'); 43 | await this.git.push(currentBranch); 44 | 45 | this.result.liveTextLine('Jenkinsfile pushed. Proceeding...'); 46 | } 47 | 48 | async confirmMerge(currentBranch: string) { 49 | 50 | if (currentBranch === config.mdbgoPipelinePublicBranch) return; 51 | 52 | const confirmed = await helpers.createConfirmationPrompt(`Your current branch is ${currentBranch}. Do you want to merge it into ${config.mdbgoPipelinePublicBranch}?`); 53 | if (!confirmed) { 54 | throw new Error('Cannot proceed without merge.'); 55 | } 56 | 57 | await this.git.checkout(config.mdbgoPipelinePublicBranch); 58 | await this.git.pull(config.mdbgoPipelinePublicBranch, false).catch(() => null); 59 | await this.git.merge(currentBranch); 60 | } 61 | 62 | async confirmSaveSettings() { 63 | if (this.context.mdbConfig.getValue('publishMethod')) return; 64 | 65 | const confirm = await helpers.createConfirmationPrompt('Do you want to use GitLab pipelines as a default publish method?'); 66 | if (confirm) { 67 | this.context.mdbConfig.setValue('publishMethod', 'pipeline'); 68 | this.context.mdbConfig.save(); 69 | await this.git.commit('.mdb', 'Update .mdb config'); 70 | } else { 71 | this.result.addAlert(OutputColor.Green, 'Success', 'This time your project will be published using GitLab pipeline. We will remember to ask you again next time.'); 72 | } 73 | } 74 | 75 | updateProjectStatus() { 76 | 77 | const repoUrl = this.git.getCurrentRemoteUrl(); 78 | const domain = this.context.mdbConfig.getValue('domain'); 79 | const projectName = this.context.mdbConfig.getValue('projectName'); 80 | const projectType = this.context.mdbConfig.getValue('meta.type'); 81 | 82 | this.options.data = JSON.stringify({ repoUrl, domain }); 83 | this.options.headers!['Content-Length'] = Buffer.byteLength(this.options.data); 84 | this.options.headers!['Content-Type'] = 'application/json'; 85 | this.options.path = `/project/save/${projectName}`; 86 | 87 | if (projectType === 'backend') { 88 | this.options.headers!['x-mdb-cli-backend-technology'] = this.context.mdbConfig.getValue('backend.platform'); 89 | } else if (projectType === 'wordpress') { 90 | this.options.headers!['x-mdb-cli-wp-starter'] = this.context.mdbConfig.getValue('meta.starter'); 91 | } 92 | 93 | return this.http.post(this.options); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/receivers/user-receiver.ts: -------------------------------------------------------------------------------- 1 | import atob from 'atob'; 2 | import Context from '../context'; 3 | import Receiver from './receiver'; 4 | import { AuthMethod, OutputColor } from '../models'; 5 | import { NormalAuthStrategy, GoogleAuthStrategy, FacebookAuthStrategy, TwitterAuthStrategy } from './strategies/auth'; 6 | 7 | class UserReceiver extends Receiver { 8 | 9 | private socialProvider: AuthMethod; 10 | private authStrategy: NormalAuthStrategy | GoogleAuthStrategy | FacebookAuthStrategy | TwitterAuthStrategy | null = null; 11 | 12 | constructor(context: Context) { 13 | super(context); 14 | 15 | this.socialProvider = AuthMethod.Normal; 16 | this.authStrategy = null; 17 | this.context.registerNonArgFlags(['help']); 18 | this.context.registerFlagExpansions({ 19 | '-u': '--username', 20 | '-p': '--password', 21 | '-m': '--method', 22 | '-h': '--help' 23 | }); 24 | this.flags = this.context.getParsedFlags(); 25 | } 26 | 27 | async register(): Promise { 28 | this.setAuthStrategy(); 29 | 30 | const err = await this.authStrategy!.register(); 31 | if (err) { 32 | this.result.addAlert(OutputColor.Red, 'Error', err); 33 | } else { 34 | this.result.addTextLine('Successfully registered.'); 35 | } 36 | } 37 | 38 | async login(): Promise { 39 | this.setSocialProvider(); 40 | this.setAuthStrategy(); 41 | 42 | const err = await this.authStrategy!.login(); 43 | if (err) { 44 | this.result.addAlert(OutputColor.Red, 'Error', err); 45 | } else { 46 | this.result.addTextLine('Successfully logged in.'); 47 | } 48 | } 49 | 50 | async logout(): Promise { 51 | this.setAuthStrategy(); 52 | 53 | const err = await this.authStrategy!.logout(); 54 | if (err) { 55 | this.result.addAlert(OutputColor.Red, 'Error', err); 56 | } else { 57 | this.result.addTextLine('Successfully logged out.'); 58 | } 59 | } 60 | 61 | async whoami(): Promise { 62 | this.context.authenticateUser(); 63 | 64 | const token = this.context.userToken; 65 | const [, jwtBody] = token.split('.'); 66 | const username = JSON.parse(atob(jwtBody)).name; 67 | 68 | this.result.addTextLine(username); 69 | } 70 | 71 | setSocialProvider(): void { 72 | 73 | const supportedMethods = [AuthMethod.Google, AuthMethod.Facebook, AuthMethod.Twitter, AuthMethod.Normal]; 74 | if (this.flags.method && !supportedMethods.includes(this.flags.method as AuthMethod)) { 75 | throw new Error(`Unsupported --method provided: ${this.flags.method}. Supported methods: ${supportedMethods.join(', ')}`); 76 | } else if (this.flags.method) { 77 | this.socialProvider = this.flags.method as AuthMethod; 78 | } 79 | } 80 | 81 | setAuthStrategy(): NormalAuthStrategy | GoogleAuthStrategy | FacebookAuthStrategy | TwitterAuthStrategy { 82 | 83 | switch (this.socialProvider) { 84 | case AuthMethod.Google: return this.authStrategy = new GoogleAuthStrategy(); 85 | case AuthMethod.Facebook: return this.authStrategy = new FacebookAuthStrategy(); 86 | case AuthMethod.Twitter: return this.authStrategy = new TwitterAuthStrategy(); 87 | case AuthMethod.Normal: return this.authStrategy = new NormalAuthStrategy(this.flags as { [key: string]: string }, this.result); 88 | default: return this.authStrategy = new NormalAuthStrategy(this.flags as { [key: string]: string }, this.result); 89 | } 90 | } 91 | } 92 | 93 | export default UserReceiver; -------------------------------------------------------------------------------- /src/test/commands/config-command.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import Context from '../../context'; 3 | import Command from '../../commands/command'; 4 | import ConfigCommand from '../../commands/config-command'; 5 | import ConfigReceiver from '../../receivers/config-receiver'; 6 | import DatabaseReceiver from '../../receivers/database-receiver'; 7 | import { createSandbox, SinonStub } from 'sinon'; 8 | 9 | describe('Command: config', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let printResultStub: SinonStub; 14 | 15 | beforeEach(() => { 16 | 17 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 18 | sandbox.stub(Context.prototype, 'authenticateUser'); 19 | sandbox.stub(Command.prototype, 'requireDotMdb'); 20 | sandbox.stub(fs, 'existsSync').returns(true); 21 | }); 22 | 23 | afterEach(() => { 24 | 25 | sandbox.reset(); 26 | sandbox.restore(); 27 | }); 28 | 29 | it('should call config receiver changeConfig() method and print result', async () => { 30 | 31 | const configStub = sandbox.stub(ConfigReceiver.prototype, 'changeConfig'); 32 | const context = new Context('config', 'config', ['projectName', 'fakeName'], []); 33 | const command = new ConfigCommand(context); 34 | 35 | await command.execute(); 36 | 37 | sandbox.assert.calledOnce(configStub); 38 | sandbox.assert.calledOnce(printResultStub); 39 | }); 40 | 41 | it('should call database receiver changePassword() method and print result', async () => { 42 | 43 | const changePasswordStub = sandbox.stub(DatabaseReceiver.prototype, 'changePassword'); 44 | const context = new Context('database', 'config', ['password'], []); 45 | const command = new ConfigCommand(context); 46 | 47 | await command.execute(); 48 | 49 | sandbox.assert.calledOnce(changePasswordStub); 50 | sandbox.assert.calledOnce(printResultStub); 51 | }); 52 | 53 | it('should call help method and print result if entity is undefined', async () => { 54 | 55 | const helpSpy = sandbox.spy(ConfigCommand.prototype, 'help'); 56 | const context = new Context('', 'config', ['projectName'], []); 57 | const command = new ConfigCommand(context); 58 | 59 | await command.execute(); 60 | 61 | sandbox.assert.calledOnce(helpSpy); 62 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 63 | }); 64 | 65 | it('should call help method and print result if args are undefined', async () => { 66 | 67 | const helpSpy = sandbox.spy(ConfigCommand.prototype, 'help'); 68 | const context = new Context('config', 'config', [], []); 69 | const command = new ConfigCommand(context); 70 | 71 | await command.execute(); 72 | 73 | sandbox.assert.calledOnce(helpSpy); 74 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/test/commands/help-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import AppReceiver from '../../receivers/app-receiver'; 4 | import HelpCommand from '../../commands/help-command'; 5 | import { createSandbox } from 'sinon'; 6 | 7 | describe('Command: help', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let command: HelpCommand, 12 | context: Context; 13 | 14 | beforeEach(() => { 15 | 16 | context = new Context('app', 'help', [], []); 17 | command = new HelpCommand(context); 18 | }); 19 | 20 | afterEach(() => { 21 | 22 | sandbox.reset(); 23 | sandbox.restore(); 24 | }); 25 | 26 | it('should call receiver getHelp method and print result', () => { 27 | 28 | const getHelpStub = sandbox.stub(AppReceiver.prototype, 'getHelp'); 29 | const printResultStub = sandbox.stub(Command.prototype, 'printResult'); 30 | 31 | command.execute(); 32 | 33 | sandbox.assert.calledOnce(getHelpStub); 34 | sandbox.assert.calledOnce(printResultStub); 35 | }); 36 | }); -------------------------------------------------------------------------------- /src/test/commands/info-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import InfoCommand from '../../commands/info-command'; 4 | import BackendReceiver from '../../receivers/backend-receiver'; 5 | import DatabaseReceiver from '../../receivers/database-receiver'; 6 | import WordpressReceiver from '../../receivers/wordpress-receiver'; 7 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 8 | 9 | describe('Command: info', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let command: InfoCommand, 14 | context: Context, 15 | helpSpy: SinonSpy, 16 | infoStub: SinonStub, 17 | printResultStub: SinonStub; 18 | 19 | beforeEach(() => { 20 | 21 | helpSpy = sandbox.spy(InfoCommand.prototype, 'help'); 22 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 23 | sandbox.stub(Context.prototype, 'authenticateUser'); 24 | }); 25 | 26 | afterEach(() => { 27 | 28 | sandbox.reset(); 29 | sandbox.restore(); 30 | }); 31 | 32 | it('should call backend receiver info method and print result if entity is backend', async () => { 33 | 34 | infoStub = sandbox.stub(BackendReceiver.prototype, 'info'); 35 | context = new Context('backend', 'info', [], []); 36 | command = new InfoCommand(context); 37 | 38 | await command.execute(); 39 | 40 | sandbox.assert.calledOnce(infoStub); 41 | sandbox.assert.calledOnce(printResultStub); 42 | }); 43 | 44 | it('should call database receiver info method and print result if entity is database', async () => { 45 | 46 | infoStub = sandbox.stub(DatabaseReceiver.prototype, 'info'); 47 | context = new Context('database', 'info', [], []); 48 | command = new InfoCommand(context); 49 | 50 | await command.execute(); 51 | 52 | sandbox.assert.calledOnce(infoStub); 53 | sandbox.assert.calledOnce(printResultStub); 54 | }); 55 | 56 | it('should call wordpress receiver info method and print result if entity is wordpress', async () => { 57 | 58 | infoStub = sandbox.stub(WordpressReceiver.prototype, 'info'); 59 | context = new Context('wordpress', 'info', [], []); 60 | command = new InfoCommand(context); 61 | 62 | await command.execute(); 63 | 64 | sandbox.assert.calledOnce(infoStub); 65 | sandbox.assert.calledOnce(printResultStub); 66 | }); 67 | 68 | it('should call help method and print result if --help flag is used', async () => { 69 | 70 | context = new Context('wordpress', 'info', [], ['--help']); 71 | command = new InfoCommand(context); 72 | 73 | await command.execute(); 74 | 75 | sandbox.assert.calledOnce(helpSpy); 76 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 77 | }); 78 | 79 | it('should call help method and print result if entity is undefined', async () => { 80 | 81 | context = new Context('', 'info', [], []); 82 | command = new InfoCommand(context); 83 | 84 | await command.execute(); 85 | 86 | sandbox.assert.calledOnce(helpSpy); 87 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 88 | }); 89 | }); 90 | -------------------------------------------------------------------------------- /src/test/commands/kill-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import KillCommand from '../../commands/kill-command'; 4 | import BackendReceiver from '../../receivers/backend-receiver'; 5 | import WordpressReceiver from '../../receivers/wordpress-receiver'; 6 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 7 | 8 | describe('Command: kill', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | let command: Command, 13 | context: Context, 14 | helpSpy: SinonSpy, 15 | killStub: SinonStub, 16 | printResultStub: SinonStub; 17 | 18 | beforeEach(() => { 19 | 20 | helpSpy = sandbox.spy(KillCommand.prototype, 'help'); 21 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 22 | sandbox.stub(Context.prototype, 'authenticateUser'); 23 | 24 | }); 25 | 26 | afterEach(() => { 27 | 28 | sandbox.reset(); 29 | sandbox.restore(); 30 | }); 31 | 32 | it('should call backend receiver kill method and print result if entity is backend', async () => { 33 | 34 | killStub = sandbox.stub(BackendReceiver.prototype, 'kill'); 35 | context = new Context('backend', 'kill', [], []); 36 | command = new KillCommand(context); 37 | 38 | await command.execute(); 39 | 40 | sandbox.assert.calledOnce(killStub); 41 | sandbox.assert.calledOnce(printResultStub); 42 | }); 43 | 44 | it('should call wordpress receiver kill method and print result if entity is wordpress', async () => { 45 | 46 | killStub = sandbox.stub(WordpressReceiver.prototype, 'kill'); 47 | context = new Context('wordpress', 'kill', [], []); 48 | command = new KillCommand(context); 49 | 50 | await command.execute(); 51 | 52 | sandbox.assert.calledOnce(killStub); 53 | sandbox.assert.calledOnce(printResultStub); 54 | }); 55 | 56 | it('should call help method and print result if --help flag is used', async () => { 57 | 58 | context = new Context('wordpress', 'kill', [], ['--help']); 59 | command = new KillCommand(context); 60 | 61 | await command.execute(); 62 | 63 | sandbox.assert.calledOnce(helpSpy); 64 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 65 | }); 66 | 67 | it('should call help method and print result if entity is undefined', async () => { 68 | 69 | context = new Context('', 'kill', [], []); 70 | command = new KillCommand(context); 71 | 72 | await command.execute(); 73 | 74 | sandbox.assert.calledOnce(helpSpy); 75 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/test/commands/login-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import LoginCommand from '../../commands/login-command'; 4 | import UserReceiver from '../../receivers/user-receiver'; 5 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 6 | 7 | describe('Command: login', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let command: Command, 12 | context: Context, 13 | helpSpy: SinonSpy, 14 | loginStub: SinonStub, 15 | printResultStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | helpSpy = sandbox.spy(LoginCommand.prototype, 'help'); 20 | loginStub = sandbox.stub(UserReceiver.prototype, 'login'); 21 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should call receiver login method and print result', async () => { 31 | 32 | context = new Context('user', 'login', [], []); 33 | command = new LoginCommand(context); 34 | 35 | await command.execute(); 36 | 37 | sandbox.assert.calledOnce(loginStub); 38 | sandbox.assert.calledOnce(printResultStub); 39 | }); 40 | 41 | it('should call help method and print result if --help flag is used', async () => { 42 | 43 | context = new Context('user', 'login', [], ['-h']); 44 | command = new LoginCommand(context); 45 | 46 | await command.execute(); 47 | 48 | sandbox.assert.calledOnce(helpSpy); 49 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 50 | }); 51 | }); -------------------------------------------------------------------------------- /src/test/commands/logout-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import LogoutCommand from '../../commands/logout-command'; 4 | import UserReceiver from '../../receivers/user-receiver'; 5 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 6 | 7 | describe('Command: logout', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let command: Command, 12 | context: Context, 13 | helpSpy: SinonSpy, 14 | logoutStub: SinonStub, 15 | printResultStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | helpSpy = sandbox.spy(LogoutCommand.prototype, 'help'); 20 | logoutStub = sandbox.stub(UserReceiver.prototype, 'logout'); 21 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should call receiver logout method and print result', async () => { 31 | 32 | context = new Context('user', 'logout', [], []); 33 | command = new LogoutCommand(context); 34 | 35 | await command.execute(); 36 | 37 | sandbox.assert.calledOnce(logoutStub); 38 | sandbox.assert.calledOnce(printResultStub); 39 | }); 40 | 41 | it('should call help method and print result if --help flag is used', async () => { 42 | 43 | context = new Context('user', 'logout', [], ['-h']); 44 | command = new LogoutCommand(context); 45 | 46 | await command.execute(); 47 | 48 | sandbox.assert.calledOnce(helpSpy); 49 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 50 | }); 51 | }); -------------------------------------------------------------------------------- /src/test/commands/logs-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import LogsCommand from '../../commands/logs-command'; 4 | import BackendReceiver from '../../receivers/backend-receiver'; 5 | import WordpressReceiver from '../../receivers/wordpress-receiver'; 6 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 7 | 8 | describe('Command: logs', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | let command: Command, 13 | context: Context, 14 | helpSpy: SinonSpy, 15 | logsStub: SinonStub, 16 | printResultStub: SinonStub; 17 | 18 | beforeEach(() => { 19 | 20 | helpSpy = sandbox.spy(LogsCommand.prototype, 'help'); 21 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 22 | sandbox.stub(Context.prototype, 'authenticateUser'); 23 | 24 | }); 25 | 26 | afterEach(() => { 27 | 28 | sandbox.reset(); 29 | sandbox.restore(); 30 | }); 31 | 32 | it('should call backend receiver logs method and print result if entity is backend', async () => { 33 | 34 | logsStub = sandbox.stub(BackendReceiver.prototype, 'logs'); 35 | context = new Context('backend', 'logs', [], []); 36 | command = new LogsCommand(context); 37 | 38 | await command.execute(); 39 | 40 | sandbox.assert.calledOnce(logsStub); 41 | sandbox.assert.calledOnce(printResultStub); 42 | }); 43 | 44 | it('should call wordpress receiver logs method and print result if entity is wordpress', async () => { 45 | 46 | logsStub = sandbox.stub(WordpressReceiver.prototype, 'logs'); 47 | context = new Context('wordpress', 'logs', [], []); 48 | command = new LogsCommand(context); 49 | 50 | await command.execute(); 51 | 52 | sandbox.assert.calledOnce(logsStub); 53 | sandbox.assert.calledOnce(printResultStub); 54 | }); 55 | 56 | it('should call help method and print result if --help flag is used', async () => { 57 | 58 | context = new Context('wordpress', 'logs', [], ['--help']); 59 | command = new LogsCommand(context); 60 | 61 | await command.execute(); 62 | 63 | sandbox.assert.calledOnce(helpSpy); 64 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 65 | }); 66 | 67 | it('should call help method and print result if entity is undefined', async () => { 68 | 69 | context = new Context('', 'logs', [], []); 70 | command = new LogsCommand(context); 71 | 72 | await command.execute(); 73 | 74 | sandbox.assert.calledOnce(helpSpy); 75 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/test/commands/orders-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import OrdersCommand from '../../commands/orders-command'; 4 | import OrderReceiver from '../../receivers/order-receiver'; 5 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 6 | 7 | describe('Command: orders', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let command: Command, 12 | context: Context; 13 | 14 | beforeEach(() => { 15 | 16 | context = new Context('order', '', [], []); 17 | sandbox.stub(context, 'authenticateUser'); 18 | command = new OrdersCommand(context); 19 | }); 20 | 21 | afterEach(() => { 22 | 23 | sandbox.reset(); 24 | sandbox.restore(); 25 | }); 26 | 27 | it('should call receiver list method and print result', async () => { 28 | 29 | const listStub = sandbox.stub(OrderReceiver.prototype, 'list'); 30 | const printResultStub = sandbox.stub(Command.prototype, 'printResult'); 31 | 32 | await command.execute(); 33 | 34 | sandbox.assert.calledOnce(listStub); 35 | sandbox.assert.calledOnce(printResultStub); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/test/commands/publish-command.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import Context from '../../context'; 3 | import Command from '../../commands/command'; 4 | import PublishCommand from '../../commands/publish-command'; 5 | import BackendReceiver from '../../receivers/backend-receiver'; 6 | import ComposeReceiver from '../../receivers/compose-receiver'; 7 | import FrontendReceiver from '../../receivers/frontend-receiver'; 8 | import WordpressReceiver from '../../receivers/wordpress-receiver'; 9 | import DotMdbConfigManager from '../../utils/managers/dot-mdb-config-manager'; 10 | import { createSandbox, SinonStub } from 'sinon'; 11 | 12 | describe('Command: publish', () => { 13 | 14 | const sandbox = createSandbox(); 15 | 16 | let printResultStub: SinonStub; 17 | 18 | beforeEach(() => { 19 | 20 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 21 | sandbox.stub(Context.prototype, 'authenticateUser'); 22 | sandbox.stub(Command.prototype, 'requireDotMdb'); 23 | sandbox.stub(fs, 'existsSync').returns(true); 24 | sandbox.stub(fs, 'writeFileSync'); 25 | }); 26 | 27 | afterEach(() => { 28 | 29 | sandbox.reset(); 30 | sandbox.restore(); 31 | }); 32 | 33 | it('should call frontend receiver publish() method and print result', async () => { 34 | 35 | const publishStub = sandbox.stub(FrontendReceiver.prototype, 'publish'); 36 | const context = new Context('frontend', 'publish', [], []); 37 | const command = new PublishCommand(context); 38 | 39 | await command.execute(); 40 | 41 | sandbox.assert.calledOnce(publishStub); 42 | sandbox.assert.calledOnce(printResultStub); 43 | }); 44 | 45 | it('should call compose receiver publish() method and print result', async () => { 46 | 47 | const publishStub = sandbox.stub(ComposeReceiver.prototype, 'publish'); 48 | const context = new Context('compose', 'publish', [], []); 49 | const command = new PublishCommand(context); 50 | 51 | await command.execute(); 52 | 53 | sandbox.assert.calledOnce(publishStub); 54 | sandbox.assert.calledOnce(printResultStub); 55 | }); 56 | 57 | it('should call backend receiver publish() method and print result', async () => { 58 | 59 | const publishStub = sandbox.stub(BackendReceiver.prototype, 'publish'); 60 | const context = new Context('backend', 'publish', [], []); 61 | const command = new PublishCommand(context); 62 | 63 | await command.execute(); 64 | 65 | sandbox.assert.calledOnce(publishStub); 66 | sandbox.assert.calledOnce(printResultStub); 67 | }); 68 | 69 | it('should call wordpress receiver publish() method and print result', async () => { 70 | 71 | const publishStub = sandbox.stub(WordpressReceiver.prototype, 'publish'); 72 | const context = new Context('wordpress', 'publish', [], []); 73 | const command = new PublishCommand(context); 74 | 75 | await command.execute(); 76 | 77 | sandbox.assert.calledOnce(publishStub); 78 | sandbox.assert.calledOnce(printResultStub); 79 | }); 80 | 81 | it('should call frontend receiver publish() method and print result if entity is undefined', async () => { 82 | 83 | const publishStub = sandbox.stub(FrontendReceiver.prototype, 'publish'); 84 | const context = new Context('', 'publish', [], []); 85 | sandbox.stub(context.mdbConfig, 'getValue').withArgs('meta.type').returns(undefined); 86 | const command = new PublishCommand(context); 87 | 88 | await command.execute(); 89 | 90 | sandbox.assert.calledOnce(publishStub); 91 | sandbox.assert.calledOnce(printResultStub); 92 | }); 93 | 94 | it('should call backend receiver publish() method and print result if entity is undefined but saved in config', async () => { 95 | 96 | sandbox.stub(Context.prototype, 'loadPackageManager'); 97 | sandbox.stub(DotMdbConfigManager.prototype, 'getValue').withArgs('meta.type').returns('backend'); 98 | const publishStub = sandbox.stub(BackendReceiver.prototype, 'publish'); 99 | const context = new Context('', 'publish', [], []); 100 | const command = new PublishCommand(context); 101 | 102 | await command.execute(); 103 | 104 | sandbox.assert.calledOnce(publishStub); 105 | sandbox.assert.calledOnce(printResultStub); 106 | }); 107 | 108 | it('should call help method and print result if --help flag is used', async () => { 109 | 110 | const helpSpy = sandbox.spy(PublishCommand.prototype, 'help'); 111 | const context = new Context('', 'publish', [], ['-h']); 112 | const command = new PublishCommand(context); 113 | 114 | await command.execute(); 115 | 116 | sandbox.assert.calledOnce(helpSpy); 117 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /src/test/commands/register-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import RegisterCommand from '../../commands/register-command'; 4 | import UserReceiver from '../../receivers/user-receiver'; 5 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 6 | 7 | describe('Command: register', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let command: Command, 12 | context: Context, 13 | helpSpy: SinonSpy, 14 | registerStub: SinonStub, 15 | printResultStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | helpSpy = sandbox.spy(RegisterCommand.prototype, 'help'); 20 | registerStub = sandbox.stub(UserReceiver.prototype, 'register'); 21 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should call receiver register method and print result', async () => { 31 | 32 | context = new Context('user', 'register', [], []); 33 | command = new RegisterCommand(context); 34 | 35 | await command.execute(); 36 | 37 | sandbox.assert.calledOnce(registerStub); 38 | sandbox.assert.calledOnce(printResultStub); 39 | }); 40 | 41 | it('should call help method and print result if --help flag is used', async () => { 42 | 43 | context = new Context('user', 'register', [], ['-h']); 44 | command = new RegisterCommand(context); 45 | 46 | await command.execute(); 47 | 48 | sandbox.assert.calledOnce(helpSpy); 49 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 50 | }); 51 | }); -------------------------------------------------------------------------------- /src/test/commands/rename-command.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import Context from '../../context'; 3 | import Command from '../../commands/command'; 4 | import RenameCommand from '../../commands/rename-command'; 5 | import BackendReceiver from '../../receivers/backend-receiver'; 6 | import FrontendReceiver from '../../receivers/frontend-receiver'; 7 | import WordpressReceiver from '../../receivers/wordpress-receiver'; 8 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 9 | 10 | describe('Command: rename', () => { 11 | 12 | const sandbox = createSandbox(); 13 | 14 | let command: RenameCommand, 15 | context: Context, 16 | helpSpy: SinonSpy, 17 | printResultStub: SinonStub, 18 | renameStub: SinonStub; 19 | 20 | afterEach(() => { 21 | 22 | sandbox.reset(); 23 | sandbox.restore(); 24 | }); 25 | 26 | beforeEach(() => { 27 | 28 | helpSpy = sandbox.spy(RenameCommand.prototype, 'help'); 29 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 30 | sandbox.stub(Context.prototype, 'authenticateUser'); 31 | sandbox.stub(Command.prototype, 'requireDotMdb'); 32 | sandbox.stub(fs, 'existsSync').returns(true); 33 | }); 34 | 35 | it('should call backend receiver rename() method and print result', async () => { 36 | 37 | renameStub = sandbox.stub(BackendReceiver.prototype, 'rename'); 38 | context = new Context('backend', 'rename', [], []); 39 | command = new RenameCommand(context); 40 | 41 | await command.execute(); 42 | 43 | sandbox.assert.calledOnce(renameStub); 44 | sandbox.assert.calledOnce(printResultStub); 45 | }); 46 | 47 | it('should call frontend receiver rename() method and print result', async () => { 48 | 49 | renameStub = sandbox.stub(FrontendReceiver.prototype, 'rename'); 50 | context = new Context('frontend', 'rename', [], []); 51 | command = new RenameCommand(context); 52 | 53 | await command.execute(); 54 | 55 | sandbox.assert.calledOnce(renameStub); 56 | sandbox.assert.calledOnce(printResultStub); 57 | }); 58 | 59 | it('should call wordpress receiver rename() method and print result', async () => { 60 | 61 | renameStub = sandbox.stub(WordpressReceiver.prototype, 'rename'); 62 | context = new Context('wordpress', 'rename', [], []); 63 | command = new RenameCommand(context); 64 | 65 | await command.execute(); 66 | 67 | sandbox.assert.calledOnce(renameStub); 68 | sandbox.assert.calledOnce(printResultStub); 69 | }); 70 | 71 | it('should call frontend receiver rename() if entity is undefined', async () => { 72 | 73 | renameStub = sandbox.stub(FrontendReceiver.prototype, 'rename'); 74 | context = new Context('', 'rename', [], []); 75 | command = new RenameCommand(context); 76 | 77 | await command.execute(); 78 | 79 | sandbox.assert.calledOnce(renameStub); 80 | sandbox.assert.calledOnce(printResultStub); 81 | }); 82 | 83 | it('should call help method and print result if --help flag is used', async () => { 84 | 85 | context = new Context('', 'rename', [], ['-h']); 86 | command = new RenameCommand(context); 87 | 88 | await command.execute(); 89 | 90 | sandbox.assert.calledOnce(helpSpy); 91 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 92 | }); 93 | }); -------------------------------------------------------------------------------- /src/test/commands/restart-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import CommandResult from '../../utils/command-result'; 4 | import RestartCommand from '../../commands/restart-command'; 5 | import BackendReceiver from '../../receivers/backend-receiver'; 6 | import WordpressReceiver from '../../receivers/wordpress-receiver'; 7 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 8 | 9 | describe('Command: restart', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let command: RestartCommand, 14 | context: Context, 15 | helpSpy: SinonSpy, 16 | printResultStub: SinonStub, 17 | restartStub: SinonStub; 18 | 19 | beforeEach(() => { 20 | 21 | sandbox.stub(CommandResult.prototype, 'addTextLine'); 22 | helpSpy = sandbox.spy(RestartCommand.prototype, 'help'); 23 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 24 | sandbox.stub(Context.prototype, 'authenticateUser'); 25 | }); 26 | 27 | afterEach(() => { 28 | 29 | sandbox.reset(); 30 | sandbox.restore(); 31 | }); 32 | 33 | it('should call backend receiver restart method and print result if entity is backend', async () => { 34 | 35 | restartStub = sandbox.stub(BackendReceiver.prototype, 'restart'); 36 | context = new Context('backend', 'restart', [], []); 37 | command = new RestartCommand(context); 38 | 39 | await command.execute(); 40 | 41 | sandbox.assert.calledOnce(restartStub); 42 | sandbox.assert.calledOnce(printResultStub); 43 | }); 44 | 45 | it('should call wordpress receiver restart method and print result if entity is wordpress', async () => { 46 | 47 | restartStub = sandbox.stub(WordpressReceiver.prototype, 'restart'); 48 | context = new Context('wordpress', 'restart', [], []); 49 | command = new RestartCommand(context); 50 | 51 | await command.execute(); 52 | 53 | sandbox.assert.calledOnce(restartStub); 54 | sandbox.assert.calledOnce(printResultStub); 55 | }); 56 | 57 | it('should call help method and print result if --help flag is used', async () => { 58 | 59 | context = new Context('backend', 'restart', [], ['--help']); 60 | command = new RestartCommand(context); 61 | 62 | await command.execute(); 63 | 64 | sandbox.assert.calledOnce(helpSpy); 65 | sandbox.assert.calledOnce(printResultStub); 66 | }); 67 | 68 | it('should call help method and print result if entity is undefined', async () => { 69 | 70 | context = new Context('', 'restart', [], []); 71 | command = new RestartCommand(context); 72 | 73 | await command.execute(); 74 | 75 | sandbox.assert.calledOnce(helpSpy); 76 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /src/test/commands/run-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import CommandResult from '../../utils/command-result'; 4 | import RunCommand from '../../commands/run-command'; 5 | import BackendReceiver from '../../receivers/backend-receiver'; 6 | import WordpressReceiver from '../../receivers/wordpress-receiver'; 7 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 8 | 9 | describe('Command: run', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let command: RunCommand, 14 | context: Context, 15 | helpSpy: SinonSpy, 16 | printResultStub: SinonStub, 17 | runStub: SinonStub; 18 | 19 | beforeEach(() => { 20 | 21 | sandbox.stub(CommandResult.prototype, 'addTextLine'); 22 | helpSpy = sandbox.spy(RunCommand.prototype, 'help'); 23 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 24 | sandbox.stub(Context.prototype, 'authenticateUser'); 25 | }); 26 | 27 | afterEach(() => { 28 | 29 | sandbox.reset(); 30 | sandbox.restore(); 31 | }); 32 | 33 | it('should call backend receiver run method and print result if entity is backend', async () => { 34 | 35 | runStub = sandbox.stub(BackendReceiver.prototype, 'run'); 36 | context = new Context('backend', 'run', [], []); 37 | command = new RunCommand(context); 38 | 39 | await command.execute(); 40 | 41 | sandbox.assert.calledOnce(runStub); 42 | sandbox.assert.calledOnce(printResultStub); 43 | }); 44 | 45 | it('should call wordpress receiver run method and print result if entity is wordpress', async () => { 46 | 47 | runStub = sandbox.stub(WordpressReceiver.prototype, 'run'); 48 | context = new Context('wordpress', 'run', [], []); 49 | command = new RunCommand(context); 50 | 51 | await command.execute(); 52 | 53 | sandbox.assert.calledOnce(runStub); 54 | sandbox.assert.calledOnce(printResultStub); 55 | }); 56 | 57 | it('should call help method and print result if --help flag is used', async () => { 58 | 59 | context = new Context('backend', 'run', [], ['--help']); 60 | command = new RunCommand(context); 61 | 62 | await command.execute(); 63 | 64 | sandbox.assert.calledOnce(helpSpy); 65 | sandbox.assert.calledOnce(printResultStub); 66 | }); 67 | 68 | it('should call help method and print result if entity is undefined', async () => { 69 | 70 | context = new Context('', 'run', [], []); 71 | command = new RunCommand(context); 72 | 73 | await command.execute(); 74 | 75 | sandbox.assert.calledOnce(helpSpy); 76 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /src/test/commands/update-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import AppReceiver from '../../receivers/app-receiver'; 4 | import UpdateCommand from '../../commands/update-command'; 5 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 6 | 7 | describe('Command: update', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let command: UpdateCommand, 12 | context: Context, 13 | helpSpy: SinonSpy, 14 | printResultStub: SinonStub, 15 | updateStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | helpSpy = sandbox.spy(UpdateCommand.prototype, 'help'); 20 | updateStub = sandbox.stub(AppReceiver.prototype, 'updateApp'); 21 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should call receiver getupdate method and print result', async () => { 31 | 32 | context = new Context('app', 'update', [], []); 33 | command = new UpdateCommand(context); 34 | 35 | await command.execute(); 36 | 37 | sandbox.assert.calledOnce(updateStub); 38 | sandbox.assert.calledOnce(printResultStub); 39 | }); 40 | 41 | it('should call help method and print result if --help flag is used', async () => { 42 | 43 | context = new Context('app', 'update', [], ['-h']); 44 | command = new UpdateCommand(context); 45 | 46 | await command.execute(); 47 | 48 | sandbox.assert.calledOnce(helpSpy); 49 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 50 | }); 51 | }); -------------------------------------------------------------------------------- /src/test/commands/version-command.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import Command from '../../commands/command'; 3 | import AppReceiver from '../../receivers/app-receiver'; 4 | import VersionCommand from '../../commands/version-command'; 5 | import { createSandbox, SinonSpy, SinonStub } from 'sinon'; 6 | 7 | describe('Command: version', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let command: VersionCommand, 12 | context: Context, 13 | helpSpy: SinonSpy, 14 | getVersionStub: SinonStub, 15 | printResultStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | helpSpy = sandbox.spy(VersionCommand.prototype, 'help'); 20 | getVersionStub = sandbox.stub(AppReceiver.prototype, 'getVersion'); 21 | printResultStub = sandbox.stub(Command.prototype, 'printResult'); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should call receiver getVersion method and print result', () => { 31 | 32 | context = new Context('app', 'version', [], []); 33 | command = new VersionCommand(context); 34 | 35 | command.execute(); 36 | 37 | sandbox.assert.calledOnce(getVersionStub); 38 | sandbox.assert.calledOnce(printResultStub); 39 | }); 40 | 41 | it('should call help method and print result if --help flag is used', async () => { 42 | 43 | context = new Context('app', 'version', [], ['-h']); 44 | command = new VersionCommand(context); 45 | 46 | await command.execute(); 47 | 48 | sandbox.assert.calledOnce(helpSpy); 49 | sandbox.assert.calledOnceWithExactly(printResultStub, [command.results]); 50 | }); 51 | }); -------------------------------------------------------------------------------- /src/test/helpers/archive-wrapper.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import { archiveProject } from "../../helpers/archiver-wrapper"; 3 | 4 | describe('Helper: archiveProject', () => { 5 | 6 | it('should call archiver and return object', () => { 7 | 8 | const archive = archiveProject('zip', { zlib: { level: 9 } }); 9 | 10 | expect(archive).to.be.an('Object'); 11 | // @ts-ignore 12 | expect(archive._format).to.eq('zip'); 13 | // @ts-ignore 14 | expect(archive.options.zlib.level).to.eq(9); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /src/test/helpers/create-checkbox-prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import inquirer from "inquirer"; 3 | import { createSandbox, SinonStub } from "sinon"; 4 | import helpers from "../../helpers"; 5 | 6 | describe('Helper: createCheckboxPrompt', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | let moduleStub: any, createPrompt: SinonStub; 11 | 12 | beforeEach( () => { 13 | 14 | moduleStub = sandbox.stub().resolvesArg(0); 15 | createPrompt = sandbox.stub(inquirer, 'createPromptModule').returns(moduleStub); 16 | }); 17 | 18 | afterEach(() => { 19 | 20 | sandbox.reset(); 21 | sandbox.restore(); 22 | }); 23 | 24 | describe('should create list prompt', () => { 25 | 26 | it('should call createPromptModule', async () => { 27 | 28 | await helpers.createCheckboxPrompt('message', ['choice']); 29 | expect(createPrompt).to.be.calledOnce; 30 | }); 31 | 32 | it('should call createPromptModule with expected args', async () => { 33 | 34 | await helpers.createCheckboxPrompt('message', ['choice']); 35 | expect(moduleStub).to.have.been.calledWith([{ type: 'checkbox', name: 'name', message: 'message', choices: ['choice'] }]); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/test/helpers/create-confirmation-prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import inquirer from "inquirer"; 3 | import { createSandbox, SinonStub } from "sinon"; 4 | import helpers from "../../helpers"; 5 | 6 | describe('Helper: createConfirmationPrompt', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | let moduleStub: any, createPrompt: SinonStub; 11 | 12 | beforeEach( () => { 13 | 14 | moduleStub = sandbox.stub().resolvesArg(0); 15 | createPrompt = sandbox.stub(inquirer, 'createPromptModule').returns(moduleStub); 16 | }); 17 | 18 | afterEach(() => { 19 | 20 | sandbox.reset(); 21 | sandbox.restore(); 22 | }); 23 | 24 | describe('should create list prompt', () => { 25 | 26 | it('should call createPromptModule', async () => { 27 | 28 | await helpers.createConfirmationPrompt('message'); 29 | expect(createPrompt).to.be.calledOnce; 30 | }); 31 | 32 | it('should call createPromptModule with expected args', async () => { 33 | 34 | await helpers.createConfirmationPrompt('message', true); 35 | expect(moduleStub).to.have.been.calledWith([{ default: true, type: 'confirm', name: 'answer', message: 'message' }]); 36 | }); 37 | }); 38 | }); -------------------------------------------------------------------------------- /src/test/helpers/create-jenkinsfile.test.ts: -------------------------------------------------------------------------------- 1 | import { createJenkinsfile } from "../../helpers/create-jenkinsfile"; 2 | import { createSandbox, SinonStub } from "sinon"; 3 | import { expect } from "chai"; 4 | import path from "path"; 5 | import fs from "fs"; 6 | 7 | describe('Helper: createJenkinsfile', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let existsStub: SinonStub, writeStub: SinonStub; 12 | 13 | beforeEach(() => { 14 | 15 | sandbox.stub(path, 'join').returns('/fake/path'); 16 | existsStub = sandbox.stub(fs, 'existsSync'); 17 | writeStub = sandbox.stub(fs, 'writeFileSync'); 18 | }); 19 | 20 | afterEach(() => { 21 | 22 | sandbox.reset(); 23 | sandbox.restore(); 24 | }); 25 | 26 | it('should create a Jenkinsfile and return true', async () => { 27 | 28 | existsStub.returns(false); 29 | 30 | const result = await createJenkinsfile('', false); 31 | 32 | expect(result).to.be.true; 33 | sandbox.assert.calledOnce(writeStub); 34 | }); 35 | 36 | it('should create simple Jenkinsfile and return true', async () => { 37 | 38 | existsStub.returns(false); 39 | 40 | const result = await createJenkinsfile('', true); 41 | 42 | expect(result).to.be.true; 43 | sandbox.assert.calledOnce(writeStub); 44 | }); 45 | 46 | it('should not create Jenkinsfile and return false if already exists', async () => { 47 | 48 | existsStub.returns(true); 49 | 50 | const result = await createJenkinsfile('', true); 51 | 52 | expect(result).to.be.false; 53 | sandbox.assert.notCalled(writeStub); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /src/test/helpers/create-list-prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import inquirer from "inquirer"; 3 | import { createSandbox, SinonStub } from "sinon"; 4 | import helpers from "../../helpers"; 5 | 6 | describe('Helper: createListPrompt', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | let moduleStub: any, createPrompt: SinonStub; 11 | 12 | beforeEach( () => { 13 | 14 | moduleStub = sandbox.stub().resolvesArg(0); 15 | createPrompt = sandbox.stub(inquirer, 'createPromptModule').returns(moduleStub); 16 | }); 17 | 18 | afterEach(() => { 19 | 20 | sandbox.reset(); 21 | sandbox.restore(); 22 | }); 23 | 24 | describe('should create list prompt', () => { 25 | 26 | it('should call createPromptModule', async () => { 27 | 28 | await helpers.createListPrompt('message', ['choice']); 29 | expect(createPrompt).to.be.calledOnce; 30 | }); 31 | 32 | it('should call createPromptModule with expected args', async () => { 33 | 34 | await helpers.createListPrompt('message', ['choice']); 35 | expect(moduleStub).to.have.been.calledWith([{ type: 'list', name: 'name', message: 'message', choices: ['choice'] }]); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/test/helpers/create-pass-prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import inquirer from "inquirer"; 3 | import { createSandbox, SinonStub } from "sinon"; 4 | import { createPassPrompt } from "../../helpers/create-pass-prompt"; 5 | 6 | describe('Helper: createPassPrompt', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | let moduleStub: any, createPrompt: SinonStub; 11 | 12 | beforeEach(() => { 13 | 14 | moduleStub = sandbox.stub().resolvesArg(0); 15 | createPrompt = sandbox.stub(inquirer, 'createPromptModule').returns(moduleStub); 16 | }); 17 | 18 | afterEach(() => { 19 | 20 | sandbox.reset(); 21 | sandbox.restore(); 22 | }); 23 | 24 | describe('should create text prompt', () => { 25 | 26 | it('should call createPromptModule', async () => { 27 | 28 | await createPassPrompt('message', 'invalid message'); 29 | expect(createPrompt).to.be.calledOnce; 30 | }); 31 | 32 | it('should call createPromptModule with expected args', async () => { 33 | 34 | const validate = (v: string) => Boolean(v); 35 | await createPassPrompt('message', 'invalid message', validate); 36 | 37 | const arg = moduleStub.getCall(0).args[0]; 38 | expect(arg).to.be.an('array'); 39 | expect(arg[0]).to.be.an('object').that.have.all.keys('message', 'name', 'type', 'validate', 'mask'); 40 | 41 | const [obj] = arg; 42 | expect(obj.message).to.eq('message'); 43 | expect(obj.name).to.eq('password'); 44 | expect(obj.type).to.eq('password'); 45 | expect(typeof obj.validate).to.eq('function'); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/test/helpers/create-text-prompt.test.ts: -------------------------------------------------------------------------------- 1 | import { expect } from "chai"; 2 | import inquirer from "inquirer"; 3 | import { createSandbox, SinonStub } from "sinon"; 4 | import helpers from "../../helpers"; 5 | 6 | describe('Helper: createTextPrompt', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | let moduleStub: any, createPrompt: SinonStub; 11 | 12 | beforeEach(() => { 13 | 14 | moduleStub = sandbox.stub().resolvesArg(0); 15 | createPrompt = sandbox.stub(inquirer, 'createPromptModule').returns(moduleStub); 16 | }); 17 | 18 | afterEach(() => { 19 | 20 | sandbox.reset(); 21 | sandbox.restore(); 22 | }); 23 | 24 | describe('should create text prompt', () => { 25 | 26 | it('should call createPromptModule', async () => { 27 | 28 | await helpers.createTextPrompt('message', 'invalid message'); 29 | expect(createPrompt).to.be.calledOnce; 30 | }); 31 | 32 | it('should call createPromptModule with expected args', async () => { 33 | 34 | const validate = (v: string) => Boolean(v); 35 | await helpers.createTextPrompt('message', 'invalid message', validate); 36 | 37 | const arg = moduleStub.getCall(0).args[0]; 38 | expect(arg).to.be.an('array'); 39 | expect(arg[0]).to.be.an('object').that.have.all.keys('message', 'name', 'type', 'validate'); 40 | 41 | const [obj] = arg; 42 | expect(obj.message).to.eq('message'); 43 | expect(obj.name).to.eq('answer'); 44 | expect(obj.type).to.eq('text'); 45 | expect(typeof obj.validate).to.eq('function'); 46 | }); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /src/test/helpers/deserialize-json-file.test.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import helpers from "../../helpers"; 3 | import { createSandbox, SinonStub } from "sinon"; 4 | import { expect } from "chai"; 5 | 6 | describe('Helper: deserializeJsonFile', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | const fakeJsonFile = 'fakeJsonFile'; 11 | let readFileStub: SinonStub; 12 | 13 | beforeEach(() => { 14 | 15 | readFileStub = sandbox.stub(fs, 'readFile'); 16 | }); 17 | 18 | afterEach(() => { 19 | 20 | sandbox.reset(); 21 | sandbox.restore(); 22 | }); 23 | 24 | it('should return promise', () => { 25 | 26 | expect(helpers.deserializeJsonFile(fakeJsonFile)).to.be.a('promise'); 27 | }); 28 | 29 | it('should parse json file', async () => { 30 | 31 | const jsonParseStub = sandbox.spy(JSON, 'parse'); 32 | 33 | readFileStub.yields(null, '{"test": "value"}'); 34 | 35 | const result = await helpers.deserializeJsonFile(fakeJsonFile); 36 | expect(jsonParseStub).to.have.been.calledOnce; 37 | expect(result).to.be.an('object'); 38 | expect(result).to.deep.eq({test: 'value'}); 39 | }); 40 | 41 | it('should call fs.readFile', (done) => { 42 | 43 | helpers.deserializeJsonFile(fakeJsonFile); 44 | 45 | expect(readFileStub).to.have.been.calledOnce; 46 | done(); 47 | }); 48 | 49 | it('should call fs.readFile with expected args', (done) => { 50 | 51 | helpers.deserializeJsonFile(fakeJsonFile); 52 | 53 | expect(readFileStub).to.have.been.calledWith(fakeJsonFile, 'utf8'); 54 | done(); 55 | }); 56 | 57 | it('should throw error from fs.readFile', async () => { 58 | 59 | readFileStub.throws('fakeErr'); 60 | 61 | try { 62 | 63 | await helpers.deserializeJsonFile(''); 64 | } catch (e) { 65 | 66 | return expect(e.name).to.be.eq('fakeErr'); 67 | } 68 | 69 | chai.assert.fail('fs.readFile function should throw error'); 70 | }); 71 | }); 72 | -------------------------------------------------------------------------------- /src/test/helpers/download-from-ftp.test.ts: -------------------------------------------------------------------------------- 1 | import unzip from "unzip-stream"; 2 | import { Readable } from "stream"; 3 | import ProgressBar from "progress"; 4 | import config from "../../config"; 5 | import helpers from "../../helpers"; 6 | import HttpWrapper from "../../utils/http-wrapper"; 7 | import { createSandbox, SinonStub } from "sinon"; 8 | import { expect } from "chai"; 9 | 10 | describe('Helper: downloadFromFTP', () => { 11 | 12 | const sandbox = createSandbox(); 13 | 14 | const fakePort = '0', 15 | fakeHost = 'fakeHost', 16 | fakeHeaders = { fake: 'fakeHeader' }, 17 | fakeDestination = 'fake/dest/folder'; 18 | 19 | let fakeRequest: any, 20 | fakeResponse: any, 21 | fakePipe: any, 22 | fakeExtract: any, 23 | createRequestStub: SinonStub, 24 | readablePipeStub: SinonStub, 25 | extractStub: SinonStub, 26 | http: HttpWrapper; 27 | 28 | beforeEach(() => { 29 | 30 | http = new HttpWrapper(); 31 | fakeRequest = { on: sandbox.stub(), end: sandbox.stub() }; 32 | fakeResponse = { on: sandbox.stub(), headers: fakeHeaders, statusCode: 200 }; 33 | fakeExtract = { on: sandbox.stub() }; 34 | fakePipe = { on: sandbox.stub() }; 35 | // sandbox.replace(config, 'port', fakePort); 36 | sandbox.replace(config, 'host', fakeHost); 37 | createRequestStub = sandbox.stub(HttpWrapper.prototype, 'createRawRequest'); 38 | readablePipeStub = sandbox.stub(Readable.prototype, 'pipe'); 39 | extractStub = sandbox.stub(unzip, 'Extract'); 40 | sandbox.stub(ProgressBar.prototype, 'tick').returns(); 41 | }); 42 | 43 | afterEach(() => { 44 | 45 | sandbox.reset(); 46 | sandbox.restore(); 47 | }); 48 | 49 | it('should download from ftp', async () => { 50 | 51 | fakeResponse.on.withArgs('end').yields(); 52 | fakeResponse.on.withArgs('data').yields('fakeData'); 53 | createRequestStub.returns(fakeRequest).yields(fakeResponse); 54 | fakePipe.on.withArgs('close').yields(''); 55 | readablePipeStub.returns(fakePipe); 56 | extractStub.returns(fakeExtract); 57 | 58 | const result = await helpers.downloadFromFTP(http, fakeHeaders, fakeDestination); 59 | 60 | sandbox.assert.callOrder(createRequestStub, extractStub, readablePipeStub); 61 | expect(result).to.be.deep.include('Download completed.'); 62 | }); 63 | 64 | it('should return expected result if error', async () => { 65 | 66 | createRequestStub.returns(fakeRequest).yields(fakeResponse); 67 | readablePipeStub.throws('fakeErr'); 68 | extractStub.returns(fakeExtract); 69 | 70 | try { 71 | 72 | await helpers.downloadFromFTP(http, fakeHeaders, fakeDestination); 73 | } 74 | catch (err) { 75 | 76 | return expect(err).to.be.deep.equal('Download error.'); 77 | } 78 | 79 | chai.assert.fail('readStream.pipe function should throw download error'); 80 | }); 81 | 82 | it('should return expected statusMessage if error response status code', async () => { 83 | 84 | fakeResponse.statusCode = 423; 85 | fakeResponse.statusMessage = 'Fake error'; 86 | fakeResponse.on.withArgs('end').yields(); 87 | fakeResponse.on.withArgs('data').yields(''); 88 | createRequestStub.returns(fakeRequest).yields(fakeResponse); 89 | 90 | try { 91 | 92 | await helpers.downloadFromFTP(http, fakeHeaders, fakeDestination); 93 | } 94 | catch (err) { 95 | 96 | return expect(err).to.be.deep.equal('Fake error'); 97 | } 98 | 99 | chai.assert.fail('Http request function should throw status error'); 100 | }); 101 | }); 102 | -------------------------------------------------------------------------------- /src/test/helpers/serialize-json-file.test.ts: -------------------------------------------------------------------------------- 1 | import fs from "fs"; 2 | import helpers from "../../helpers"; 3 | import { createSandbox, SinonStub } from "sinon"; 4 | import { expect } from "chai"; 5 | 6 | describe('Helper: serializeJsonFile', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | const fakeFileName = 'fakeName', 11 | fakeObject = { fake: 'object' }; 12 | 13 | let writeFileStub: SinonStub; 14 | 15 | beforeEach(() => { 16 | 17 | writeFileStub = sandbox.stub(fs, 'writeFile'); 18 | }); 19 | 20 | afterEach(() => { 21 | 22 | sandbox.reset(); 23 | sandbox.restore(); 24 | }); 25 | 26 | describe('should serialize json file', () => { 27 | 28 | it('should call fs.writeFile', (done) => { 29 | 30 | helpers.serializeJsonFile(fakeFileName, fakeObject); 31 | 32 | expect(writeFileStub).to.be.calledOnce; 33 | done(); 34 | }); 35 | 36 | it('should call fs.writeFile with expected arguments', (done) => { 37 | 38 | helpers.serializeJsonFile(fakeFileName, fakeObject); 39 | const fakeSerializedObject = JSON.stringify(fakeObject, null, ' '); 40 | 41 | expect(writeFileStub).to.be.calledWith(fakeFileName, fakeSerializedObject, 'utf-8'); 42 | done(); 43 | }); 44 | 45 | it('should throw error on fs.writeFile fail', async () => { 46 | 47 | writeFileStub.throws('fakeError'); 48 | 49 | try { 50 | 51 | await helpers.serializeJsonFile(fakeFileName, fakeObject); 52 | } catch (e) { 53 | 54 | return expect(e.name).to.be.eq('fakeError'); 55 | } 56 | 57 | chai.assert.fail('fs.writeFile function should throw error'); 58 | }); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/test/index.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import chai from 'chai'; 4 | import sinonChai from 'sinon-chai'; 5 | 6 | chai.use(sinonChai); 7 | -------------------------------------------------------------------------------- /src/test/receivers/app-receiver.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import AppReceiver from '../../receivers/app-receiver'; 3 | import { createSandbox } from 'sinon'; 4 | import { expect } from 'chai'; 5 | 6 | describe('Receiver: app', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | let context: Context, 11 | receiver: AppReceiver; 12 | 13 | afterEach(() => { 14 | 15 | sandbox.reset(); 16 | sandbox.restore(); 17 | }); 18 | 19 | describe('Method: getHelp', () => { 20 | 21 | beforeEach(() => { 22 | 23 | context = new Context('app', 'help', [], []); 24 | receiver = new AppReceiver(context); 25 | }); 26 | 27 | it('should set the expected result', () => { 28 | 29 | receiver.getHelp(); 30 | 31 | expect(receiver.result.messages).to.be.an('array').that.is.not.empty; 32 | }); 33 | }); 34 | 35 | describe('Method: getVersion', () => { 36 | 37 | //@ts-ignore 38 | const packageJson = require('../../../package.json'); 39 | const expectedResult = { type: 'text', value: `Version: ${packageJson.version}` }; 40 | 41 | beforeEach(() => { 42 | 43 | context = new Context('app', 'version', [], []); 44 | receiver = new AppReceiver(context); 45 | }); 46 | 47 | it('should set the expected result', () => { 48 | 49 | receiver.getVersion(); 50 | 51 | expect(receiver.result.messages).to.be.an('array').that.is.not.empty; 52 | expect(receiver.result.messages).to.deep.include(expectedResult); 53 | }); 54 | }); 55 | 56 | describe('Method: updateApp', () => { 57 | 58 | beforeEach(() => { 59 | 60 | context = new Context('app', 'update', [], []); 61 | receiver = new AppReceiver(context); 62 | }); 63 | 64 | it('should set the expected result if success', async () => { 65 | 66 | const expectedResult = { type: 'alert', value: { title: 'Success', body: '' }, color: 'green' }; 67 | sandbox.stub(Context.prototype, 'loadPackageManager').resolves(); 68 | sandbox.stub(context, 'packageManager').value({ update: sandbox.stub().resolves(expectedResult.value.body) }); 69 | 70 | await receiver.updateApp(); 71 | 72 | expect(receiver.result.messages).to.be.an('array').that.is.not.empty; 73 | expect(receiver.result.messages).to.deep.include(expectedResult); 74 | }); 75 | 76 | it('should set the expected result if error', async () => { 77 | 78 | const expectedResult = { type: 'alert', value: { title: 'Error:', body: new Error('Update failed') }, color: 'red' }; 79 | sandbox.stub(Context.prototype, 'loadPackageManager').resolves(); 80 | sandbox.stub(context, 'packageManager').value({ update: sandbox.stub().rejects(expectedResult.value.body) }); 81 | 82 | await receiver.updateApp(); 83 | 84 | expect(receiver.result.messages).to.be.an('array').that.is.not.empty; 85 | expect(receiver.result.messages).to.deep.include(expectedResult); 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /src/test/receivers/blank-receiver.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import Context from '../../context'; 3 | import helpers from '../../helpers'; 4 | import BlankReceiver from '../../receivers/blank-receiver'; 5 | import { createSandbox, SinonStub } from 'sinon'; 6 | import { expect } from 'chai'; 7 | 8 | describe('Receiver: blank', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | let context: Context, 13 | receiver: BlankReceiver; 14 | 15 | beforeEach(() => { 16 | 17 | sandbox.stub(Context.prototype, 'authenticateUser'); 18 | }); 19 | 20 | afterEach(() => { 21 | 22 | sandbox.reset(); 23 | sandbox.restore(); 24 | }); 25 | 26 | describe('Method: init', () => { 27 | 28 | let createConfirmationPromptStub: SinonStub, 29 | createJenkinsfileStub: SinonStub, 30 | createTextPromptStub: SinonStub, 31 | eraseDirectoriesStub: SinonStub, 32 | existsSyncStub: SinonStub; 33 | 34 | beforeEach(() => { 35 | 36 | sandbox.stub(process, 'cwd').returns('fake/cwd'); 37 | existsSyncStub = sandbox.stub(fs, 'existsSync'); 38 | sandbox.stub(fs, 'mkdirSync'); 39 | context = new Context('blank', 'init', [], ['--name', 'fakeName']); 40 | receiver = new BlankReceiver(context); 41 | sandbox.stub(context, '_loadPackageJsonConfig'); 42 | sandbox.stub(context, 'loadPackageManager').resolves(); 43 | sandbox.stub(context, 'packageManager').value({ init: sandbox.stub().returns('') }); 44 | sandbox.stub(context, 'mdbConfig').value({ setValue: sandbox.stub(), save: sandbox.stub() }); 45 | createConfirmationPromptStub = sandbox.stub(helpers, 'createConfirmationPrompt'); 46 | createJenkinsfileStub = sandbox.stub(helpers, 'createJenkinsfile'); 47 | createTextPromptStub = sandbox.stub(helpers, 'createTextPrompt'); 48 | eraseDirectoriesStub = sandbox.stub(helpers, 'eraseDirectories'); 49 | }); 50 | 51 | it('should init blank project and return expected result', async () => { 52 | 53 | const expectedResult = { type: 'alert', value: { title: 'Success', body: 'Your project was initialized in fake/cwd/fakeName' }, color: 'green' }; 54 | existsSyncStub.returns(false); 55 | eraseDirectoriesStub.resolves(); 56 | createJenkinsfileStub.resolves(); 57 | 58 | await receiver.init(); 59 | 60 | expect(receiver.result.messages).to.deep.include(expectedResult); 61 | }); 62 | 63 | it('should try init blank project and return expected result if error in erase directories', async () => { 64 | 65 | const expectedResult = { type: 'alert', value: { title: 'Error', body: 'Fake error' }, color: 'red' }; 66 | existsSyncStub.returns(true); 67 | createTextPromptStub.resolves('fakeName'); 68 | createConfirmationPromptStub.resolves(false); 69 | eraseDirectoriesStub.returns(Promise.reject('Fake error')); 70 | createJenkinsfileStub.resolves(); 71 | 72 | await receiver.init(); 73 | 74 | expect(receiver.result.messages).to.deep.include(expectedResult); 75 | }); 76 | 77 | it('should try init blank project and return expected result if error creating Jenkinsfile', async () => { 78 | 79 | const expectedResult = { type: 'alert', value: { title: 'Error', body: 'Fake error' }, color: 'red' }; 80 | receiver.context.packageJsonConfig = { scripts: { test: 'fake test script' } }; 81 | existsSyncStub.withArgs('fake/cwd/fakeName').returns(true); 82 | existsSyncStub.withArgs('fake/cwd/newProjectName').returns(false); 83 | createConfirmationPromptStub.resolves(true); 84 | createTextPromptStub.resolves('newProjectName'); 85 | eraseDirectoriesStub.resolves(); 86 | createJenkinsfileStub.returns(Promise.reject('Fake error')); 87 | 88 | await receiver.init(); 89 | 90 | expect(receiver.result.messages).to.deep.include(expectedResult); 91 | }); 92 | }); 93 | }); 94 | -------------------------------------------------------------------------------- /src/test/receivers/config-receiver.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import ConfigReceiver from '../../receivers/config-receiver'; 3 | import { createSandbox } from 'sinon'; 4 | import { expect } from 'chai'; 5 | 6 | describe('Receiver: Config', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | let context: Context, 11 | receiver: ConfigReceiver; 12 | 13 | afterEach(() => { 14 | sandbox.reset(); 15 | sandbox.restore(); 16 | }); 17 | 18 | it('should register --unset flag', function () { 19 | 20 | context = new Context('config', 'config', [], []); 21 | const registerStub = sandbox.stub(context, 'registerNonArgFlags'); 22 | 23 | new ConfigReceiver(context); 24 | 25 | expect(registerStub).to.have.been.calledWith(['enable-ssl', 'global', 'leave', 'unset', 'list']); 26 | }); 27 | 28 | it('should call unsetValue() method with provided argument', function () { 29 | 30 | const args = ['fakekey']; 31 | const flags = ['--unset']; 32 | context = new Context('config', 'config', args, flags); 33 | 34 | const saveStub = sandbox.stub(context.mdbConfig, 'save'); 35 | const unsetStub = sandbox.stub(context.mdbConfig, 'unsetValue'); 36 | 37 | receiver = new ConfigReceiver(context); 38 | receiver.changeConfig(); 39 | 40 | expect(unsetStub).to.have.been.calledWith(...args); 41 | expect(saveStub).to.have.been.calledOnce; 42 | }); 43 | 44 | it('should call setValue() method with provided argument', function () { 45 | 46 | const args = ['fakekey', 'fakevalue']; 47 | context = new Context('config', 'config', args, []); 48 | 49 | const saveStub = sandbox.stub(context.mdbConfig, 'save'); 50 | const unsetStub = sandbox.stub(context.mdbConfig, 'setValue'); 51 | 52 | receiver = new ConfigReceiver(context); 53 | receiver.changeConfig(); 54 | 55 | expect(unsetStub).to.have.been.calledWith(...args); 56 | expect(saveStub).to.have.been.calledOnce; 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/test/receivers/order-receiver.test.ts: -------------------------------------------------------------------------------- 1 | import config from '../../config'; 2 | import Context from '../../context'; 3 | import OrderReceiver from '../../receivers/order-receiver'; 4 | import { CustomOkResponse } from '../../utils/http-wrapper'; 5 | import { createSandbox } from 'sinon'; 6 | import { expect } from 'chai'; 7 | 8 | describe('Receiver: order', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | let context: Context, 13 | receiver: OrderReceiver; 14 | 15 | beforeEach(() => { 16 | 17 | sandbox.stub(Context.prototype, 'authenticateUser'); 18 | sandbox.stub(config, 'host').value('fakeHost'); 19 | sandbox.stub(config, 'port').value('fakePort'); 20 | }); 21 | 22 | afterEach(() => { 23 | 24 | sandbox.reset(); 25 | sandbox.restore(); 26 | }); 27 | 28 | describe('Method: list', () => { 29 | 30 | it('should set expected result if user does not have any orders', async () => { 31 | 32 | context = new Context('order', '', [], []); 33 | receiver = new OrderReceiver(context); 34 | sandbox.stub(receiver.http, 'get').resolves({ body: '[]' } as CustomOkResponse); 35 | 36 | await receiver.list(); 37 | 38 | expect(receiver.result.messages[0].value).to.eql('You don\'t have any orders yet.'); 39 | }); 40 | 41 | it('should set expected result if user has an order', async () => { 42 | 43 | const fakeOrder = { 44 | postId: 1, 45 | postDate: '2019-06-24T06:49:53.000Z', 46 | postStatus: 'wc-completed' 47 | }; 48 | const expectedResult = [{ 49 | 'Order ID': 1, 50 | 'Order Date': new Date(fakeOrder.postDate).toLocaleString(), 51 | 'Order Status': 'completed' 52 | }]; 53 | context = new Context('order', '', [], []); 54 | receiver = new OrderReceiver(context); 55 | sandbox.stub(receiver.http, 'get').resolves({ body: JSON.stringify([fakeOrder]) } as CustomOkResponse); 56 | 57 | await receiver.list(); 58 | 59 | expect(receiver.result.messages[0].value).to.eql(expectedResult); 60 | }); 61 | }); 62 | }); -------------------------------------------------------------------------------- /src/test/receivers/repo-receiver.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import Context from '../../context'; 3 | import helpers from '../../helpers'; 4 | import RepoReceiver from '../../receivers/repo-receiver'; 5 | import { CustomOkResponse } from '../../utils/http-wrapper'; 6 | import { createSandbox, SinonStub } from 'sinon'; 7 | import { expect } from 'chai'; 8 | 9 | describe('Receiver: repo', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let context: Context, 14 | receiver: RepoReceiver; 15 | 16 | beforeEach(() => { 17 | 18 | sandbox.stub(Context.prototype, 'authenticateUser'); 19 | }); 20 | 21 | afterEach(() => { 22 | 23 | sandbox.reset(); 24 | sandbox.restore(); 25 | }); 26 | 27 | describe('Method: init', function () { 28 | 29 | let createJenkinsfileStub: SinonStub, 30 | fsExistsSyncStub: SinonStub; 31 | 32 | beforeEach(() => { 33 | 34 | sandbox.stub(process, 'cwd').returns('fake/cwd'); 35 | context = new Context('repo', 'init', [], []); 36 | receiver = new RepoReceiver(context); 37 | createJenkinsfileStub = sandbox.stub(helpers, 'createJenkinsfile'); 38 | fsExistsSyncStub = sandbox.stub(fs, 'existsSync'); 39 | }); 40 | 41 | it('should init repo and return expected result', async () => { 42 | 43 | const gitInitStub = sandbox.stub(receiver.git, 'init'); 44 | const gitAddOriginStub = sandbox.stub(receiver.git, 'addOrigin'); 45 | const gitCommitStub = sandbox.stub(receiver.git, 'commit'); 46 | const gitPushStub = sandbox.stub(receiver.git, 'push'); 47 | const expectedResult = { type: 'alert', value: { title: '\nSuccess', body: 'Project fakeName successfully created. Repository url: \n' }, color: 'green' }; 48 | sandbox.stub(receiver.http, 'post').resolves({ body: '{"name":"fakeName","url":"","webhook":"true","saved":"true","pipeline":"true"}' } as CustomOkResponse); 49 | sandbox.stub(receiver, '_getProjectName').resolves('fakeName'); 50 | createJenkinsfileStub.resolves(); 51 | fsExistsSyncStub.returns(false); 52 | 53 | await receiver.init(); 54 | 55 | sandbox.assert.callOrder(gitInitStub, gitAddOriginStub, gitCommitStub, gitPushStub); 56 | expect(createJenkinsfileStub).to.have.been.calledOnce; 57 | expect(receiver.result.messages[0]).to.deep.include(expectedResult); 58 | }); 59 | 60 | it('should setOrigin repo and return expected result if .git exists', async () => { 61 | 62 | const gitSetOriginStub = sandbox.stub(receiver.git, 'setOrigin'); 63 | const gitPushStub = sandbox.stub(receiver.git, 'push'); 64 | const expectedResult = { type: 'alert', value: { title: '\nSuccess', body: 'Project fakeName successfully created. Repository url: \n' }, color: 'green' }; 65 | sandbox.stub(receiver.http, 'post').resolves({ body: '{"name":"fakeName","url":"","webhook":"true","saved":"true","pipeline":"true"}' } as CustomOkResponse); 66 | sandbox.stub(receiver, '_getProjectName').resolves('fakeName'); 67 | createJenkinsfileStub.resolves(); 68 | fsExistsSyncStub.returns(true); 69 | 70 | await receiver.init(); 71 | 72 | sandbox.assert.callOrder(gitSetOriginStub, gitPushStub); 73 | expect(receiver.result.messages[0]).to.deep.include(expectedResult); 74 | }); 75 | 76 | it('should create package.json if cannot get projectName', async () => { 77 | 78 | const createPackageJsonStub = sandbox.stub(receiver, '_createPackageJson'); 79 | sandbox.stub(context, '_loadPackageJsonConfig').callsFake(() => { 80 | receiver.context.packageJsonConfig = { name: 'fakeName' }; 81 | }); 82 | sandbox.stub(receiver.git, 'setOrigin'); 83 | sandbox.stub(receiver.git, 'push'); 84 | sandbox.stub(receiver.http, 'post').resolves({ body: '{"name":"fakeName","url":"","webhook":"true","saved":"true","pipeline":"true"}' } as CustomOkResponse); 85 | createJenkinsfileStub.resolves(); 86 | fsExistsSyncStub.returns(true); 87 | 88 | await receiver.init(); 89 | 90 | expect(createPackageJsonStub).to.have.been.calledOnce; 91 | }); 92 | 93 | it('should throw file required error if package.json is empty', async () => { 94 | 95 | sandbox.stub(receiver, '_createPackageJson'); 96 | 97 | try { 98 | await receiver.init(); 99 | } catch (e) { 100 | return expect(e.message).to.be.eq('package.json file is required.'); 101 | } 102 | 103 | chai.assert.fail('_getProjectName function should throw error if package.json is empty'); 104 | }); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /src/test/receivers/starter-receiver.test.ts: -------------------------------------------------------------------------------- 1 | import Context from '../../context'; 2 | import StarterReceiver from '../../receivers/starter-receiver'; 3 | import { CustomOkResponse } from '../../utils/http-wrapper'; 4 | import { createSandbox } from 'sinon'; 5 | import { expect } from 'chai'; 6 | 7 | describe('Receiver: starter', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | let context: Context, 12 | receiver: StarterReceiver; 13 | 14 | beforeEach(() => { 15 | sandbox.stub(Context.prototype, 'authenticateUser'); 16 | }); 17 | 18 | afterEach(() => { 19 | sandbox.reset(); 20 | sandbox.restore(); 21 | }); 22 | 23 | describe('Method: list', function () { 24 | 25 | async function testPrintStartersInTableFormat(context: Context) { 26 | 27 | receiver = new StarterReceiver(context); 28 | 29 | sandbox.stub(receiver.http, 'get').resolves({ 30 | body: JSON.stringify([ 31 | { 32 | productId: null, 33 | productTitle: 'Material Design for Bootstrap 4 (Vue)', 34 | productSlug: 'Vue-Bootstrap-with-Material-Design', 35 | available: true 36 | }, 37 | { 38 | productId: null, 39 | productTitle: 'Material Design for Bootstrap 5', 40 | productSlug: 'MDB5-Free', 41 | available: true 42 | }, 43 | { 44 | productId: 55506, 45 | productTitle: 'Material Design for Bootstrap Pro (Vue version)', 46 | productSlug: 'vue-ui-kit', 47 | available: false 48 | } 49 | ]) 50 | } as CustomOkResponse); 51 | 52 | const textLineStub = sandbox.stub(receiver.result, 'addTextLine'); 53 | 54 | await receiver.list(); 55 | 56 | expect(textLineStub).to.have.been.called; 57 | } 58 | 59 | it('should print starters in a table format when command is `mdb starter ls`', async function () { 60 | 61 | context = new Context('starter', 'ls', [], []); 62 | await testPrintStartersInTableFormat(context); 63 | }); 64 | 65 | it('should print starters in a table format when command is `mdb starters`', async function () { 66 | 67 | context = new Context('starters', '', [], []); 68 | await testPrintStartersInTableFormat(context); 69 | }); 70 | 71 | it('should print error if failed to fetch products', async function () { 72 | 73 | context = new Context('starter', 'ls', [], []); 74 | receiver = new StarterReceiver(context); 75 | 76 | sandbox.stub(receiver.http, 'get').rejects(new Error('fake error')); 77 | 78 | const alertStub = sandbox.stub(receiver.result, 'addAlert'); 79 | 80 | await receiver.list(); 81 | 82 | expect(alertStub).to.have.been.calledWith('red'); 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /src/test/receivers/strategies/auth/auth-strategy.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import config from '../../../../config'; 4 | import { AuthStrategy } from '../../../../receivers/strategies/auth'; 5 | import fs from 'fs'; 6 | import { createSandbox, SinonStub } from 'sinon'; 7 | import { expect } from 'chai'; 8 | 9 | describe('Strategy: AuthStrategy', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let strategy: AuthStrategy; 14 | 15 | beforeEach(() => { 16 | 17 | // @ts-ignore 18 | strategy = new AuthStrategy(); 19 | sandbox.stub(config, 'tokenDir').value('fake/token/dir'); 20 | sandbox.stub(config, 'tokenFile').value('fake-token-file'); 21 | }); 22 | 23 | afterEach(() => { 24 | 25 | sandbox.reset(); 26 | sandbox.restore(); 27 | }); 28 | 29 | describe('logout', () => { 30 | 31 | let unlinkStub: SinonStub; 32 | 33 | beforeEach(() => unlinkStub = sandbox.stub(fs, 'unlinkSync')); 34 | 35 | it('should logout user', () => { 36 | 37 | unlinkStub.resolves(); 38 | 39 | const result = strategy.logout(); 40 | 41 | expect(result).to.be.null; 42 | }); 43 | 44 | it('should return expected result if user is not logged in', () => { 45 | 46 | const expectedResult = 'You are not logged in.'; 47 | unlinkStub.throws({ code: 'ENOENT' }); 48 | 49 | const result = strategy.logout(); 50 | 51 | expect(result).to.be.eq(expectedResult); 52 | }); 53 | 54 | it('should return expected result if error', () => { 55 | 56 | const expectedResult = 'Logout failed: Fake error'; 57 | unlinkStub.throws({ message: 'Fake error' }); 58 | 59 | const result = strategy.logout(); 60 | 61 | expect(result).to.be.eq(expectedResult); 62 | }); 63 | }); 64 | 65 | describe('saveToken', () => { 66 | 67 | const fakeToken = 'fake.user.token'; 68 | 69 | let mkdirStub: SinonStub, 70 | writeStub: SinonStub; 71 | 72 | beforeEach(() => { 73 | 74 | mkdirStub = sandbox.stub(fs, 'mkdirSync'); 75 | writeStub = sandbox.stub(fs, 'writeFileSync'); 76 | }); 77 | 78 | it('should save token and return expected result', () => { 79 | 80 | const result = strategy.saveToken(fakeToken); 81 | 82 | expect(result).to.be.eq(true); 83 | }); 84 | 85 | it('should return expected result if no token', () => { 86 | 87 | // @ts-ignore 88 | const result = strategy.saveToken(); 89 | 90 | expect(result).to.be.eq(false); 91 | }); 92 | 93 | it('should return expected result if error', () => { 94 | 95 | mkdirStub.throws({ message: 'Fake error' }); 96 | 97 | const result = strategy.saveToken(fakeToken); 98 | 99 | expect(result).to.be.eq(false); 100 | }); 101 | }); 102 | }); 103 | -------------------------------------------------------------------------------- /src/test/receivers/strategies/auth/facebook-auth-strategy.test.ts: -------------------------------------------------------------------------------- 1 | import config from '../../../../config' 2 | import { FacebookAuthStrategy } from '../../../../receivers/strategies/auth'; 3 | import { createSandbox } from 'sinon'; 4 | import { expect } from 'chai'; 5 | 6 | describe('Strategy: FacebookAuthStrategy', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | const fakeUrl = 'fakeFacebookUrl'; 11 | 12 | it('should have a proper socialAuthUrl', () => { 13 | 14 | sandbox.stub(config.auth.social.facebook, 'url').value(fakeUrl); 15 | const strategy = new FacebookAuthStrategy(); 16 | 17 | const result = strategy.socialAuthUrl; 18 | 19 | expect(result).to.be.eq(fakeUrl); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/test/receivers/strategies/auth/google-auth-strategy.test.ts: -------------------------------------------------------------------------------- 1 | import config from '../../../../config'; 2 | import { GoogleAuthStrategy } from '../../../../receivers/strategies/auth'; 3 | import { createSandbox } from 'sinon'; 4 | import { expect } from 'chai'; 5 | 6 | describe('Strategy: GoogleAuthStrategy', () => { 7 | 8 | const sandbox = createSandbox(); 9 | 10 | const fakeUrl = 'fakeGoogleUrl'; 11 | 12 | it('should have a proper socialAuthUrl', () => { 13 | 14 | sandbox.stub(config.auth.social.google, 'url').value(fakeUrl); 15 | const strategy = new GoogleAuthStrategy(); 16 | 17 | const result = strategy.socialAuthUrl; 18 | 19 | expect(result).to.be.eq(fakeUrl); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/test/receivers/strategies/auth/twitter-auth-strategy.test.ts: -------------------------------------------------------------------------------- 1 | import config from '../../../../config'; 2 | import helpers from '../../../../helpers'; 3 | import { AuthStrategy, TwitterAuthStrategy } from '../../../../receivers/strategies/auth'; 4 | import { createSandbox, SinonStub } from 'sinon'; 5 | import { expect } from 'chai'; 6 | 7 | describe('Strategy: TwitterAuthStrategy', () => { 8 | 9 | const sandbox = createSandbox(); 10 | 11 | const fakeUrl = 'fakeTwitterUrl'; 12 | 13 | let strategy: TwitterAuthStrategy, 14 | saveTokenStub: SinonStub; 15 | 16 | beforeEach(() => { 17 | 18 | saveTokenStub = sandbox.stub(AuthStrategy.prototype, 'saveToken'); 19 | sandbox.stub(helpers, 'createTextPrompt').resolves('fake.user.token'); 20 | sandbox.stub(TwitterAuthStrategy.prototype, 'openSocialLoginBrowser'); 21 | strategy = new TwitterAuthStrategy(); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should have a proper socialAuthUrl', () => { 31 | 32 | sandbox.stub(config.auth.social.twitter, 'url').value(fakeUrl); 33 | 34 | const result = strategy.socialAuthUrl; 35 | 36 | expect(result).to.be.eq(fakeUrl); 37 | }); 38 | 39 | it('should login user and return expected result', async () => { 40 | 41 | const strategy = new TwitterAuthStrategy(); 42 | sandbox.stub(strategy, 'socialAuthUrl').value('fakeUrl'); 43 | saveTokenStub.returns(true); 44 | 45 | const result = await strategy.login(); 46 | 47 | expect(result).to.be.eq(''); 48 | }); 49 | 50 | it('should return expected result if login fails', async () => { 51 | 52 | const expectedResult = 'Login failed. Could not save token.'; 53 | sandbox.stub(strategy, 'socialAuthUrl').value('fakeUrl'); 54 | saveTokenStub.returns(false); 55 | 56 | const result = await strategy.login(); 57 | 58 | expect(result).to.be.eq(expectedResult); 59 | }); 60 | }); 61 | -------------------------------------------------------------------------------- /src/test/receivers/strategies/config/normal-config-strategy.test.ts: -------------------------------------------------------------------------------- 1 | import config from '../../../../config'; 2 | import Context from '../../../../context'; 3 | import CommandResult from '../../../../utils/command-result'; 4 | import { NormalConfigStrategy } from '../../../../receivers/strategies/config'; 5 | import DotMdbConfigManager from '../../../../utils/managers/dot-mdb-config-manager'; 6 | import { createSandbox, SinonStub } from 'sinon'; 7 | import { expect } from 'chai'; 8 | 9 | describe('Strategy: NormalConfigStrategy', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let result: CommandResult, 14 | context: Context, 15 | strategy: NormalConfigStrategy, 16 | save: SinonStub, 17 | setValue: SinonStub, 18 | unsetValue: SinonStub; 19 | 20 | beforeEach(() => { 21 | 22 | result = new CommandResult(); 23 | sandbox.stub(process, 'cwd').returns('fake/cwd'); 24 | sandbox.stub(Context.prototype, 'authenticateUser'); 25 | sandbox.stub(config, 'tokenDir').value('fake/token/dir'); 26 | sandbox.stub(config, 'tokenFile').value('fake-token-file'); 27 | save = sandbox.stub(DotMdbConfigManager.prototype, 'save'); 28 | setValue = sandbox.stub(DotMdbConfigManager.prototype, 'setValue'); 29 | unsetValue = sandbox.stub(DotMdbConfigManager.prototype, 'unsetValue'); 30 | }); 31 | 32 | afterEach(() => { 33 | 34 | sandbox.reset(); 35 | sandbox.restore(); 36 | }); 37 | 38 | describe('Method: setValue()', function () { 39 | 40 | it('should throw error if invalid package manager', function () { 41 | 42 | context = new Context('', '', [], []); 43 | context.registerNonArgFlags(['global']); 44 | strategy = new NormalConfigStrategy(context); 45 | 46 | try { 47 | strategy.setValue('packageManager', 'value'); 48 | } catch (e) { 49 | expect(e.message.toLowerCase()).to.include('invalid'); 50 | return; 51 | } 52 | 53 | chai.assert.fail('ConfigStrategy should throw an error'); 54 | }); 55 | 56 | it('should throw error if invalid publish method', function () { 57 | 58 | context = new Context('', '', [], []); 59 | context.registerNonArgFlags(['global']); 60 | strategy = new NormalConfigStrategy(context); 61 | 62 | try { 63 | strategy.setValue('publishMethod', 'value'); 64 | } catch (e) { 65 | expect(e.message.toLowerCase()).to.include('invalid'); 66 | return; 67 | } 68 | 69 | chai.assert.fail('ConfigStrategy should throw an error'); 70 | }); 71 | 72 | it('should set and save global config value', function () { 73 | 74 | context = new Context('', '', [], ['--global']); 75 | context.registerNonArgFlags(['global']); 76 | strategy = new NormalConfigStrategy(context); 77 | 78 | strategy.setValue('packageManager', 'npm'); 79 | 80 | sandbox.assert.calledOnceWithExactly(setValue, 'packageManager', 'npm', true); 81 | sandbox.assert.calledOnceWithExactly(save, 'fake/cwd', true); 82 | }); 83 | 84 | it('should set and save config value', function () { 85 | 86 | context = new Context('', '', [], []); 87 | context.registerNonArgFlags(['global']); 88 | strategy = new NormalConfigStrategy(context); 89 | 90 | strategy.setValue('publishMethod', 'ftp'); 91 | 92 | sandbox.assert.calledOnceWithExactly(setValue, 'publishMethod', 'ftp', false); 93 | sandbox.assert.calledOnceWithExactly(save, 'fake/cwd', false); 94 | }); 95 | }); 96 | 97 | describe('Method: unsetValue()', function () { 98 | 99 | it('should unset global config value', function () { 100 | 101 | context = new Context('', '', [], ['--global', '--unset']); 102 | context.registerNonArgFlags(['global', 'unset']); 103 | strategy = new NormalConfigStrategy(context); 104 | 105 | strategy.unsetValue('name'); 106 | 107 | sandbox.assert.calledOnceWithExactly(unsetValue, 'name', true); 108 | sandbox.assert.calledOnceWithExactly(save, 'fake/cwd', true); 109 | }); 110 | 111 | it('should unset config value', function () { 112 | 113 | context = new Context('', '', [], ['--unset']); 114 | context.registerNonArgFlags(['global', 'unset']); 115 | strategy = new NormalConfigStrategy(context); 116 | 117 | strategy.unsetValue('name'); 118 | 119 | sandbox.assert.calledOnceWithExactly(unsetValue, 'name', false); 120 | sandbox.assert.calledOnceWithExactly(save, 'fake/cwd', false); 121 | }); 122 | }); 123 | }); 124 | -------------------------------------------------------------------------------- /src/test/receivers/strategies/config/project-name-config-strategy.test.ts: -------------------------------------------------------------------------------- 1 | import config from '../../../../config'; 2 | import Context from '../../../../context'; 3 | import CommandResult from '../../../../utils/command-result'; 4 | import DotMdbConfigManager from '../../../../utils/managers/dot-mdb-config-manager'; 5 | import { ProjectNameConfigStrategy } from '../../../../receivers/strategies/config'; 6 | import { createSandbox, SinonStub } from 'sinon'; 7 | 8 | describe('Strategy: ProjectConfigStrategy', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | let result: CommandResult, 13 | context: Context, 14 | strategy: ProjectNameConfigStrategy, 15 | loadPackageJsonConfig: SinonStub, 16 | setPackageJsonValue: SinonStub, 17 | save: SinonStub, 18 | setValue: SinonStub, 19 | unsetValue: SinonStub; 20 | 21 | beforeEach(() => { 22 | 23 | result = new CommandResult(); 24 | sandbox.stub(process, 'cwd').returns('fake/cwd'); 25 | sandbox.stub(Context.prototype, 'authenticateUser'); 26 | sandbox.stub(config, 'tokenDir').value('fake/token/dir'); 27 | sandbox.stub(config, 'tokenFile').value('fake-token-file'); 28 | save = sandbox.stub(DotMdbConfigManager.prototype, 'save'); 29 | setValue = sandbox.stub(DotMdbConfigManager.prototype, 'setValue'); 30 | unsetValue = sandbox.stub(DotMdbConfigManager.prototype, 'unsetValue'); 31 | loadPackageJsonConfig = sandbox.stub(Context.prototype, '_loadPackageJsonConfig'); 32 | setPackageJsonValue = sandbox.stub(Context.prototype, 'setPackageJsonValue'); 33 | }); 34 | 35 | afterEach(() => { 36 | 37 | sandbox.reset(); 38 | sandbox.restore(); 39 | }); 40 | 41 | describe('Method: setValue()', function () { 42 | 43 | it('should set projectName in .mdb config file', function () { 44 | 45 | context = new Context('', '', [], []); 46 | context.packageJsonConfig = {}; 47 | strategy = new ProjectNameConfigStrategy(context); 48 | 49 | strategy.setValue('projectName', 'fake-name'); 50 | 51 | sandbox.assert.notCalled(setPackageJsonValue); 52 | sandbox.assert.calledOnceWithExactly(setValue, 'projectName', 'fake-name'); 53 | sandbox.assert.calledOnceWithExactly(save); 54 | }); 55 | 56 | it('should set projectName in package.json and .mdb config file', function () { 57 | 58 | context = new Context('', '', [], []); 59 | context.packageJsonConfig = { name: '' }; 60 | strategy = new ProjectNameConfigStrategy(context); 61 | 62 | strategy.setValue('projectName', 'fake-name'); 63 | 64 | sandbox.assert.calledOnceWithExactly(setPackageJsonValue, 'name', 'fake-name'); 65 | sandbox.assert.calledOnceWithExactly(setValue, 'projectName', 'fake-name'); 66 | sandbox.assert.calledOnceWithExactly(save); 67 | }); 68 | }); 69 | 70 | describe('Method: unsetValue()', function () { 71 | 72 | it('should unset projectName from .mdb config file', function () { 73 | 74 | context = new Context('', '', [], []); 75 | context.packageJsonConfig = {}; 76 | strategy = new ProjectNameConfigStrategy(context); 77 | 78 | strategy.unsetValue('projectName'); 79 | 80 | sandbox.assert.calledOnceWithExactly(unsetValue, 'projectName'); 81 | sandbox.assert.calledOnceWithExactly(save); 82 | }); 83 | }); 84 | }); 85 | -------------------------------------------------------------------------------- /src/test/receivers/strategies/publish/ftp-publish-strategy.test.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import fse from 'fs-extra'; 3 | import btoa from 'btoa'; 4 | import helpers from '../../../../helpers'; 5 | import Context from '../../../../context'; 6 | import CommandResult from '../../../../utils/command-result'; 7 | import { FtpPublishStrategy } from '../../../../receivers/strategies/publish'; 8 | import { createSandbox, SinonStub } from 'sinon'; 9 | import { expect } from 'chai'; 10 | 11 | describe('Strategy: FtpPublishStrategy', () => { 12 | 13 | const sandbox = createSandbox(); 14 | 15 | let commandResult: CommandResult, 16 | context: Context, 17 | runBuildStub: SinonStub, 18 | strategy: FtpPublishStrategy; 19 | 20 | beforeEach(() => { 21 | runBuildStub = sandbox.stub(FtpPublishStrategy.prototype, 'runBuildScript').resolves(); 22 | sandbox.stub(CommandResult.prototype, 'addAlert'); 23 | commandResult = new CommandResult(); 24 | }); 25 | 26 | afterEach(() => { 27 | sandbox.reset(); 28 | sandbox.restore(); 29 | }); 30 | 31 | it('should not build project', async function () { 32 | 33 | context = new Context('', '', [], []); 34 | context.packageJsonConfig = {}; 35 | strategy = new FtpPublishStrategy(context, commandResult); 36 | // @ts-ignore 37 | strategy._loadMetaData(); 38 | 39 | await strategy.buildProject(); 40 | 41 | expect(runBuildStub).to.not.have.been.called; 42 | }); 43 | 44 | it('should build angular project', async function () { 45 | 46 | context = new Context('', '', [], []); 47 | context.packageJsonConfig = { scripts: { build: 'fake script' }, dependencies: { '@angular/core': 'fakever' } }; 48 | strategy = new FtpPublishStrategy(context, commandResult); 49 | // @ts-ignore 50 | strategy._loadMetaData(); 51 | 52 | sandbox.stub(helpers, 'deserializeJsonFile').resolves({ scripts: { build: '' } }); 53 | sandbox.stub(helpers, 'serializeJsonFile').resolves(); 54 | 55 | await strategy.buildProject(); 56 | 57 | expect(runBuildStub).to.have.been.calledOnce; 58 | }); 59 | 60 | it('should build react project', async function () { 61 | 62 | context = new Context('', '', [], []); 63 | context.packageJsonConfig = { scripts: { build: 'fake script' }, dependencies: { 'react': 'fakever' } }; 64 | context.userToken = `fake.${btoa(JSON.stringify({ name: 'fakename' }))}.fake`; 65 | strategy = new FtpPublishStrategy(context, commandResult); 66 | // @ts-ignore 67 | strategy._loadMetaData(); 68 | 69 | sandbox.stub(helpers, 'serializeJsonFile').resolves(); 70 | sandbox.stub(fs, 'existsSync').returns(false); 71 | 72 | await strategy.buildProject(); 73 | 74 | expect(runBuildStub).to.have.been.calledOnce; 75 | }); 76 | 77 | it('should build vue project', async function () { 78 | 79 | context = new Context('', '', [], []); 80 | context.packageJsonConfig = { scripts: { build: 'fake script' }, dependencies: { 'vue': 'fakever' } }; 81 | strategy = new FtpPublishStrategy(context, commandResult); 82 | // @ts-ignore 83 | strategy._loadMetaData(); 84 | 85 | sandbox.stub(fs, 'existsSync').returns(true); 86 | 87 | await strategy.buildProject(); 88 | 89 | expect(runBuildStub).to.have.been.calledOnce; 90 | }); 91 | 92 | it('should build custom project', async function () { 93 | 94 | context = new Context('', '', [], []); 95 | context.packageJsonConfig = { scripts: { build: 'fake script' } }; 96 | strategy = new FtpPublishStrategy(context, commandResult); 97 | // @ts-ignore 98 | strategy._loadMetaData(); 99 | 100 | sandbox.stub(fs, 'existsSync').returns(true); 101 | 102 | await strategy.buildProject(); 103 | 104 | expect(runBuildStub).to.have.been.calledOnce; 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /src/test/utils/command-result.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import CommandResult from "../../utils/command-result"; 4 | import { expect } from 'chai'; 5 | import { createSandbox, SinonStub } from 'sinon'; 6 | import { OutputColor } from "../../models/output-color"; 7 | 8 | describe('CommandResult', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | let result: CommandResult; 13 | 14 | beforeEach(() => { 15 | 16 | result = new CommandResult(); 17 | }); 18 | 19 | afterEach(() => { 20 | 21 | sandbox.reset(); 22 | sandbox.restore(); 23 | }); 24 | 25 | it('should get messages array', function () { 26 | 27 | const messages = result.messages; 28 | 29 | expect(messages).to.be.an('array'); 30 | }); 31 | 32 | describe('should fill messages array', function () { 33 | 34 | it('should add text line', function () { 35 | 36 | result.addTextLine('message'); 37 | 38 | expect(result.messages).to.be.an('array'); 39 | expect(result.messages[0].type).to.equal('text'); 40 | expect(result.messages[0].value).to.equal('message'); 41 | }); 42 | 43 | it('should add table', function () { 44 | 45 | 46 | result.addTable([{ test: 'value' }]); 47 | 48 | expect(result.messages).to.be.an('array'); 49 | expect(result.messages[0].type).to.equal('table'); 50 | expect(result.messages[0].value).to.have.deep.members([{ test: 'value' }]); 51 | }); 52 | 53 | it('should add alert', function () { 54 | 55 | result.addAlert(OutputColor.Green, 'title', 'body'); 56 | 57 | expect(result.messages).to.be.an('array'); 58 | expect(result.messages[0].type).to.equal('alert'); 59 | expect(result.messages[0].value).to.be.an('object'); 60 | }); 61 | }); 62 | 63 | describe('should emit live outputs', function () { 64 | 65 | it('should emit live text line', function () { 66 | 67 | const emitStub: SinonStub = sandbox.stub(result, 'emit'); 68 | result.liveTextLine('message'); 69 | 70 | expect(emitStub.called).to.be.true; 71 | expect(emitStub.calledWith('mdb.cli.live.output', { messages: [{ type: 'text', value: 'message' }] })).to.be.ok; 72 | }); 73 | 74 | it('should emit live table', function () { 75 | 76 | const emitStub: SinonStub = sandbox.stub(result, 'emit'); 77 | result.liveTable([{ test: 'message' }]); 78 | 79 | expect(emitStub.called).to.be.true; 80 | expect(emitStub.calledWith('mdb.cli.live.output', { messages: [{ type: 'table', value: [{ test: 'message' }] }] })).to.be.ok; 81 | }); 82 | 83 | it('should emit live alert', function () { 84 | 85 | const emitStub: SinonStub = sandbox.stub(result, 'emit'); 86 | result.liveAlert(OutputColor.Red, 'title', 'body'); 87 | 88 | expect(emitStub.called).to.be.true; 89 | expect(emitStub.calledWith('mdb.cli.live.output', { messages: [{ type: 'alert', value: { title: 'title', body: 'body' }, color: 'red' }] })).to.be.ok; 90 | }); 91 | }); 92 | }); 93 | -------------------------------------------------------------------------------- /src/test/utils/managers/npm-package-manager.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import PackageManager from "../../../utils/managers/package-manager"; 4 | import NpmPackageManager from "../../../utils/managers/npm-package-manager"; 5 | import { expect } from 'chai'; 6 | import { createSandbox, SinonStub } from 'sinon'; 7 | 8 | describe('Utils: NpmPackageManager', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | const fakeCwd = 'fake/cwd'; 13 | 14 | let manager: NpmPackageManager, 15 | taskStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | manager = new NpmPackageManager(); 20 | // @ts-ignore 21 | taskStub = sandbox.stub(PackageManager.prototype, '_task'); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should cmdCommand getter return correct command', () => { 31 | 32 | expect(manager.cmdCommand).to.be.eq('npm'); 33 | }); 34 | 35 | it('should spawn `npm init` with expected arguments', async () => { 36 | 37 | await manager.init(fakeCwd); 38 | 39 | sandbox.assert.calledWith(taskStub, ['init'], 'package.json created. Proceeding...', 'package.json initialization failed', fakeCwd); 40 | }); 41 | 42 | it('should spawn `npm build` with expected arguments', async () => { 43 | 44 | await manager.build(fakeCwd); 45 | 46 | sandbox.assert.calledWith(taskStub, ['run', 'build'], 'Project built successfully', 'Project could not be built', fakeCwd); 47 | }); 48 | 49 | it('should spawn `npm test` with expected arguments', async () => { 50 | 51 | await manager.test(); 52 | 53 | sandbox.assert.calledWith(taskStub, ['run', 'test'], 'Tests ran successfully', 'Tests failed'); 54 | }); 55 | 56 | it('should spawn `npm i` with expected arguments', async () => { 57 | 58 | await manager.update(); 59 | 60 | sandbox.assert.calledWith(taskStub, ['i', '-g', 'mdb-cli'], 'Successfully updated', 'Update failed'); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/test/utils/managers/package-manager-loader.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import PackageManagerLoader from "../../../utils/managers/package-manager-loader"; 4 | import YarnPackageManager from "../../../utils/managers/yarn-package-manager"; 5 | import NpmPackageManager from "../../../utils/managers/npm-package-manager"; 6 | import { PackageManagers } from "../../../models/package-managers"; 7 | import helpers from '../../../helpers'; 8 | import { createSandbox } from 'sinon'; 9 | import { expect } from 'chai'; 10 | 11 | describe('Utils: PackageManager', () => { 12 | 13 | const sandbox = createSandbox(); 14 | 15 | afterEach(() => { 16 | 17 | sandbox.reset(); 18 | sandbox.restore(); 19 | }); 20 | 21 | describe('Method: load', () => { 22 | 23 | it('should load npm package manager if defined in arguments', async () => { 24 | 25 | const result = await PackageManagerLoader.load(PackageManagers.NPM); 26 | 27 | expect(result).to.be.an.instanceOf(NpmPackageManager); 28 | }); 29 | 30 | it('should load yarn package manager if defined in arguments', async () => { 31 | 32 | const result = await PackageManagerLoader.load(PackageManagers.YARN); 33 | 34 | expect(result).to.be.an.instanceOf(YarnPackageManager); 35 | }); 36 | 37 | it('should load package manager if not defined in arguments', async () => { 38 | 39 | sandbox.stub(helpers, 'createListPrompt').resolves('yarn'); 40 | 41 | const result = await PackageManagerLoader.load(); 42 | 43 | expect(result).to.be.an.instanceOf(YarnPackageManager); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /src/test/utils/managers/yarn-package-manager.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import PackageManager from "../../../utils/managers/package-manager"; 4 | import YarnPackageManager from "../../../utils/managers/yarn-package-manager"; 5 | import { expect } from 'chai'; 6 | import { createSandbox, SinonStub } from 'sinon'; 7 | 8 | describe('Utils: YarnPackageManager', () => { 9 | 10 | const sandbox = createSandbox(); 11 | 12 | const fakeCwd = 'fake/cwd'; 13 | 14 | let manager: YarnPackageManager, 15 | taskStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | manager = new YarnPackageManager(); 20 | // @ts-ignore 21 | taskStub = sandbox.stub(PackageManager.prototype, '_task'); 22 | }); 23 | 24 | afterEach(() => { 25 | 26 | sandbox.reset(); 27 | sandbox.restore(); 28 | }); 29 | 30 | it('should cmdCommand getter return correct command', () => { 31 | 32 | expect(manager.cmdCommand).to.be.eq('yarn'); 33 | }); 34 | 35 | it('should spawn `yarn init` with expected arguments', async () => { 36 | 37 | await manager.init(fakeCwd); 38 | 39 | sandbox.assert.calledWith(taskStub, ['init'], 'package.json created. Proceeding...', 'package.json initialization failed', fakeCwd); 40 | }); 41 | 42 | it('should spawn `yarn build` with expected arguments', async () => { 43 | 44 | await manager.build(fakeCwd); 45 | 46 | sandbox.assert.calledWith(taskStub, ['build'], 'Project built successfully', 'Project could not be built', fakeCwd); 47 | }); 48 | 49 | it('should spawn `yarn test` with expected arguments', async () => { 50 | 51 | await manager.test(); 52 | 53 | sandbox.assert.calledWith(taskStub, ['test'], 'Tests ran successfully', 'Tests failed'); 54 | }); 55 | 56 | it('should spawn `yarn global add` with expected arguments', async () => { 57 | 58 | await manager.update(); 59 | 60 | sandbox.assert.calledWith(taskStub, ['global', 'add', 'mdb-cli'], 'Successfully updated', 'Update failed'); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/test/utils/output-printer.test.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import OutputPrinter from "../../utils/output-printer"; 4 | import { expect } from 'chai'; 5 | import { createSandbox, SinonStub } from 'sinon'; 6 | import CommandResult from "../../utils/command-result"; 7 | import { OutputColor } from "../../models/output-color"; 8 | 9 | describe('OutputPrinter', () => { 10 | 11 | const sandbox = createSandbox(); 12 | 13 | let printer: OutputPrinter, 14 | consoleLogStub: SinonStub, 15 | consoleTableStub: SinonStub; 16 | 17 | beforeEach(() => { 18 | 19 | printer = new OutputPrinter(); 20 | 21 | consoleLogStub = sandbox.stub(console, 'log'); 22 | consoleTableStub = sandbox.stub(console, 'table'); 23 | }); 24 | 25 | afterEach(() => { 26 | 27 | sandbox.reset(); 28 | sandbox.restore(); 29 | }); 30 | 31 | describe('should print results in console', function () { 32 | 33 | it('should print console log text', function () { 34 | 35 | let results: CommandResult = new CommandResult(); 36 | 37 | results.addTextLine('message'); 38 | results.addTextLine('message2'); 39 | 40 | printer.print([results]); 41 | 42 | expect( consoleLogStub.calledTwice ).to.be.true; 43 | }); 44 | 45 | it('should print console table', function () { 46 | 47 | let results: CommandResult = new CommandResult(); 48 | 49 | results.addTable([{message: 'message'}]); 50 | results.addTable([{message: 'message'}]); 51 | 52 | printer.print([results]); 53 | 54 | expect( consoleTableStub.calledTwice ).to.be.true; 55 | }); 56 | 57 | it('should print console log alert', function () { 58 | 59 | let results: CommandResult = new CommandResult(); 60 | 61 | results.addAlert(OutputColor.Red, 'title', 'body'); 62 | results.addAlert(OutputColor.Green, 'title2', 'body2'); 63 | 64 | printer.print([results]); 65 | 66 | expect( consoleLogStub.calledTwice ).to.be.true; 67 | }); 68 | }); 69 | }); 70 | -------------------------------------------------------------------------------- /src/utils/command-result.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import EventEmitter from "events"; 4 | import {OutputColor} from "../models/output-color"; 5 | import {AlertMessage, MessageType, TableMessage, TextLineMessage} from "../models/output-message"; 6 | 7 | class CommandResult extends EventEmitter { 8 | 9 | private _messages: Array = []; 10 | 11 | constructor(...args: any[]) { 12 | super(...args); 13 | } 14 | 15 | get messages() { 16 | return this._messages; 17 | } 18 | 19 | addTextLine(msg: TextLineMessage['value']) { 20 | this._messages.push({ type: 'text', value: msg }); 21 | } 22 | 23 | addTable(table: TableMessage['value']) { 24 | this._messages.push({ type: 'table', value: table }); 25 | } 26 | 27 | addAlert(color: OutputColor, title: AlertMessage['value']['title'], body: AlertMessage['value']['body']) { 28 | this._messages.push({ type: 'alert', value: { title, body }, color }); 29 | } 30 | 31 | liveTextLine(msg: TextLineMessage['value']) { 32 | this._liveOutput({ type: 'text', value: msg }); 33 | } 34 | 35 | liveTable(table: TableMessage['value']) { 36 | this._liveOutput({ type: 'table', value: table }); 37 | } 38 | 39 | liveAlert(color: OutputColor, title: AlertMessage['value']['title'], body: AlertMessage['value']['body']) { 40 | this._liveOutput({ type: 'alert', value: { title, body }, color }); 41 | } 42 | 43 | private _liveOutput(msg: { type: MessageType, value: TextLineMessage['value'] | TableMessage['value'] | AlertMessage['value'], color?: OutputColor }) { 44 | this.emit('mdb.cli.live.output', { messages: [msg] }); 45 | } 46 | } 47 | 48 | export default CommandResult; 49 | -------------------------------------------------------------------------------- /src/utils/managers/dot-mdb-config-manager.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import config from '../../config'; 4 | import { ProjectEntry } from '../../models'; 5 | 6 | import { DOT_MDB_SCHEME, DotMdb, DotMdbGlobal, DOT_MDB_GLOBAL_SCHEME } from "../../models/dot-mdb"; 7 | 8 | class DotMdbConfigManager { 9 | 10 | public mdbConfig: DotMdb = {}; 11 | public globalConfig: DotMdbGlobal = {}; 12 | 13 | constructor() { 14 | this.load(); 15 | } 16 | 17 | getValue(keyPath: string, global: boolean = false): string | undefined { 18 | 19 | this.validateConfigKey(keyPath); 20 | 21 | const pathSplit = keyPath.split('.'); 22 | let temp: DotMdb | DotMdbGlobal = { ...(global ? this.globalConfig : this.mdbConfig) }; 23 | for (let part of pathSplit) { 24 | if (!temp.hasOwnProperty(part)) { 25 | return undefined; 26 | } 27 | // @ts-ignore 28 | temp = temp[part]; 29 | } 30 | 31 | return temp as string; 32 | } 33 | 34 | setValue(keyPath: string, value: string | ProjectEntry[], global: boolean = false) { 35 | 36 | this.validateConfigKey(keyPath, global); 37 | 38 | this._setKeyPathValue(global ? this.globalConfig : this.mdbConfig, keyPath, value); 39 | } 40 | 41 | unsetValue(keyPath: string, global: boolean = false) { 42 | 43 | this.validateConfigKey(keyPath, global); 44 | 45 | this._unsetKeyPathValue(global ? this.globalConfig : this.mdbConfig, keyPath); 46 | } 47 | 48 | validateConfigKey(keyPath: string, global: boolean = false) { 49 | 50 | const pathSplit = keyPath.split('.'); 51 | let temp = global ? DOT_MDB_GLOBAL_SCHEME : DOT_MDB_SCHEME; 52 | pathSplit.forEach((part) => { 53 | if (!temp.hasOwnProperty(part)) { 54 | throw new Error(`Invalid config: ${keyPath}`); 55 | } 56 | // @ts-ignore 57 | temp = temp[part]; 58 | }); 59 | } 60 | 61 | save(cwd = process.cwd(), global: boolean = false) { 62 | const settingsPath = path.join(global ? config.tokenDir : cwd, '.mdb'); 63 | const settings = global ? this.globalConfig : this.mdbConfig; 64 | 65 | try { 66 | fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8'); 67 | } catch (e) { 68 | throw new Error(`Could not save config file: ${e}`); 69 | } 70 | } 71 | 72 | load() { 73 | const settingsPath = path.join(process.cwd(), '.mdb'); 74 | const globalSettingsPath = path.join(config.tokenDir, '.mdb'); 75 | 76 | try { 77 | const config = fs.readFileSync(settingsPath, 'utf8'); 78 | this.mdbConfig = JSON.parse(config); 79 | } catch (e) { 80 | this.mdbConfig = {}; 81 | } 82 | 83 | try { 84 | const globalConfig = fs.readFileSync(globalSettingsPath, 'utf8'); 85 | this.globalConfig = JSON.parse(globalConfig); 86 | } catch (e) { 87 | this.globalConfig = {}; 88 | } 89 | } 90 | 91 | private _unsetKeyPathValue(object: DotMdb | DotMdbGlobal, keyPath: string) { 92 | const pathSplit = keyPath.split('.'); 93 | if (pathSplit.length === 1) { 94 | if (object.hasOwnProperty(keyPath)) { 95 | // @ts-ignore 96 | delete object[keyPath]; 97 | } 98 | return; 99 | } 100 | // @ts-ignore 101 | this._unsetKeyPathValue(object[pathSplit[0]] || {}, pathSplit.slice(1).join('.')); 102 | } 103 | 104 | private _setKeyPathValue(object: DotMdb | DotMdbGlobal, keyPath: string, value: string | ProjectEntry[]) { 105 | const pathSplit = keyPath.split('.'); 106 | if (pathSplit.length === 1) { 107 | // @ts-ignore 108 | return object[keyPath] = value; 109 | } else { 110 | // @ts-ignore 111 | object[pathSplit[0]] = object[pathSplit[0]] || {}; 112 | } 113 | // @ts-ignore 114 | this._setKeyPathValue(object[pathSplit[0]], pathSplit.slice(1).join('.'), value); 115 | } 116 | } 117 | 118 | export default DotMdbConfigManager; 119 | -------------------------------------------------------------------------------- /src/utils/managers/npm-package-manager.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import PackageManager from "./package-manager"; 4 | 5 | class NpmPackageManager extends PackageManager { 6 | 7 | get cmdCommand() { 8 | return 'npm'; 9 | } 10 | 11 | init(cwd?: string) { 12 | return this._task(['init'], 'package.json created. Proceeding...', 'package.json initialization failed', cwd); 13 | } 14 | 15 | build(cwd: string) { 16 | return this._task(['run', 'build'], 'Project built successfully', 'Project could not be built', cwd); 17 | } 18 | 19 | test() { 20 | return this._task(['run', 'test'], 'Tests ran successfully', 'Tests failed'); 21 | } 22 | 23 | update() { 24 | return this._task(['i', '-g', 'mdb-cli'], 'Successfully updated', 'Update failed'); 25 | } 26 | } 27 | 28 | export default NpmPackageManager; 29 | -------------------------------------------------------------------------------- /src/utils/managers/package-manager-loader.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import PackageManager from './package-manager'; 4 | import NpmPackageManager from './npm-package-manager'; 5 | import YarnPackageManager from './yarn-package-manager'; 6 | import { PackageManagers } from '../../models/package-managers'; 7 | import helpers from '../../helpers'; 8 | 9 | class PackageManagerLoader { 10 | 11 | static async load(manager?: PackageManagers): Promise { 12 | 13 | const choices = [PackageManagers.NPM, PackageManagers.YARN]; 14 | 15 | if (!manager) { 16 | const chosen = await helpers.createListPrompt('Which package manager do you use?', choices); 17 | manager = PackageManagers[chosen.toUpperCase() as keyof typeof PackageManagers]; 18 | } 19 | 20 | switch (manager) { 21 | case PackageManagers.NPM: return new NpmPackageManager(); 22 | case PackageManagers.YARN: return new YarnPackageManager(); 23 | } 24 | } 25 | } 26 | 27 | export default PackageManagerLoader; 28 | -------------------------------------------------------------------------------- /src/utils/managers/package-manager.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import childProcess from "child_process"; 4 | import {CliStatus} from "../../models/cli-status"; 5 | 6 | abstract class PackageManager { 7 | 8 | private isWindows = process.platform === 'win32'; 9 | 10 | abstract get cmdCommand(): string; 11 | 12 | abstract init(cwd?: string): Promise; 13 | 14 | abstract build(cwd: string): Promise; 15 | 16 | abstract test(): Promise; 17 | 18 | abstract update(): Promise; 19 | 20 | protected _task(args: string[], successMsg: string, errorMsg: string, cwd?: string): Promise { 21 | 22 | return new Promise((resolve, reject) => { 23 | const task = childProcess.spawn(this.cmdCommand, args, { cwd, stdio: 'inherit', ...(this.isWindows && { shell: true }) }); 24 | task.on('error', (error: number) => reject(error)); 25 | task.on('exit', (code: number) => code === CliStatus.SUCCESS ? resolve(successMsg) : reject(errorMsg)); 26 | }); 27 | } 28 | } 29 | 30 | export default PackageManager; 31 | -------------------------------------------------------------------------------- /src/utils/managers/yarn-package-manager.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import PackageManager from "./package-manager"; 4 | 5 | class YarnPackageManager extends PackageManager { 6 | 7 | get cmdCommand() { 8 | return 'yarn'; 9 | } 10 | 11 | init(cwd?: string) { 12 | return this._task(['init'], 'package.json created. Proceeding...', 'package.json initialization failed', cwd); 13 | } 14 | 15 | build(cwd: string) { 16 | return this._task(['build'], 'Project built successfully', 'Project could not be built', cwd); 17 | } 18 | 19 | test() { 20 | return this._task(['test'], 'Tests ran successfully', 'Tests failed'); 21 | } 22 | 23 | update() { 24 | return this._task(['global', 'add', 'mdb-cli'], 'Successfully updated', 'Update failed'); 25 | } 26 | } 27 | 28 | export default YarnPackageManager; 29 | -------------------------------------------------------------------------------- /src/utils/output-printer.ts: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { OutputColor } from "../models/output-color"; 4 | import CommandResult from "./command-result"; 5 | import { AlertMessage, TableMessage, TextLineMessage } from "../models/output-message"; 6 | 7 | class OutputPrinter { 8 | 9 | private consoleColors = { 10 | [OutputColor.Red]: '\x1b[31m%s\x1b[0m', 11 | [OutputColor.Green]: '\x1b[32m%s\x1b[0m', 12 | [OutputColor.Yellow]: '\x1b[33m%s\x1b[0m', 13 | [OutputColor.Blue]: '\x1b[34m%s\x1b[0m', 14 | [OutputColor.Purple]: '\x1b[35m%s\x1b[0m', 15 | [OutputColor.Turquoise]: '\x1b[36m%s\x1b[0m', 16 | [OutputColor.Grey]: '\x1b[37m%s\x1b[0m', 17 | [OutputColor.Inverted]: '\x1b[40m%s\x1b[0m', 18 | [OutputColor.InvertedRed]: '\x1b[41m%s\x1b[0m', 19 | [OutputColor.InvertedGreen]: '\x1b[42m%s\x1b[0m', 20 | [OutputColor.InvertedYellow]: '\x1b[43m%s\x1b[0m', 21 | [OutputColor.InvertedBlue]: '\x1b[44m%s\x1b[0m', 22 | [OutputColor.InvertedPurple]: '\x1b[45m%s\x1b[0m', 23 | [OutputColor.InvertedTurquoise]: '\x1b[46m%s\x1b[0m', 24 | [OutputColor.InvertedGrey]: '\x1b[47m%s\x1b[0m', 25 | [OutputColor.GreyBody]: '\x1b[0m%s\x1b[90m%s\x1b[0m' 26 | }; 27 | 28 | /** 29 | * @param results: CommandResult[] 30 | */ 31 | print(results: CommandResult[]) { 32 | 33 | results.forEach((cr) => { 34 | 35 | cr.messages.forEach((m: TextLineMessage | TableMessage | AlertMessage) => { 36 | 37 | if (m.type === 'text') { 38 | console.log((m as TextLineMessage).value); 39 | } else if (m.type === 'table') { 40 | console.table((m as TableMessage).value); 41 | } else if (m.type === 'alert') { 42 | console.log(this.consoleColors[(m as AlertMessage).color], (m as AlertMessage).value.title, (m as AlertMessage).value.body); 43 | } else { 44 | console.log(m.value); 45 | } 46 | }); 47 | }); 48 | } 49 | } 50 | 51 | export default OutputPrinter; -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "lib": ["es6"], 6 | "allowJs": true, 7 | "outDir": "dist", 8 | "rootDir": "src", 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "esModuleInterop": true, 12 | "resolveJsonModule": true, 13 | "skipLibCheck": true, 14 | "forceConsistentCasingInFileNames": true 15 | }, 16 | "include": [ 17 | "./src" 18 | ], 19 | "exclude": [ 20 | "./src/test/", 21 | "./src/postbuild.js" 22 | ] 23 | } 24 | --------------------------------------------------------------------------------