Skip to content

Commit

Permalink
feat: Enhance cli command line (#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgamez authored Jul 5, 2023
1 parent 632b880 commit 2bc59f9
Show file tree
Hide file tree
Showing 5 changed files with 398 additions and 61 deletions.
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,43 @@ yarn run dev

Open `localhost:8080` on your browser

### Command line
The GBFS validator can be used a command line. Find below the steps for using cli feature:

- Download or clone this repository
```shell
git clone https://github.com/fluctuo/gbfs-validator.git
cd gbfs-validator
```
- Executes the cli script using one of the listed below options

```shell
./gbfs-validator/cli.js -u {http_address_of_gbfs_dataset} -s {local_path_to_output_report_file}
```
or
```shell
node ./gbfs-validator/cli.js -u {http_address_of_gbfs_dataset} -s {local_path_to_output_report_file}
```

## CLI help parameter
To get the list of supported paramters
```shell
./gbfs-validator/cli.js --help
```

## Usage description and supported parameters
```
Usage: cli [OPTIONS]...
Options:
-v, --version output the version number
-u, --url <feed_url> URL of the GBFS feed
-vb, --verbose Verbose mode prints debugging console logs
-s, --save-report <report_path> Local path to output report file
-pr, --print-report <yes_no> Print report to standard output (choices: "yes", "no", default: "yes")
-h, --help display help for command
```

## Projects based on this validator

[transport.data.gouv.fr GBFS validator tool](https://transport.data.gouv.fr/validation?type=gbfs) - Tool displaying interactive geofencing, station, and vehicle maps, the validation results, and metadata of GBFS feeds.
Expand Down
94 changes: 53 additions & 41 deletions gbfs-validator/__test__/cli.test.js
Original file line number Diff line number Diff line change
@@ -1,57 +1,69 @@
let path = require('path')
let exec = require('node:child_process').exec

const serverOpts = {
port: 0,
host: '127.0.0.1',
}

describe('cli', () => {
describe('without arguments', () => {
test('should show help without required url', async () => {
const mockExit = jest.spyOn(process, 'exit').mockImplementation(() => {})
const mockConsoleError = jest
.spyOn(console, 'error')
.mockImplementation(() => {})

expect(() => {
require('../cli.js')
}).toThrow('Missing URL')

expect(mockExit).toHaveBeenCalledWith(1)
})
const cliExec = (args) => {
const command = `${path.resolve(`${__dirname}/../cli.js`)} ${args.join(' ')}`
return new Promise(resolve => {
exec(command,
// Setting exit override to allow program to exit simplifying unit testing
{ env: { ...process.env, 'EXIT_OVERRIDE': 'true' } },
(error, stdout, stderr) => {
resolve({
code: error && error.code ? error.code : 0,
error,
stdout,
stderr
})
})
})
}

describe('with arguments', () => {
let gbfsFeedServer
describe('cli', () => {

beforeAll(async () => {
gbfsFeedServer = require('./fixtures/server')()
let gbfsFeedServer
let feedUrl

await gbfsFeedServer.listen(serverOpts)
beforeAll(async () => {
gbfsFeedServer = require('./fixtures/server')()
await gbfsFeedServer.listen(serverOpts)
feedUrl = `http://${gbfsFeedServer.server.address().address}:${gbfsFeedServer.server.address().port}/gbfs.json`
})

return gbfsFeedServer
})
afterAll(() => {
return gbfsFeedServer.close()
})

afterAll(() => {
return gbfsFeedServer.close()
})
test('should show an error if url parameter is not set', async () => {
const result = await cliExec([])
expect(result.code).toBe(1)
expect(result.error.message).toContain('error: required option \'-u, --url <feed_url>\' not specified')
})

test('should run cli', async () => {
const url = `http://${gbfsFeedServer.server.address().address}:${
gbfsFeedServer.server.address().port
}`
test('should success and print the report when url parameter set and -pr is set as default', async () => {
const result = await cliExec([`-u`, `${feedUrl}`])
expect(result.code).toBe(0)
expect(result.stdout).toContain('summary:')
})

process.argv[2] = url
test('should show an error if pr parameter is set to `no` and -s is not set', async () => {
const result = await cliExec([`-u ${feedUrl}`, '-pr no'])
expect(result.code).toBe(1)
expect(result.stdout).toContain('Please set at least one of the following options: --save-report or --print-report')
})

const cli = await require('../cli.js')
test('should success when paramters url and save report has valid values and print report is set to no', async () => {
const result = await cliExec([`-u ${feedUrl}`, '-s /dev/null', '-pr no'])
expect(result.code).toBe(0)
})

expect(cli).toMatchObject({
summary: expect.objectContaining({
version: { detected: '2.2', validated: '2.2' },
hasErrors: true,
validatorVersion: '1.0.0',
errorsCount: 1
}),
files: expect.any(Array)
})
})
test('should success and print report when paramters url and save report are valid and print report is set to yes', async () => {
const result = await cliExec([`-u ${feedUrl}`, '-s /dev/null', '-pr yes'])
expect(result.code).toBe(0)
expect(result.stdout).toContain('summary:')
})
})
})
98 changes: 88 additions & 10 deletions gbfs-validator/cli.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,95 @@
#!/usr/bin/env node

const { inspect } = require('util')
const GBFS = require('./')
const commander = require('commander')
const fs = require('fs')
const fsPath = require('path')
const GBFS = require('./gbfs')
const pjson = require('./package.json')

getFeedValidationReport = async (url) => {
const gbfs = new GBFS(url)
return gbfs.validation()
}

const isExitOverrided = () => (process.env.EXIT_OVERRIDE === 'true')

if (!process.argv[2]) {
console.error('Usage: gbfs-validator GBFS_URL')
process.exit(1)
const printingReport = (options) => (options.printReport === 'yes')

const exitProcess = (code) => {
if (!isExitOverrided && code === 1) {
process.exit(code)
}
process.exit(0)
}

const gbfs = new GBFS(process.argv[2])
parseOptions = () => {
commander
.version(pjson.version, '-v, --version')
.usage('[OPTIONS]...')
.requiredOption('-u, --url <feed_url>', 'URL of the GBFS feed')
.option('-vb, --verbose', 'Verbose mode prints debugging console logs')
.option('-s, --save-report <report_path>', 'Local path to output report file')
.addOption(new commander.Option('-pr, --print-report <yes_no>', 'Print report to standard output').default('yes').choices(['yes', 'no']))

// Supporting friendly unit testing and possible CI integrations
// The process throw an exception on parsing error in addition to the parsing error
if (isExitOverrided()) {
commander.exitOverride()
}
return commander.parse(process.argv).opts()
}

const saveReport = (report, filePath) => {
const dirname = fsPath.dirname(filePath);
if (!fs.existsSync(dirname)) {
fs.mkdirSync(dirname, { recursive: true });
}
fs.writeFileSync(filePath, JSON.stringify(report))
}

const processFeedValidation = async (options) => {
if (options.verbose) {
console.log("Started GBFS validation with options: \n " + inspect(options, { depth: null, colors: true }))
}
try {
const report = await getFeedValidationReport(options.url)
if (printingReport(options)) {
console.log(inspect(report, { depth: null, colors: true }))
}
if (options.saveReport) {
saveReport(report, options.saveReport)
}
} catch (error) {
console.error(`Critical error while validating GBFS feed => ${error}`)
exitProcess(1);
}
}

const validate = () => {
const options = parseOptions()
if (!options.saveReport && !printingReport(options)) {
console.log('Please set at least one of the following options: --save-report or --print-report')
commander.help()
exitProcess(1)
}

processFeedValidation(options).then(
() => {
if (options.verbose) {
console.log("Validation completed")
}
}
)
}

if (require.main === module) {
gbfs
.validation()
.then(result => console.log(inspect(result, { depth: null, colors: true })))
validate()
} else {
module.exports = gbfs.validation()
}
module.exports = {
validate,
processFeedValidation,
saveReport,
getFeedValidationReport,
}
}
1 change: 1 addition & 0 deletions gbfs-validator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"ajv": "^8.9.0",
"ajv-errors": "^3.0.0",
"ajv-formats": "^2.1.1",
"commander": "^11.0.0",
"fast-json-patch": "^3.1.0",
"got": "^11.8.2",
"json-merge-patch": "^1.0.2"
Expand Down
Loading

0 comments on commit 2bc59f9

Please sign in to comment.