Skip to content

Commit

Permalink
feat: add commander
Browse files Browse the repository at this point in the history
  • Loading branch information
Adán Toscano López committed Oct 26, 2023
1 parent bb8d4d7 commit c9377b5
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 46 deletions.
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,27 @@ npm ci

## Usage

To start a guided configuration:

```sh
npx api-mock-runner
```

To explore options:

```sh
npx api-mock-runner -h
```

```sh
npm start
Usage: api-mock-runner [options]

Options:
-o, --origin <origin> path or repo containing schemas
-s, --schema [schemaPaths...] path to schemas
-p, --port [ports...] port to serve each schema
-r, --run-config use saved config
-h, --help display help for command
```

### Response selection
Expand Down
21 changes: 15 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "0.0.0",
"description": "Create mocks servers with its schemas",
"main": "src/index.js",
"bin": "src/index.js",
"type": "module",
"exports": {
".": {
Expand Down Expand Up @@ -37,7 +38,8 @@
"license": "MIT",
"dependencies": {
"@inquirer/prompts": "^3.1.2",
"@os3/open-api-mocker": "^2.1.0"
"@os3/open-api-mocker": "^2.1.0",
"commander": "^11.1.0"
},
"devDependencies": {
"@esm-bundle/chai": "^4.3.4-fix.0",
Expand Down
48 changes: 45 additions & 3 deletions src/index.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#!/usr/bin/env node

import * as fs from 'node:fs';
import { initWithConfigFile, startMockServer, init } from './services/user-flow-steps.js';
import { program } from 'commander';
import startMockServer from './services/start-mock-server.js';
import { initWithConfigFile, initWithSchemaPaths, init } from './services/user-flow-steps.js';
import { RC_FILE_NAME } from './services/utils.js';

/**
Expand All @@ -9,8 +13,46 @@ import { RC_FILE_NAME } from './services/utils.js';
* @returns {Promise<void>}
*/
const main = async () => {
program
.option('-o, --origin <origin>', 'path or repo containing schemas')
.option('-s, --schema [schemaPaths...]', 'path to schemas')
.option('-p, --port [ports...]', 'port to serve each schema')
.option('-r, --run-config', 'use saved config');

program.parse();

const options = program.opts();
const configFileExists = fs.existsSync(`${process.cwd()}/${RC_FILE_NAME}`);
const config = configFileExists ? await initWithConfigFile() : await init();
await startMockServer(config.selectedSchemas);
if (options.runConfig && !configFileExists) {
console.log('no config file found');
const config = await init();
return startMockServer(config.selectedSchemas);
}
if (options.runConfig) {
const config = JSON.parse(fs.readFileSync(`${process.cwd()}/${RC_FILE_NAME}`));
return startMockServer(config.selectedSchemas);
}
if (options?.origin) {
const config = await init({
origin: options.origin,
schemaPaths: options.schema,
ports: options.port,
});
return startMockServer(config.selectedSchemas);
}
if (options?.schema?.length) {
const config = await initWithSchemaPaths({
schemaPaths: options.schema,
ports: options.port,
});
return startMockServer(config.selectedSchemas);
}
if (configFileExists) {
const config = await initWithConfigFile();
return startMockServer(config.selectedSchemas);
}
const config = await init();
return startMockServer(config.selectedSchemas);
};

main();
2 changes: 1 addition & 1 deletion src/services/find-oas-from-dir.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const findOasFromDir = async (startPath, acc) => {
await findOasFromDir(filePath, oasFiles);
} else if ((file.endsWith('.yaml') || file.endsWith('.yml')) && (await isOas(filePath))) {
oasFiles.push({
filename: file,
fileName: file,
path: startPath,
filePath,
});
Expand Down
29 changes: 29 additions & 0 deletions src/services/start-mock-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import OpenApiMocker from '@os3/open-api-mocker';

/**
* @typedef {import('./user-flow-steps.js').Schema} Schema
*/

/**
* Start the mock server
* @async
* @function startMockServer
* @param {Schema[]} schemas - An array of schemas
* @returns {Promise<void>}
*/
async function startMockServer(schemas) {
for (const schema of schemas) {
const openApiMocker = new OpenApiMocker({
port: schema.port,
schema: schema.path,
watch: true,
});

await openApiMocker.validate();
await openApiMocker.mock();
// Separate each server with a empty line
console.log();
}
}

export default startMockServer;
89 changes: 55 additions & 34 deletions src/services/user-flow-steps.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { checkbox, confirm, input } from '@inquirer/prompts';
import * as fs from 'node:fs';
import OpenApiMocker from '@os3/open-api-mocker';
import { OpenApiSchemaNotFoundError } from '../errors/openapi-schema-not-found-error.js';
import cloneGitRepository from '../services/clone-git-repository.js';
import findOasFromDir from '../services/find-oas-from-dir.js';
Expand Down Expand Up @@ -68,27 +67,6 @@ async function getSchemas(origin) {
return schemas;
}

/**
* Start the mock server
* @async
* @function startMockServer
* @param {Schema[]} selectedSchemas - An array of schemas
* @returns {Promise<void>}
*/
async function startMockServer(selectedSchemas) {
for (let currentSchema of selectedSchemas) {
const openApiMocker = new OpenApiMocker({
port: currentSchema.port,
schema: currentSchema.path,
watch: true,
});
await openApiMocker.validate();

await openApiMocker.mock();
console.log();
}
}

/**
* get initial values from user
* @async
Expand All @@ -107,25 +85,31 @@ async function getOrigin() {
* Start flow without config
* @async
* @function init
* @property {Options} options - cli options
* @returns {Promise<Config>} A object with the complete config
* @throws {OpenApiSchemaNotFoundError} When no schemas are found in the given directory
*/
async function init() {
const schemasOrigin = await startNewFlow();

async function init({ origin, schemaPaths, ports } = {}) {
const schemasOrigin = origin || (await startNewFlow());
const schemas = await getSchemas(schemasOrigin);
if (!schemas.length) {
throw new OpenApiSchemaNotFoundError();
}
const schemasToMock = await checkbox({
message: 'Select a schema',
choices: schemas.map((schema) => {
return { name: schema.fileName, value: schema.filePath };
}),
// TODO: pending validation to ensure that at least one schema is selected. Waiting next inquirer release.
});

const selectedSchemas = await askForPorts(schemasToMock);
const schemasFilePaths = schemas.map((s) => s.filePath);
const schemaPathsAreAvailable = schemaPaths?.every((path) => schemasFilePaths.includes(path));

const schemasToMock = schemaPathsAreAvailable
? schemaPaths
: await checkbox({
message: 'Select a schema',
choices: schemas.map((schema) => {
return { value: schema.filePath };
}),
// TODO: pending validation to ensure that at least one schema is selected. Waiting next inquirer release.
});

const selectedSchemas = ports?.length ? assignPorts(schemasToMock, ports) : await askForPorts(schemasToMock);
const config = { schemasOrigin, selectedSchemas };

fs.writeFileSync(`${process.cwd()}/${RC_FILE_NAME}`, JSON.stringify(config, null, '\t'));
Expand All @@ -134,6 +118,29 @@ async function init() {
return config;
}

/**
* @typedef {Object} Options
* @property {string[]} schemaPaths - Local schema paths
* @property {string[]} ports - An array of ports
*/

/**
* Start flow without config
* @async
* @function init
* @property {Options} options - cli options
* @returns {Promise<Config>} A object with the complete config
*/
async function initWithSchemaPaths({ schemaPaths, ports } = {}) {
const selectedSchemas = ports?.length ? assignPorts(schemaPaths, ports) : await askForPorts(schemaPaths);
const config = { selectedSchemas };

fs.writeFileSync(`${process.cwd()}/${RC_FILE_NAME}`, JSON.stringify(config, null, '\t'));
console.log(config);

return config;
}

/**
* @typedef {Object} Schema
* @property {string} path - The path of the schema
Expand Down Expand Up @@ -164,4 +171,18 @@ async function askForPorts(schemaPaths) {
return selectedSchemas;
}

export { init, initWithConfigFile, startMockServer };
/**
* Assigns ports for each schema
* @function assignPorts
* @param {string[]} schemaPaths - An array of schemas
* @param {string[]} ports - An array of ports
* @returns {Schema[]} An array of selected Schemas
*/
function assignPorts(schemaPaths, ports) {
return schemaPaths.map((schemaPath, i) => {
const portNumber = Number.parseInt(ports[i]) || Number.parseInt(ports[ports.length - 1]) + (i + 1 - ports.length);
return { path: schemaPath, port: portNumber };
});
}

export { initWithConfigFile, initWithSchemaPaths, init };

0 comments on commit c9377b5

Please # to comment.